aiexecode 1.0.94 → 1.0.127
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/README.md +198 -88
- package/index.js +310 -86
- package/mcp-agent-lib/src/mcp_message_logger.js +17 -16
- package/package.json +4 -4
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/_next/static/chunks/{37d0cd2587a38f79.js → b6c0459f3789d25c.js} +1 -1
- package/payload_viewer/out/_next/static/chunks/b75131b58f8ca46a.css +3 -0
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +3 -3
- package/payload_viewer/web_server.js +361 -0
- package/prompts/completion_judge.txt +4 -0
- package/prompts/orchestrator.txt +116 -3
- package/src/LLMClient/client.js +401 -18
- package/src/LLMClient/converters/responses-to-claude.js +67 -18
- package/src/LLMClient/converters/responses-to-zai.js +667 -0
- package/src/LLMClient/errors.js +30 -4
- package/src/LLMClient/index.js +5 -0
- package/src/ai_based/completion_judge.js +263 -186
- package/src/ai_based/orchestrator.js +171 -35
- package/src/commands/agents.js +70 -0
- package/src/commands/apikey.js +1 -1
- package/src/commands/bg.js +129 -0
- package/src/commands/commands.js +51 -0
- package/src/commands/debug.js +52 -0
- package/src/commands/help.js +11 -1
- package/src/commands/model.js +42 -7
- package/src/commands/reasoning_effort.js +2 -2
- package/src/commands/skills.js +46 -0
- package/src/config/ai_models.js +106 -6
- package/src/config/constants.js +71 -0
- package/src/config/feature_flags.js +6 -7
- package/src/frontend/App.js +108 -1
- package/src/frontend/components/AutocompleteMenu.js +7 -1
- package/src/frontend/components/BackgroundProcessList.js +175 -0
- package/src/frontend/components/ConversationItem.js +26 -10
- package/src/frontend/components/CurrentModelView.js +2 -2
- package/src/frontend/components/HelpView.js +106 -2
- package/src/frontend/components/Input.js +33 -11
- package/src/frontend/components/ModelListView.js +1 -1
- package/src/frontend/components/SetupWizard.js +51 -8
- package/src/frontend/hooks/useFileCompletion.js +467 -0
- package/src/frontend/utils/toolUIFormatter.js +261 -0
- package/src/system/agents_loader.js +289 -0
- package/src/system/ai_request.js +156 -12
- package/src/system/background_process.js +317 -0
- package/src/system/code_executer.js +496 -56
- package/src/system/command_parser.js +33 -3
- package/src/system/conversation_state.js +265 -0
- package/src/system/conversation_trimmer.js +132 -0
- package/src/system/custom_command_loader.js +386 -0
- package/src/system/file_integrity.js +73 -10
- package/src/system/log.js +10 -2
- package/src/system/output_helper.js +52 -9
- package/src/system/session.js +213 -58
- package/src/system/session_memory.js +30 -2
- package/src/system/skill_loader.js +318 -0
- package/src/system/system_info.js +254 -40
- package/src/system/tool_approval.js +10 -0
- package/src/system/tool_registry.js +15 -1
- package/src/system/ui_events.js +11 -0
- package/src/tools/code_editor.js +16 -10
- package/src/tools/file_reader.js +66 -9
- package/src/tools/glob.js +0 -3
- package/src/tools/ripgrep.js +5 -7
- package/src/tools/skill_tool.js +122 -0
- package/src/tools/web_downloader.js +0 -3
- package/src/util/clone.js +174 -0
- package/src/util/config.js +55 -2
- package/src/util/config_migration.js +174 -0
- package/src/util/debug_log.js +8 -2
- package/src/util/exit_handler.js +8 -0
- package/src/util/file_reference_parser.js +132 -0
- package/src/util/path_validator.js +178 -0
- package/src/util/prompt_loader.js +91 -1
- package/src/util/safe_fs.js +66 -3
- package/payload_viewer/out/_next/static/chunks/ecd2072ebf41611f.css +0 -3
- /package/payload_viewer/out/_next/static/{wkEKh6i9XPSyP6rjDRvHn → 42iEoi-1o5MxNIZ1SWSvV}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{wkEKh6i9XPSyP6rjDRvHn → 42iEoi-1o5MxNIZ1SWSvV}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{wkEKh6i9XPSyP6rjDRvHn → 42iEoi-1o5MxNIZ1SWSvV}/_ssgManifest.js +0 -0
package/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { loadPreviousSessions, reconstructUIHistory, deleteHistoryFile } from ".
|
|
|
13
13
|
import { getModelForProvider } from "./src/system/ai_request.js";
|
|
14
14
|
import { runSetupWizard, isConfigured } from "./src/util/setup_wizard.js";
|
|
15
15
|
import { safeRm, safeMkdir, safeCopyFile, safeReaddir } from './src/util/safe_fs.js';
|
|
16
|
+
import { parseFileReferences } from './src/util/file_reference_parser.js';
|
|
16
17
|
import chalk from 'chalk';
|
|
17
18
|
import { startUI } from './src/frontend/index.js';
|
|
18
19
|
import { uiEvents } from './src/system/ui_events.js';
|
|
@@ -21,9 +22,68 @@ import { Command } from 'commander';
|
|
|
21
22
|
import { registerMcpCliCommands } from './src/cli/mcp_cli.js';
|
|
22
23
|
import { createDebugLogger } from './src/util/debug_log.js';
|
|
23
24
|
import { checkForUpdates } from './src/util/version_check.js';
|
|
24
|
-
|
|
25
25
|
const debugLog = createDebugLogger('index.log', 'index');
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* 의존성 오류를 출력합니다.
|
|
29
|
+
*/
|
|
30
|
+
function printDependencyError(check) {
|
|
31
|
+
const { osInfo, issues } = check;
|
|
32
|
+
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log(chalk.bold.red('Missing Required Dependencies'));
|
|
35
|
+
console.log('');
|
|
36
|
+
|
|
37
|
+
// OS 정보
|
|
38
|
+
const osName = osInfo.name || check.os;
|
|
39
|
+
const osVersion = osInfo.version ? ` ${osInfo.version}` : '';
|
|
40
|
+
const pkgMgr = osInfo.packageManagerName ? ` (${osInfo.packageManagerName})` : '';
|
|
41
|
+
console.log(chalk.dim(`System: ${osName}${osVersion}${pkgMgr}`));
|
|
42
|
+
console.log('');
|
|
43
|
+
|
|
44
|
+
// 각 이슈 출력
|
|
45
|
+
issues.forEach((issue, idx) => {
|
|
46
|
+
if (issue.type === 'unsupported_os') {
|
|
47
|
+
console.log(chalk.red(`✗ ${issue.message}`));
|
|
48
|
+
console.log(chalk.dim(` ${issue.details}`));
|
|
49
|
+
|
|
50
|
+
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
51
|
+
console.log('');
|
|
52
|
+
console.log(chalk.yellow('Alternatives:'));
|
|
53
|
+
issue.suggestions.forEach(suggestion => {
|
|
54
|
+
console.log(chalk.dim(` • ${suggestion}`));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
} else if (issue.type === 'missing_command') {
|
|
58
|
+
console.log(chalk.red(`✗ ${issue.displayName || issue.command}`));
|
|
59
|
+
if (issue.description) {
|
|
60
|
+
console.log(chalk.dim(` ${issue.description}`));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(chalk.green(' Install:'));
|
|
64
|
+
console.log(chalk.cyan(` $ ${issue.install.primary}`));
|
|
65
|
+
|
|
66
|
+
if (issue.install.alternatives && issue.install.alternatives.length > 0) {
|
|
67
|
+
issue.install.alternatives.forEach(alt => {
|
|
68
|
+
console.log(chalk.dim(` $ ${alt}`));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (issue.install.url) {
|
|
73
|
+
console.log(chalk.dim(` More info: ${issue.install.url}`));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (idx < issues.length - 1) {
|
|
78
|
+
console.log('');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
console.log('');
|
|
83
|
+
console.log(chalk.yellow('After installing, restart your terminal and run aiexecode again.'));
|
|
84
|
+
console.log('');
|
|
85
|
+
}
|
|
86
|
+
|
|
27
87
|
// Read version from package.json
|
|
28
88
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
89
|
const packageJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf-8'));
|
|
@@ -37,8 +97,12 @@ program
|
|
|
37
97
|
.version(VERSION)
|
|
38
98
|
.option('-c, --continue <session_id>', 'Continue from previous session (16-char hex session ID)')
|
|
39
99
|
.option('--viewer', 'Start payload viewer web server')
|
|
40
|
-
.option('
|
|
100
|
+
.option('--port <port>', 'Port for payload viewer (default: 3300)', '3300')
|
|
41
101
|
.option('--init', 'Initialize project-specific prompts in current directory')
|
|
102
|
+
.option('--dangerously-skip-permissions', 'Skip all tool approval prompts (use with caution)')
|
|
103
|
+
.option('-p, --pipe', 'Pipe mode: non-interactive execution without REPL (includes --dangerously-skip-permissions)')
|
|
104
|
+
.option('--debug', 'Enable debug logging to ~/.aiexe/ (for pipe mode)')
|
|
105
|
+
.option('--sessionid <id>', 'Continue from specified session ID (for pipe mode)')
|
|
42
106
|
.argument('[mission]', 'Natural language task description (e.g., "refactor auth module")')
|
|
43
107
|
.action((mission, options) => {
|
|
44
108
|
// 메인 커맨드 action 핸들러
|
|
@@ -58,6 +122,16 @@ Examples:
|
|
|
58
122
|
Start payload viewer on default port (3300)
|
|
59
123
|
$ aiexecode --viewer --port 8000
|
|
60
124
|
Start payload viewer on custom port
|
|
125
|
+
$ aiexecode --dangerously-skip-permissions "build the project"
|
|
126
|
+
Run without tool approval prompts (use with caution)
|
|
127
|
+
|
|
128
|
+
Pipe Mode (for automation/scripting):
|
|
129
|
+
$ aiexecode -p "list files in current directory"
|
|
130
|
+
Run without UI, output session ID to stdout
|
|
131
|
+
$ aiexecode -p --debug "create a test file"
|
|
132
|
+
Run with debug logging to ~/.aiexe/
|
|
133
|
+
$ aiexecode -p --debug --sessionid abc1234567890def "now delete that file"
|
|
134
|
+
Continue from previous session context
|
|
61
135
|
|
|
62
136
|
Available Slash Commands (in interactive mode):
|
|
63
137
|
/help Show all available commands
|
|
@@ -88,7 +162,7 @@ Configuration:
|
|
|
88
162
|
MCP servers: ~/.aiexe/mcp_config.json
|
|
89
163
|
|
|
90
164
|
Supported AI Providers:
|
|
91
|
-
-
|
|
165
|
+
- Z.AI (GLM-4.5, GLM-4.6, GLM-4.7)
|
|
92
166
|
|
|
93
167
|
For more information, visit the project repository.
|
|
94
168
|
`);
|
|
@@ -105,6 +179,11 @@ const shouldContinue = options.continue;
|
|
|
105
179
|
const viewerMode = options.viewer;
|
|
106
180
|
const initMode = options.init;
|
|
107
181
|
const viewerPort = parseInt(options.port, 10);
|
|
182
|
+
const pipeMode = options.pipe || false;
|
|
183
|
+
const debugMode = options.debug || false;
|
|
184
|
+
const sessionIdOption = options.sessionid;
|
|
185
|
+
// pipe mode는 자동으로 dangerouslySkipPermissions를 포함
|
|
186
|
+
const dangerouslySkipPermissions = options.dangerouslySkipPermissions || pipeMode || false;
|
|
108
187
|
let mission = args[0];
|
|
109
188
|
|
|
110
189
|
// Init 모드 처리
|
|
@@ -167,9 +246,27 @@ if (viewerMode) {
|
|
|
167
246
|
await new Promise(() => { });
|
|
168
247
|
}
|
|
169
248
|
|
|
249
|
+
// ========================================
|
|
250
|
+
// 의존성 체크 (가장 먼저 수행)
|
|
251
|
+
// ========================================
|
|
252
|
+
// ripgrep, node, bash 등 필수 시스템 의존성을 확인
|
|
253
|
+
// Python은 선택사항이므로 여기서는 체크하지 않음 (나중에 settings 로드 후 확인)
|
|
254
|
+
const dependencyCheck = await checkDependencies({ skipPython: true });
|
|
255
|
+
if (!dependencyCheck.success) {
|
|
256
|
+
printDependencyError(dependencyCheck);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 시스템 정보 수집 (의존성 체크 통과 후)
|
|
261
|
+
const initialSystemInfo = await getSystemInfo({ skipPython: true });
|
|
262
|
+
|
|
170
263
|
// 전역 설정
|
|
171
264
|
process.app_custom = {};
|
|
172
265
|
process.app_custom.__dirname = dirname(fileURLToPath(import.meta.url));
|
|
266
|
+
process.app_custom.dangerouslySkipPermissions = dangerouslySkipPermissions;
|
|
267
|
+
process.app_custom.pipeMode = pipeMode;
|
|
268
|
+
process.app_custom.debugMode = debugMode;
|
|
269
|
+
process.app_custom.systemInfo = initialSystemInfo;
|
|
173
270
|
|
|
174
271
|
// 개발 모드 감지: 현재 디렉토리에서 node index.js로 실행했는지 확인
|
|
175
272
|
// (글로벌 설치 후 aiexecode 명령으로 실행 시에는 다른 경로에서 실행됨)
|
|
@@ -189,37 +286,53 @@ function generateSessionID() {
|
|
|
189
286
|
}
|
|
190
287
|
|
|
191
288
|
// Session ID 설정
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
289
|
+
// --continue (interactive mode) 또는 --sessionid (pipe mode) 옵션 처리
|
|
290
|
+
const continueSessionId = shouldContinue || sessionIdOption;
|
|
291
|
+
if (continueSessionId) {
|
|
292
|
+
// 세션 이어가기 모드
|
|
293
|
+
if (typeof continueSessionId !== 'string' || continueSessionId.length !== 16 || !/^[0-9a-f]{16}$/.test(continueSessionId)) {
|
|
294
|
+
if (pipeMode) {
|
|
295
|
+
// Pipe mode에서는 stderr로 에러 출력
|
|
296
|
+
console.error('[ERROR] Invalid session ID format. Must be 16-char hex (e.g., abc1234567890def)');
|
|
297
|
+
process.exit(2);
|
|
298
|
+
} else {
|
|
299
|
+
console.log(chalk.red('\nStartup Failed: Invalid Session ID Format\n'));
|
|
300
|
+
console.log(chalk.yellow('Reason:'));
|
|
301
|
+
console.log(' The session ID must be a 16-character hexadecimal string (0-9, a-f).');
|
|
302
|
+
console.log(chalk.yellow('\nSolution:'));
|
|
303
|
+
console.log(' 1. Check your session ID format - it should look like: abc1234567890def');
|
|
304
|
+
console.log(' 2. Use the correct format with --continue option:');
|
|
305
|
+
console.log(chalk.cyan(' aiexecode --continue abc1234567890def "your mission"'));
|
|
306
|
+
console.log(' 3. Or start a new session without --continue option\n');
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
204
309
|
}
|
|
205
|
-
process.app_custom.sessionID =
|
|
310
|
+
process.app_custom.sessionID = continueSessionId;
|
|
206
311
|
debugLog(`Continuing session ID: ${process.app_custom.sessionID}`);
|
|
207
312
|
} else {
|
|
208
|
-
//
|
|
313
|
+
// 새 세션 시작
|
|
209
314
|
process.app_custom.sessionID = generateSessionID();
|
|
210
315
|
debugLog(`New session ID: ${process.app_custom.sessionID}`);
|
|
211
316
|
}
|
|
212
317
|
|
|
213
|
-
// 임시 폴더 정리
|
|
214
|
-
|
|
215
|
-
|
|
318
|
+
// 임시 폴더 정리 (debug 모드가 아닐 때만)
|
|
319
|
+
// debug 모드에서는 로그를 유지해야 하므로 정리하지 않음
|
|
320
|
+
if (!debugMode) {
|
|
321
|
+
await safeRm(PAYLOAD_LOG_DIR, { recursive: true, force: true }); // 홈 디렉토리의 .aiexe/payload_log
|
|
322
|
+
await safeRm(DEBUG_LOG_DIR, { recursive: true, force: true }); // 홈 디렉토리의 .aiexe/debuglog
|
|
323
|
+
}
|
|
216
324
|
|
|
217
325
|
// 설정 로드 (시스템 정보 수집 전에 먼저 로드)
|
|
218
326
|
await ensureConfigDirectory();
|
|
219
327
|
|
|
220
328
|
// 초기 설정 확인 및 Setup Wizard 실행
|
|
329
|
+
// Pipe mode에서는 Setup Wizard 스킵 (설정이 없으면 에러)
|
|
221
330
|
const configured = await isConfigured();
|
|
222
331
|
if (!configured) {
|
|
332
|
+
if (pipeMode) {
|
|
333
|
+
console.error('[ERROR] Not configured. Run aiexecode interactively first to complete setup.');
|
|
334
|
+
process.exit(2);
|
|
335
|
+
}
|
|
223
336
|
const setupCompleted = await runSetupWizard();
|
|
224
337
|
|
|
225
338
|
if (!setupCompleted) {
|
|
@@ -237,48 +350,28 @@ if (!configured) {
|
|
|
237
350
|
|
|
238
351
|
const settings = await loadSettings();
|
|
239
352
|
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
// 시스템 정보 수집 (Python 도구가 비활성화된 경우 Python 체크 생략)
|
|
244
|
-
process.app_custom.systemInfo = await getSystemInfo({ skipPython });
|
|
245
|
-
|
|
246
|
-
// 의존성 체크 (flutter doctor와 유사)
|
|
247
|
-
const dependencyCheck = await checkDependencies({ skipPython });
|
|
248
|
-
if (!dependencyCheck.success) {
|
|
249
|
-
console.log(chalk.red('\nStartup Failed: Missing Required Dependencies\n'));
|
|
250
|
-
console.log(chalk.yellow('Reason:'));
|
|
251
|
-
console.log(' One or more required system dependencies are missing or incompatible.\n');
|
|
252
|
-
|
|
253
|
-
dependencyCheck.issues.forEach(issue => {
|
|
254
|
-
if (issue.type === 'unsupported_os') {
|
|
255
|
-
console.log(chalk.red(' [UNSUPPORTED] ') + issue.message);
|
|
256
|
-
console.log(' ' + issue.details);
|
|
257
|
-
} else if (issue.type === 'missing_command') {
|
|
258
|
-
console.log(chalk.red(` [MISSING] ${issue.command}: `) + issue.message);
|
|
259
|
-
console.log(chalk.cyan(` Install: ${issue.install}`));
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
console.log(chalk.yellow('\nSolution:'));
|
|
264
|
-
console.log(' 1. Install all missing dependencies listed above');
|
|
265
|
-
console.log(' 2. Ensure all commands are accessible in your PATH');
|
|
266
|
-
console.log(' 3. Restart your terminal after installation');
|
|
267
|
-
console.log(' 4. Run aiexecode again\n');
|
|
268
|
-
process.exit(1);
|
|
353
|
+
// --debug 옵션이 있으면 SHOW_API_PAYLOAD 활성화 (런타임에서만)
|
|
354
|
+
if (debugMode) {
|
|
355
|
+
settings.SHOW_API_PAYLOAD = true;
|
|
269
356
|
}
|
|
270
357
|
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
358
|
+
// Python 도구 사용 여부에 따른 시스템 정보 업데이트
|
|
359
|
+
const skipPython = settings?.TOOLS_ENABLED?.run_python_code === false;
|
|
360
|
+
if (!skipPython) {
|
|
361
|
+
// Python이 활성화된 경우 전체 시스템 정보 수집 (Python 포함)
|
|
362
|
+
process.app_custom.systemInfo = await getSystemInfo({ skipPython: false });
|
|
363
|
+
|
|
364
|
+
// Python 관련 경고 확인
|
|
365
|
+
const fullCheck = await checkDependencies({ skipPython: false });
|
|
366
|
+
if (fullCheck.warnings && fullCheck.warnings.length > 0) {
|
|
367
|
+
fullCheck.warnings.forEach(warning => {
|
|
368
|
+
if (warning.type === 'optional_command') {
|
|
369
|
+
debugLog(`Optional dependency missing: ${warning.command}`);
|
|
370
|
+
debugLog(` Install: ${warning.install.primary}`);
|
|
371
|
+
debugLog(` Disabled features: ${warning.disabledFeatures?.join(', ') || 'none'}`);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
282
375
|
}
|
|
283
376
|
|
|
284
377
|
// 환경변수 설정
|
|
@@ -294,14 +387,16 @@ if (!process.env.REASONING_EFFORT && settings?.REASONING_EFFORT) {
|
|
|
294
387
|
|
|
295
388
|
// 최종 검증
|
|
296
389
|
if (!process.env.API_KEY) {
|
|
390
|
+
if (pipeMode) {
|
|
391
|
+
console.error('[ERROR] API_KEY not configured. Set it in ~/.aiexe/settings.json');
|
|
392
|
+
process.exit(2);
|
|
393
|
+
}
|
|
297
394
|
console.log(chalk.red('\nStartup Failed: Missing API Key\n'));
|
|
298
395
|
console.log(chalk.yellow('Reason:'));
|
|
299
396
|
console.log(' API_KEY is not configured in the settings.');
|
|
300
397
|
console.log(chalk.yellow('\nSolution:'));
|
|
301
|
-
console.log(' 1. Obtain an API key from
|
|
302
|
-
console.log(chalk.cyan('
|
|
303
|
-
console.log(chalk.cyan(' Google: https://makersuite.google.com/app/apikey'));
|
|
304
|
-
console.log(chalk.cyan(' Anthropic: https://console.anthropic.com/settings/keys'));
|
|
398
|
+
console.log(' 1. Obtain an API key from Z.AI:');
|
|
399
|
+
console.log(chalk.cyan(' https://z.ai/manage-apikey/apikey-list'));
|
|
305
400
|
console.log(` 2. Add the API key to your settings file:`);
|
|
306
401
|
console.log(chalk.cyan(` ${SETTINGS_FILE}`));
|
|
307
402
|
console.log(' 3. Or run the setup wizard again by deleting the settings file');
|
|
@@ -309,6 +404,12 @@ if (!process.env.API_KEY) {
|
|
|
309
404
|
process.exit(1);
|
|
310
405
|
}
|
|
311
406
|
|
|
407
|
+
// Pipe mode에서는 mission이 필수
|
|
408
|
+
if (pipeMode && (!mission || !mission.trim())) {
|
|
409
|
+
console.error('[ERROR] Mission is required in pipe mode. Usage: node index.js -p "your mission"');
|
|
410
|
+
process.exit(2);
|
|
411
|
+
}
|
|
412
|
+
|
|
312
413
|
// ========================================
|
|
313
414
|
// MCP Integration 초기화
|
|
314
415
|
// ========================================
|
|
@@ -451,7 +552,34 @@ async function handleSubmit(text) {
|
|
|
451
552
|
// 슬래시 커맨드 처리
|
|
452
553
|
if (isCommand(text)) {
|
|
453
554
|
try {
|
|
454
|
-
await commandRegistry.execute(text);
|
|
555
|
+
const result = await commandRegistry.execute(text);
|
|
556
|
+
|
|
557
|
+
// 커스텀 커맨드가 반환된 경우 AI에게 전달하여 실행
|
|
558
|
+
if (result && result.type === 'custom_command') {
|
|
559
|
+
debugLog(`Custom command invoked: ${result.commandName}`);
|
|
560
|
+
uiEvents.addSystemMessage(`📋 Command: ${result.commandName}`);
|
|
561
|
+
|
|
562
|
+
// 커맨드의 프롬프트를 미션으로 사용하여 세션 실행
|
|
563
|
+
await runSession({
|
|
564
|
+
mission: result.prompt,
|
|
565
|
+
maxIterations: 50,
|
|
566
|
+
mcpToolSchemas,
|
|
567
|
+
mcpToolFunctions
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
// 스킬이 반환된 경우 AI에게 전달하여 실행
|
|
571
|
+
else if (result && result.type === 'skill') {
|
|
572
|
+
debugLog(`Skill invoked: ${result.skillName}`);
|
|
573
|
+
uiEvents.addSkillInvoked(result.skillName);
|
|
574
|
+
|
|
575
|
+
// 스킬의 프롬프트를 미션으로 사용하여 세션 실행
|
|
576
|
+
await runSession({
|
|
577
|
+
mission: result.prompt,
|
|
578
|
+
maxIterations: 50,
|
|
579
|
+
mcpToolSchemas,
|
|
580
|
+
mcpToolFunctions
|
|
581
|
+
});
|
|
582
|
+
}
|
|
455
583
|
} catch (err) {
|
|
456
584
|
debugLog(`Command execution error: ${err.message}`);
|
|
457
585
|
uiEvents.addErrorMessage(`${err.message}\nType /help to see available commands`);
|
|
@@ -462,8 +590,12 @@ async function handleSubmit(text) {
|
|
|
462
590
|
// 세션 실행 (AI Agent의 미션 수행)
|
|
463
591
|
// 시작/종료 알림 및 저장은 runSession 내부에서 처리
|
|
464
592
|
try {
|
|
593
|
+
// 파일 참조 파싱 (@경로 -> 참조된 파일 목록으로 변환)
|
|
594
|
+
const parsed = await parseFileReferences(text);
|
|
595
|
+
const missionText = parsed.hasReferences ? parsed.transformedMessage : text;
|
|
596
|
+
|
|
465
597
|
await runSession({
|
|
466
|
-
mission:
|
|
598
|
+
mission: missionText, // 변환된 미션 (파일 참조 포함 시)
|
|
467
599
|
maxIterations: 50, // 최대 반복 횟수
|
|
468
600
|
mcpToolSchemas, // AI 모델에 전달할 MCP 도구 스키마들
|
|
469
601
|
mcpToolFunctions // 실제 MCP 도구 실행 함수들
|
|
@@ -476,28 +608,54 @@ async function handleSubmit(text) {
|
|
|
476
608
|
const errorType = err?.type || err?.constructor?.name || 'Error';
|
|
477
609
|
const errorStack = err?.stack || 'No stack trace available';
|
|
478
610
|
|
|
611
|
+
// debug 설정 확인
|
|
612
|
+
const settings = await loadSettings().catch(() => ({}));
|
|
613
|
+
const isDebugMode = settings?.SHOW_API_PAYLOAD === true;
|
|
614
|
+
|
|
479
615
|
// 인증 에러인 경우 설정 파일 안내
|
|
480
616
|
if (err?.code === 'invalid_api_key' || err?.status === 401) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
617
|
+
if (isDebugMode) {
|
|
618
|
+
const consolidatedErrorMessage = [
|
|
619
|
+
`[Main] Authentication failed: ${errorMessage}`,
|
|
620
|
+
` ├─ Code: ${errorCode}`,
|
|
621
|
+
` ├─ Status: ${errorStatus}`,
|
|
622
|
+
` ├─ Type: ${errorType}`,
|
|
623
|
+
` └─ Stack trace: ${errorStack}`
|
|
624
|
+
].join('\n');
|
|
625
|
+
uiEvents.addErrorMessage(consolidatedErrorMessage);
|
|
626
|
+
} else {
|
|
627
|
+
uiEvents.addErrorMessage('[Main] API 키가 유효하지 않습니다.');
|
|
628
|
+
}
|
|
629
|
+
uiEvents.addSystemMessage(`Please check your API key in ${SETTINGS_FILE}`);
|
|
490
630
|
} else {
|
|
491
631
|
// 일반 에러
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
632
|
+
if (isDebugMode) {
|
|
633
|
+
const consolidatedErrorMessage = [
|
|
634
|
+
`[Main] Session execution error: ${errorMessage}`,
|
|
635
|
+
` ├─ Code: ${errorCode}`,
|
|
636
|
+
` ├─ Status: ${errorStatus}`,
|
|
637
|
+
` ├─ Type: ${errorType}`,
|
|
638
|
+
` ├─ Error Object: ${JSON.stringify(err, null, 2)}`,
|
|
639
|
+
` └─ Stack trace: ${errorStack}`
|
|
640
|
+
].join('\n');
|
|
641
|
+
uiEvents.addErrorMessage(consolidatedErrorMessage);
|
|
642
|
+
} else {
|
|
643
|
+
// 사용자 친화적 에러 메시지
|
|
644
|
+
const statusNum = parseInt(errorStatus) || parseInt(err?.status);
|
|
645
|
+
let userFriendlyMessage;
|
|
646
|
+
|
|
647
|
+
if (statusNum === 503 || errorMessage.includes('503')) {
|
|
648
|
+
userFriendlyMessage = 'AI 서버가 일시적으로 응답하지 않습니다. 잠시 후 다시 시도해주세요.';
|
|
649
|
+
} else if (statusNum === 500 || errorMessage.includes('500')) {
|
|
650
|
+
userFriendlyMessage = 'AI 서버에서 오류가 발생했습니다. 잠시 후 다시 시도해주세요.';
|
|
651
|
+
} else if (statusNum === 429) {
|
|
652
|
+
userFriendlyMessage = '요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요.';
|
|
653
|
+
} else {
|
|
654
|
+
userFriendlyMessage = '오류가 발생했습니다. 문제가 지속되면 /debug on 으로 상세 정보를 확인하세요.';
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
uiEvents.addErrorMessage(`[Main] ${userFriendlyMessage}`);
|
|
658
|
+
}
|
|
501
659
|
}
|
|
502
660
|
}
|
|
503
661
|
}
|
|
@@ -514,9 +672,75 @@ async function handleExit() {
|
|
|
514
672
|
});
|
|
515
673
|
}
|
|
516
674
|
|
|
675
|
+
// ========================================
|
|
676
|
+
// Pipe Mode 처리
|
|
677
|
+
// ========================================
|
|
678
|
+
// UI 없이 직접 세션 실행하고 sessionid 출력 후 종료
|
|
679
|
+
if (pipeMode) {
|
|
680
|
+
debugLog(`[PipeMode] Starting pipe mode execution`);
|
|
681
|
+
debugLog(`[PipeMode] Mission: ${mission}`);
|
|
682
|
+
debugLog(`[PipeMode] SessionID: ${process.app_custom.sessionID}`);
|
|
683
|
+
debugLog(`[PipeMode] Debug: ${debugMode}`);
|
|
684
|
+
|
|
685
|
+
try {
|
|
686
|
+
// MCP 초기화 완료 대기
|
|
687
|
+
await mcpInitPromise;
|
|
688
|
+
debugLog(`[PipeMode] MCP initialization complete`);
|
|
689
|
+
|
|
690
|
+
// 이전 세션 로드 (--sessionid 옵션)
|
|
691
|
+
let previousSessions = null;
|
|
692
|
+
if (sessionIdOption) {
|
|
693
|
+
previousSessions = await loadPreviousSessions(process.app_custom.sessionID);
|
|
694
|
+
if (previousSessions && previousSessions.length > 0) {
|
|
695
|
+
debugLog(`[PipeMode] Loaded ${previousSessions.length} previous session(s)`);
|
|
696
|
+
} else {
|
|
697
|
+
debugLog(`[PipeMode] No previous session found for ID: ${process.app_custom.sessionID}`);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// 파일 참조 파싱
|
|
702
|
+
const parsed = await parseFileReferences(mission);
|
|
703
|
+
const missionText = parsed.hasReferences ? parsed.transformedMessage : mission;
|
|
704
|
+
|
|
705
|
+
// 세션 실행
|
|
706
|
+
const result = await runSession({
|
|
707
|
+
mission: missionText,
|
|
708
|
+
maxIterations: 50,
|
|
709
|
+
mcpToolSchemas,
|
|
710
|
+
mcpToolFunctions,
|
|
711
|
+
previousSessions
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// stdout에 sessionid만 출력 (다른 모든 출력은 stderr나 로그 파일로)
|
|
715
|
+
console.log(process.app_custom.sessionID);
|
|
716
|
+
|
|
717
|
+
// MCP 정리
|
|
718
|
+
if (mcpIntegration) {
|
|
719
|
+
await mcpIntegration.cleanup();
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// 종료 (성공=0, 미션 미완료=1)
|
|
723
|
+
process.exit(result?.mission_solved ? 0 : 1);
|
|
724
|
+
} catch (error) {
|
|
725
|
+
console.error(`[ERROR] ${error.message}`);
|
|
726
|
+
debugLog(`[PipeMode] Fatal error: ${error.stack}`);
|
|
727
|
+
|
|
728
|
+
// MCP 정리 시도
|
|
729
|
+
if (mcpIntegration) {
|
|
730
|
+
await mcpIntegration.cleanup().catch(() => {});
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
process.exit(2);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// ========================================
|
|
738
|
+
// Interactive Mode (UI)
|
|
739
|
+
// ========================================
|
|
740
|
+
|
|
517
741
|
// 히스토리 복원 (--continue 옵션)
|
|
518
742
|
let initialHistory = [];
|
|
519
|
-
if (
|
|
743
|
+
if (continueSessionId) {
|
|
520
744
|
const previousSessions = await loadPreviousSessions(process.app_custom.sessionID);
|
|
521
745
|
if (previousSessions && previousSessions.length > 0) {
|
|
522
746
|
debugLog(`Loaded ${previousSessions.length} session(s) from history`);
|
|
@@ -566,7 +790,7 @@ const versionCheckPromise = checkForUpdates(VERSION).then(info => {
|
|
|
566
790
|
|
|
567
791
|
// UI 시작
|
|
568
792
|
const currentModel = await getModelForProvider();
|
|
569
|
-
const currentReasoningEffort = settings?.
|
|
793
|
+
const currentReasoningEffort = settings?.REASONING_EFFORT || process.env.REASONING_EFFORT;
|
|
570
794
|
uiInstance = startUI({
|
|
571
795
|
onSubmit: handleSubmit,
|
|
572
796
|
onClearScreen: handleClearScreen,
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* @version 1.0.0
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
13
|
import path from 'path';
|
|
14
|
+
import { safeMkdirSync, safeWriteFile } from '../../src/util/safe_fs.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* 타임스탬프를 파일명 형식으로 포맷
|
|
@@ -94,9 +95,9 @@ export class MCPMessageLogger {
|
|
|
94
95
|
*/
|
|
95
96
|
_initializeLogDir() {
|
|
96
97
|
try {
|
|
97
|
-
// 로그 디렉토리 생성
|
|
98
|
-
if (!
|
|
99
|
-
|
|
98
|
+
// 로그 디렉토리 생성 (safe_fs 사용 - ALLOWED_DIRECTORIES 검증)
|
|
99
|
+
if (!existsSync(this.options.logDir)) {
|
|
100
|
+
safeMkdirSync(this.options.logDir, { recursive: true });
|
|
100
101
|
}
|
|
101
102
|
} catch (error) {
|
|
102
103
|
console.error(`Failed to initialize log directory: ${error.message}`);
|
|
@@ -105,14 +106,14 @@ export class MCPMessageLogger {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
/**
|
|
108
|
-
* 개별 메시지를 JSON 파일로 저장
|
|
109
|
+
* 개별 메시지를 JSON 파일로 저장 (safe_fs 사용 - ALLOWED_DIRECTORIES 검증)
|
|
109
110
|
*/
|
|
110
|
-
_writeMessageToFile(direction, serverName, message, metadata) {
|
|
111
|
+
async _writeMessageToFile(direction, serverName, message, metadata) {
|
|
111
112
|
try {
|
|
112
|
-
// 서버별 하위 디렉토리 생성
|
|
113
|
+
// 서버별 하위 디렉토리 생성 (safe_fs 사용)
|
|
113
114
|
const serverLogDir = path.join(this.options.logDir, serverName);
|
|
114
|
-
if (!
|
|
115
|
-
|
|
115
|
+
if (!existsSync(serverLogDir)) {
|
|
116
|
+
safeMkdirSync(serverLogDir, { recursive: true });
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
// 순차 번호 획득 및 증가
|
|
@@ -132,7 +133,7 @@ export class MCPMessageLogger {
|
|
|
132
133
|
? JSON.stringify(logData, null, 2)
|
|
133
134
|
: JSON.stringify(logData);
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
await safeWriteFile(filepath, content, 'utf-8');
|
|
136
137
|
} catch (error) {
|
|
137
138
|
console.error(`Failed to write message file: ${error.message}`);
|
|
138
139
|
}
|
|
@@ -150,9 +151,9 @@ export class MCPMessageLogger {
|
|
|
150
151
|
|
|
151
152
|
this.stats.sent++;
|
|
152
153
|
|
|
153
|
-
// 파일로 저장
|
|
154
|
+
// 파일로 저장 (fire-and-forget, 에러는 내부에서 처리됨)
|
|
154
155
|
if (this.options.logDir && (this.options.output === 'file' || this.options.output === 'both')) {
|
|
155
|
-
this._writeMessageToFile('SEND', serverName, message, metadata);
|
|
156
|
+
this._writeMessageToFile('SEND', serverName, message, metadata).catch(() => {});
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
// 콘솔 출력
|
|
@@ -187,9 +188,9 @@ export class MCPMessageLogger {
|
|
|
187
188
|
|
|
188
189
|
this.stats.received++;
|
|
189
190
|
|
|
190
|
-
// 파일로 저장
|
|
191
|
+
// 파일로 저장 (fire-and-forget, 에러는 내부에서 처리됨)
|
|
191
192
|
if (this.options.logDir && (this.options.output === 'file' || this.options.output === 'both')) {
|
|
192
|
-
this._writeMessageToFile('RECV', serverName, message, metadata);
|
|
193
|
+
this._writeMessageToFile('RECV', serverName, message, metadata).catch(() => {});
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
// 콘솔 출력
|
|
@@ -230,9 +231,9 @@ export class MCPMessageLogger {
|
|
|
230
231
|
code: error.code
|
|
231
232
|
};
|
|
232
233
|
|
|
233
|
-
// 파일로 저장
|
|
234
|
+
// 파일로 저장 (fire-and-forget, 에러는 내부에서 처리됨)
|
|
234
235
|
if (this.options.logDir && (this.options.output === 'file' || this.options.output === 'both')) {
|
|
235
|
-
this._writeMessageToFile('ERROR', serverName, errorMessage, metadata);
|
|
236
|
+
this._writeMessageToFile('ERROR', serverName, errorMessage, metadata).catch(() => {});
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
// 콘솔 출력
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiexecode",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.127",
|
|
4
|
+
"description": "Your intelligent coding companion that thinks, plans, and builds with you",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"LICENSE"
|
|
22
22
|
],
|
|
23
23
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
25
|
-
"npm": ">=
|
|
24
|
+
"node": ">= 18.0.0",
|
|
25
|
+
"npm": ">= 8.0.0"
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://aiexecode.com",
|
|
28
28
|
"repository": {
|