aiexecode 1.0.157

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 (188) hide show
  1. package/LICENSE +68 -0
  2. package/README.md +347 -0
  3. package/config_template/mcp_config.json +3 -0
  4. package/config_template/package_name_store.json +5 -0
  5. package/config_template/settings.json +5 -0
  6. package/index.js +879 -0
  7. package/mcp-agent-lib/example/01-basic-usage.js +82 -0
  8. package/mcp-agent-lib/example/02-quick-start.js +52 -0
  9. package/mcp-agent-lib/example/03-http-server.js +76 -0
  10. package/mcp-agent-lib/example/04-multiple-servers.js +117 -0
  11. package/mcp-agent-lib/example/05-error-handling.js +116 -0
  12. package/mcp-agent-lib/example/06-resources-and-prompts.js +174 -0
  13. package/mcp-agent-lib/example/07-advanced-configuration.js +191 -0
  14. package/mcp-agent-lib/example/08-real-world-chatbot.js +331 -0
  15. package/mcp-agent-lib/example/README.md +346 -0
  16. package/mcp-agent-lib/index.js +19 -0
  17. package/mcp-agent-lib/init.sh +3 -0
  18. package/mcp-agent-lib/package-lock.json +1216 -0
  19. package/mcp-agent-lib/package.json +53 -0
  20. package/mcp-agent-lib/sampleFastMCPClient/client.py +25 -0
  21. package/mcp-agent-lib/sampleFastMCPClient/run.sh +3 -0
  22. package/mcp-agent-lib/sampleFastMCPServer/run.sh +3 -0
  23. package/mcp-agent-lib/sampleFastMCPServer/server.py +12 -0
  24. package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/run.sh +3 -0
  25. package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/server.py +43 -0
  26. package/mcp-agent-lib/sampleFastMCPServerRootsRequest/server.py +63 -0
  27. package/mcp-agent-lib/sampleMCPHost/index.js +386 -0
  28. package/mcp-agent-lib/sampleMCPHost/mcp_config.json +24 -0
  29. package/mcp-agent-lib/sampleMCPHostFeatures/elicitation.js +151 -0
  30. package/mcp-agent-lib/sampleMCPHostFeatures/index.js +166 -0
  31. package/mcp-agent-lib/sampleMCPHostFeatures/roots.js +197 -0
  32. package/mcp-agent-lib/src/mcp_client.js +1860 -0
  33. package/mcp-agent-lib/src/mcp_message_logger.js +517 -0
  34. package/package.json +72 -0
  35. package/payload_viewer/out/404/index.html +1 -0
  36. package/payload_viewer/out/404.html +1 -0
  37. package/payload_viewer/out/_next/static/chunks/060f9a97930f3d04.js +1 -0
  38. package/payload_viewer/out/_next/static/chunks/103c802c8f4a5ea1.js +1 -0
  39. package/payload_viewer/out/_next/static/chunks/16474fd6c6910c45.js +1 -0
  40. package/payload_viewer/out/_next/static/chunks/17722e3ac4e00587.js +1 -0
  41. package/payload_viewer/out/_next/static/chunks/305b077a9873cf54.js +1 -0
  42. package/payload_viewer/out/_next/static/chunks/4c1d05c6741c2bdd.js +5 -0
  43. package/payload_viewer/out/_next/static/chunks/538cc02e54714b23.js +1 -0
  44. package/payload_viewer/out/_next/static/chunks/6251fa5907d2b226.js +5 -0
  45. package/payload_viewer/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  46. package/payload_viewer/out/_next/static/chunks/b6c0459f3789d25c.js +1 -0
  47. package/payload_viewer/out/_next/static/chunks/b75131b58f8ca46a.css +3 -0
  48. package/payload_viewer/out/_next/static/chunks/bd2dcf98c9b362f6.js +1 -0
  49. package/payload_viewer/out/_next/static/chunks/c8a542ae21335479.js +1 -0
  50. package/payload_viewer/out/_next/static/chunks/cdd12d5c1a5a6064.js +1 -0
  51. package/payload_viewer/out/_next/static/chunks/e411019f55d87c42.js +1 -0
  52. package/payload_viewer/out/_next/static/chunks/e60ef129113f6e24.js +1 -0
  53. package/payload_viewer/out/_next/static/chunks/f1ac9047ac4a3fde.js +1 -0
  54. package/payload_viewer/out/_next/static/chunks/turbopack-0ac29803ce3c3c7a.js +3 -0
  55. package/payload_viewer/out/_next/static/chunks/turbopack-89db4c64206a73e4.js +3 -0
  56. package/payload_viewer/out/_next/static/chunks/turbopack-a5b8235fa59d7119.js +3 -0
  57. package/payload_viewer/out/_next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  58. package/payload_viewer/out/_next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  59. package/payload_viewer/out/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  60. package/payload_viewer/out/_next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  61. package/payload_viewer/out/_next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  62. package/payload_viewer/out/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  63. package/payload_viewer/out/_next/static/media/favicon.0b3bf435.ico +0 -0
  64. package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_buildManifest.js +14 -0
  65. package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_clientMiddlewareManifest.json +1 -0
  66. package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_ssgManifest.js +1 -0
  67. package/payload_viewer/out/favicon.ico +0 -0
  68. package/payload_viewer/out/file.svg +1 -0
  69. package/payload_viewer/out/globe.svg +1 -0
  70. package/payload_viewer/out/index.html +1 -0
  71. package/payload_viewer/out/index.txt +23 -0
  72. package/payload_viewer/out/next.svg +1 -0
  73. package/payload_viewer/out/vercel.svg +1 -0
  74. package/payload_viewer/out/window.svg +1 -0
  75. package/payload_viewer/web_server.js +861 -0
  76. package/prompts/completion_judge.txt +128 -0
  77. package/prompts/orchestrator.txt +1213 -0
  78. package/src/LLMClient/client.js +1375 -0
  79. package/src/LLMClient/converters/input-normalizer.js +238 -0
  80. package/src/LLMClient/converters/responses-to-claude.js +503 -0
  81. package/src/LLMClient/converters/responses-to-gemini.js +648 -0
  82. package/src/LLMClient/converters/responses-to-ollama.js +348 -0
  83. package/src/LLMClient/converters/responses-to-zai.js +667 -0
  84. package/src/LLMClient/errors.js +398 -0
  85. package/src/LLMClient/index.js +36 -0
  86. package/src/ai_based/completion_judge.js +421 -0
  87. package/src/ai_based/orchestrator.js +527 -0
  88. package/src/ai_based/pip_package_installer.js +173 -0
  89. package/src/ai_based/pip_package_lookup.js +197 -0
  90. package/src/cli/mcp_cli.js +70 -0
  91. package/src/cli/mcp_commands.js +255 -0
  92. package/src/commands/agents.js +18 -0
  93. package/src/commands/apikey.js +55 -0
  94. package/src/commands/bg.js +140 -0
  95. package/src/commands/commands.js +56 -0
  96. package/src/commands/debug.js +54 -0
  97. package/src/commands/exit.js +19 -0
  98. package/src/commands/help.js +35 -0
  99. package/src/commands/mcp.js +128 -0
  100. package/src/commands/model.js +176 -0
  101. package/src/commands/setup.js +13 -0
  102. package/src/commands/skills.js +51 -0
  103. package/src/commands/tools.js +165 -0
  104. package/src/commands/viewer.js +147 -0
  105. package/src/config/ai_models.js +312 -0
  106. package/src/config/config.js +10 -0
  107. package/src/config/constants.js +71 -0
  108. package/src/config/feature_flags.js +15 -0
  109. package/src/frontend/App.js +1263 -0
  110. package/src/frontend/README.md +81 -0
  111. package/src/frontend/components/AutocompleteMenu.js +47 -0
  112. package/src/frontend/components/BackgroundProcessList.js +175 -0
  113. package/src/frontend/components/BlankLine.js +62 -0
  114. package/src/frontend/components/ConversationItem.js +893 -0
  115. package/src/frontend/components/CurrentModelView.js +43 -0
  116. package/src/frontend/components/FileDiffViewer.js +616 -0
  117. package/src/frontend/components/Footer.js +25 -0
  118. package/src/frontend/components/Header.js +42 -0
  119. package/src/frontend/components/HelpView.js +154 -0
  120. package/src/frontend/components/Input.js +344 -0
  121. package/src/frontend/components/LoadingIndicator.js +31 -0
  122. package/src/frontend/components/ModelListView.js +49 -0
  123. package/src/frontend/components/ModelUpdatedView.js +22 -0
  124. package/src/frontend/components/SessionSpinner.js +66 -0
  125. package/src/frontend/components/SetupWizard.js +242 -0
  126. package/src/frontend/components/StreamOutput.js +34 -0
  127. package/src/frontend/components/TodoList.js +56 -0
  128. package/src/frontend/components/ToolApprovalPrompt.js +452 -0
  129. package/src/frontend/design/themeColors.js +42 -0
  130. package/src/frontend/hooks/useCompletion.js +84 -0
  131. package/src/frontend/hooks/useFileCompletion.js +467 -0
  132. package/src/frontend/hooks/useKeypress.js +145 -0
  133. package/src/frontend/index.js +65 -0
  134. package/src/frontend/utils/GridRenderer.js +140 -0
  135. package/src/frontend/utils/InlineFormatter.js +156 -0
  136. package/src/frontend/utils/diffUtils.js +235 -0
  137. package/src/frontend/utils/inputBuffer.js +441 -0
  138. package/src/frontend/utils/markdownParser.js +377 -0
  139. package/src/frontend/utils/outputRedirector.js +47 -0
  140. package/src/frontend/utils/renderInkComponent.js +42 -0
  141. package/src/frontend/utils/syntaxHighlighter.js +149 -0
  142. package/src/frontend/utils/toolUIFormatter.js +261 -0
  143. package/src/system/agents_loader.js +170 -0
  144. package/src/system/ai_request.js +737 -0
  145. package/src/system/background_process.js +317 -0
  146. package/src/system/code_executer.js +1233 -0
  147. package/src/system/command_loader.js +40 -0
  148. package/src/system/command_parser.js +133 -0
  149. package/src/system/conversation_state.js +265 -0
  150. package/src/system/conversation_trimmer.js +265 -0
  151. package/src/system/custom_command_loader.js +395 -0
  152. package/src/system/file_integrity.js +466 -0
  153. package/src/system/import_analyzer.py +174 -0
  154. package/src/system/log.js +82 -0
  155. package/src/system/mcp_integration.js +304 -0
  156. package/src/system/output_helper.js +89 -0
  157. package/src/system/session.js +1393 -0
  158. package/src/system/session_memory.js +481 -0
  159. package/src/system/skill_loader.js +324 -0
  160. package/src/system/system_info.js +483 -0
  161. package/src/system/tool_approval.js +160 -0
  162. package/src/system/tool_registry.js +184 -0
  163. package/src/system/ui_events.js +279 -0
  164. package/src/tools/code_editor.js +792 -0
  165. package/src/tools/file_reader.js +385 -0
  166. package/src/tools/glob.js +263 -0
  167. package/src/tools/response_message.js +30 -0
  168. package/src/tools/ripgrep.js +554 -0
  169. package/src/tools/skill_tool.js +122 -0
  170. package/src/tools/todo_write.js +182 -0
  171. package/src/tools/web_download.py +74 -0
  172. package/src/tools/web_downloader.js +83 -0
  173. package/src/util/clone.js +174 -0
  174. package/src/util/config.js +203 -0
  175. package/src/util/config_migration.js +174 -0
  176. package/src/util/debug_log.js +49 -0
  177. package/src/util/exit_handler.js +53 -0
  178. package/src/util/file_reference_parser.js +132 -0
  179. package/src/util/mcp_config_manager.js +159 -0
  180. package/src/util/output_formatter.js +50 -0
  181. package/src/util/path_helper.js +27 -0
  182. package/src/util/path_validator.js +178 -0
  183. package/src/util/prompt_loader.js +184 -0
  184. package/src/util/rag_helper.js +101 -0
  185. package/src/util/safe_fs.js +645 -0
  186. package/src/util/setup_wizard.js +62 -0
  187. package/src/util/text_formatter.js +33 -0
  188. package/src/util/version_check.js +116 -0
@@ -0,0 +1,173 @@
1
+ import { checkValidPackageName } from "./pip_package_lookup.js";
2
+ import { installPythonPackage, isPackageInstalled, analyzeImports } from "../system/code_executer.js";
3
+ import { createDebugLogger } from "../util/debug_log.js";
4
+
5
+ const debugLog = createDebugLogger('pip_package_installer.log', 'pip_package_installer');
6
+
7
+ // 이 파일은 코드에서 필요로 하는 파이썬 패키지를 확인하고 자동으로 설치합니다.
8
+ // Code Executer가 스크립트를 실행할 때 패키지 누락 오류가 나지 않도록 Orchestrator 실행 이전에 환경을 준비합니다.
9
+
10
+ // 하나의 파일을 분석해 필요한 외부 패키지를 찾아 설치합니다.
11
+ export async function installRequiredPackages(filePath) {
12
+ try {
13
+ const analysisResult = await analyzeImports(filePath);
14
+ const externalPackages = analysisResult;
15
+ if (externalPackages.length === 0) {
16
+ return {
17
+ success: true,
18
+ message: "No external packages required",
19
+ installedPackages: []
20
+ };
21
+ }
22
+ const validPackageNames = await checkValidPackageName(externalPackages);
23
+ const installResults = [];
24
+ const alreadyInstalled = [];
25
+ const failedInstalls = [];
26
+
27
+ for (const packageName of validPackageNames) {
28
+ const isInstalled = await isPackageInstalled(packageName);
29
+ if (isInstalled) {
30
+ alreadyInstalled.push(packageName);
31
+ continue;
32
+ }
33
+ const installSuccess = await installPythonPackage(packageName);
34
+ if (installSuccess) {
35
+ installResults.push(packageName);
36
+ } else {
37
+ failedInstalls.push(packageName);
38
+ }
39
+ }
40
+
41
+ return {
42
+ success: failedInstalls.length === 0,
43
+ originalPackages: externalPackages,
44
+ validatedPackages: validPackageNames,
45
+ installedPackages: installResults,
46
+ alreadyInstalled: alreadyInstalled,
47
+ failedInstalls: failedInstalls,
48
+ summary: {
49
+ total: validPackageNames.length,
50
+ installed: installResults.length,
51
+ alreadyInstalled: alreadyInstalled.length,
52
+ failed: failedInstalls.length
53
+ }
54
+ };
55
+
56
+ } catch (error) {
57
+ return {
58
+ success: false,
59
+ error: error.message,
60
+ details: error
61
+ };
62
+ }
63
+ }
64
+
65
+ export async function installPackageList(packageList) {
66
+ try {
67
+ debugLog(`\n=== Installing package list ===`);
68
+ debugLog("Packages to install:", packageList);
69
+
70
+ const validPackageNames = await checkValidPackageName(packageList);
71
+ debugLog("Valid package names:", validPackageNames);
72
+
73
+ const installResults = [];
74
+ const alreadyInstalled = [];
75
+ const failedInstalls = [];
76
+
77
+ for (const packageName of validPackageNames) {
78
+ debugLog(`\n--- Processing package: ${packageName} ---`);
79
+
80
+ const isInstalled = await isPackageInstalled(packageName);
81
+ if (isInstalled) {
82
+ debugLog(`[SKIP] ${packageName} is already installed`);
83
+ alreadyInstalled.push(packageName);
84
+ continue;
85
+ }
86
+
87
+ debugLog(`Installing ${packageName}...`);
88
+ const installSuccess = await installPythonPackage(packageName);
89
+
90
+ if (installSuccess) {
91
+ debugLog(`[OK] Successfully installed ${packageName}`);
92
+ installResults.push(packageName);
93
+ } else {
94
+ debugLog(`[FAIL] Failed to install ${packageName}`);
95
+ failedInstalls.push(packageName);
96
+ }
97
+ }
98
+
99
+ return {
100
+ success: failedInstalls.length === 0,
101
+ originalPackages: packageList,
102
+ validatedPackages: validPackageNames,
103
+ installedPackages: installResults,
104
+ alreadyInstalled: alreadyInstalled,
105
+ failedInstalls: failedInstalls,
106
+ summary: {
107
+ total: validPackageNames.length,
108
+ installed: installResults.length,
109
+ alreadyInstalled: alreadyInstalled.length,
110
+ failed: failedInstalls.length
111
+ }
112
+ };
113
+
114
+ } catch (error) {
115
+ return {
116
+ success: false,
117
+ error: error.message,
118
+ details: error
119
+ };
120
+ }
121
+ }
122
+
123
+ export async function installRequiredPackagesForFiles(filePaths) {
124
+ try {
125
+ debugLog(`\n=== Analyzing multiple files ===`);
126
+ debugLog("Files to analyze:", filePaths);
127
+
128
+ const allExternalPackages = new Set();
129
+ const analysisResults = [];
130
+
131
+ for (const filePath of filePaths) {
132
+ const analysisResult = await analyzeImports(filePath);
133
+ analysisResults.push({
134
+ file: filePath,
135
+ success: analysisResult.success,
136
+ packages: analysisResult.success ? analysisResult.data.external_packages : []
137
+ });
138
+
139
+ if (analysisResult.success) {
140
+ analysisResult.data.external_packages.forEach(pkg =>
141
+ allExternalPackages.add(pkg)
142
+ );
143
+ }
144
+ }
145
+
146
+ const uniquePackages = Array.from(allExternalPackages);
147
+ debugLog("All unique external packages:", uniquePackages);
148
+
149
+ if (uniquePackages.length === 0) {
150
+ return {
151
+ success: true,
152
+ message: "No external packages required",
153
+ analysisResults: analysisResults,
154
+ installedPackages: []
155
+ };
156
+ }
157
+
158
+ const installResult = await installPackageList(uniquePackages);
159
+
160
+ return {
161
+ ...installResult,
162
+ analysisResults: analysisResults,
163
+ filesAnalyzed: filePaths.length
164
+ };
165
+
166
+ } catch (error) {
167
+ return {
168
+ success: false,
169
+ error: error.message,
170
+ details: error
171
+ };
172
+ }
173
+ }
@@ -0,0 +1,197 @@
1
+ import dotenv from "dotenv";
2
+ import { request, getModelForProvider } from "../system/ai_request.js";
3
+ import { safeReadFile, safeWriteFile } from '../util/safe_fs.js';
4
+ import { join } from 'path';
5
+ import { CONFIG_DIR, ensureConfigDirectory } from "../util/config.js";
6
+ import { createDebugLogger } from "../util/debug_log.js";
7
+
8
+ const debugLog = createDebugLogger('pip_package_lookup.log', 'pip_package_lookup');
9
+
10
+ dotenv.config({ quiet: true });
11
+
12
+ // 이 파일은 import에 쓰인 이름을 실제 pip 설치 이름과 연결해 줍니다.
13
+ // pip_package_installer가 정확한 패키지를 설치할 수 있도록 모든 모듈이 같은 이름을 공유하게 만듭니다.
14
+ // Package name cache file path
15
+ const CACHE_FILE = join(CONFIG_DIR, 'package_name_store.json');
16
+
17
+ // 캐시 파일에서 미리 확인해 둔 패키지 매핑을 읽어옵니다.
18
+ async function loadPackageStore() {
19
+ try {
20
+ await ensureConfigDirectory();
21
+ const data = await safeReadFile(CACHE_FILE, 'utf8');
22
+ return JSON.parse(data);
23
+ } catch (error) {
24
+ // Return empty object if file doesn't exist or can't be read
25
+ return {};
26
+ }
27
+ }
28
+
29
+ // 새로 찾은 패키지 정보를 캐시 파일에 저장합니다.
30
+ async function savePackageStore(store) {
31
+ try {
32
+ await ensureConfigDirectory();
33
+ await safeWriteFile(CACHE_FILE, JSON.stringify(store, null, 2), 'utf8');
34
+ debugLog(`✓ Package cache saved to ${CACHE_FILE}`);
35
+ } catch (error) {
36
+ debugLog('Failed to save package cache:', error);
37
+ }
38
+ }
39
+
40
+
41
+ const pythonPackageNameListSchema = {
42
+ "type": "object",
43
+ "properties": {
44
+ "package_mappings": {
45
+ "type": "array",
46
+ "description": "List of mappings between import names and pip install package names.",
47
+ "items": {
48
+ "type": "object",
49
+ "properties": {
50
+ "import_name": {
51
+ "type": "string",
52
+ "description": "Python module import name.",
53
+ "minLength": 1
54
+ },
55
+ "pip_name": {
56
+ "type": "string",
57
+ "description": "Package name used for pip install.",
58
+ "minLength": 1
59
+ }
60
+ },
61
+ "required": [
62
+ "import_name",
63
+ "pip_name"
64
+ ],
65
+ "additionalProperties": false
66
+ }
67
+ }
68
+ },
69
+ "required": [
70
+ "package_mappings"
71
+ ],
72
+ "additionalProperties": false
73
+ };
74
+
75
+ // AI에게 전달할 대화 형식 메시지 묶음을 만들어 줍니다.
76
+ function buildConversationHistory(systemMessage, dialogHistory) {
77
+ const messages = [
78
+ {
79
+ role: "system",
80
+ content: [{
81
+ type: "input_text",
82
+ text: systemMessage
83
+ }]
84
+ }
85
+ ];
86
+
87
+ for (const dialog of dialogHistory) {
88
+ messages.push({
89
+ role: dialog.role,
90
+ content: [{
91
+ type: "input_text",
92
+ text: dialog.content
93
+ }]
94
+ });
95
+ }
96
+
97
+ return messages;
98
+ }
99
+
100
+ // import 이름 목록을 받아 실제 pip 패키지 이름을 알려줍니다.
101
+ export async function checkValidPackageName(pkgList) {
102
+ const taskName = 'pip_package_lookup';
103
+
104
+ // 1. 캐시 파일에서 패키지 매핑 로드
105
+ const packageStore = await loadPackageStore();
106
+
107
+ // 2. 캐시에서 확인할 수 있는 패키지들과 AI 요청이 필요한 패키지들 분리
108
+ const cachedResults = [];
109
+ const uncachedPackages = [];
110
+
111
+ for (const pkg of pkgList) {
112
+ if (packageStore.hasOwnProperty(pkg)) {
113
+ debugLog(`✓ Found in cache: ${pkg} -> ${packageStore[pkg]}`);
114
+ // 캐시된 값이 null이 아닌 경우만 결과에 추가 (표준 라이브러리는 null로 저장됨)
115
+ if (packageStore[pkg] !== null) {
116
+ cachedResults.push(packageStore[pkg]);
117
+ }
118
+ } else {
119
+ uncachedPackages.push(pkg);
120
+ }
121
+ }
122
+
123
+ // 3. 캐시되지 않은 패키지가 없으면 캐시 결과만 반환
124
+ if (uncachedPackages.length === 0) {
125
+ debugLog("All packages found in cache, no AI request needed");
126
+ return cachedResults;
127
+ }
128
+
129
+ debugLog(`Cache miss for packages: ${uncachedPackages.join(', ')}`);
130
+ debugLog("Making AI request for uncached packages...");
131
+
132
+ // 4. AI에게 캐시되지 않은 패키지들만 요청
133
+ const codeGenerationTemplate = [
134
+ "You are an expert in validating PyPI Python package names.",
135
+ "The user will provide package names used in imports.",
136
+ "Respond with the correct package names for pip install.",
137
+ "For example: 'PIL' should be 'Pillow', 'cv2' should be 'opencv-python'.",
138
+ "If a package is part of Python standard library, exclude it from the list."
139
+ ].join("\n");
140
+
141
+ const dialogHistory = [{
142
+ role: 'user',
143
+ content: `Package names to validate: ${uncachedPackages.join(', ')}`
144
+ }];
145
+
146
+ const conversationHistory = buildConversationHistory(codeGenerationTemplate, dialogHistory);
147
+
148
+ try {
149
+ const model = await getModelForProvider();
150
+ const response = await request(taskName, {
151
+ model: model,
152
+ input: conversationHistory,
153
+ text: {
154
+ "format": {
155
+ "type": "json_schema",
156
+ "name": "pip_package_name_list",
157
+ "strict": true,
158
+ "schema": pythonPackageNameListSchema
159
+ }
160
+ },
161
+ reasoning: {},
162
+ max_output_tokens: 2048,
163
+ store: true
164
+ });
165
+ const codeResponse = JSON.parse(response.output_text);
166
+
167
+ // 5. AI 응답 결과를 캐시에 저장
168
+ const aiResults = [];
169
+ const receivedMappings = new Set();
170
+
171
+ for (const { import_name, pip_name } of codeResponse.package_mappings) {
172
+ packageStore[import_name] = pip_name;
173
+ aiResults.push(pip_name);
174
+ receivedMappings.add(import_name);
175
+ debugLog(`✓ Cached: ${import_name} -> ${pip_name}`);
176
+ }
177
+
178
+ // 6. AI 응답에 포함되지 않은 패키지들은 표준 라이브러리로 간주하여 null로 캐시
179
+ for (const pkg of uncachedPackages) {
180
+ if (!receivedMappings.has(pkg)) {
181
+ packageStore[pkg] = null;
182
+ debugLog(`✓ Cached as standard library: ${pkg} -> null`);
183
+ }
184
+ }
185
+
186
+ // 7. 업데이트된 캐시를 파일에 저장
187
+ await savePackageStore(packageStore);
188
+
189
+ // 8. 캐시 결과와 AI 결과 합쳐서 반환
190
+ return [...cachedResults, ...aiResults];
191
+
192
+ } catch (error) {
193
+ debugLog("Error validating package names:", error);
194
+ // 에러 발생 시 캐시 결과와 원본 패키지명 반환
195
+ return [...cachedResults, ...uncachedPackages];
196
+ }
197
+ }
@@ -0,0 +1,70 @@
1
+ // MCP CLI 명령어 등록
2
+ // CLI 환경에서 MCP 서버 설정을 관리하는 명령어들을 등록
3
+ import { Command } from 'commander';
4
+ import {
5
+ addMcpServer,
6
+ listMcpServers,
7
+ getMcpServer,
8
+ removeMcpServer,
9
+ addMcpServerFromJson
10
+ } from './mcp_commands.js';
11
+
12
+ /**
13
+ * MCP 관련 CLI 명령어를 commander 프로그램에 등록
14
+ * 사용자가 터미널에서 'aiexecode mcp ...' 명령으로 MCP 서버를 관리할 수 있게 함
15
+ * @param {Command} program - Commander 프로그램 인스턴스
16
+ */
17
+ export function registerMcpCliCommands(program) {
18
+ // 'mcp' 상위 명령어 생성 (aiexecode mcp ...)
19
+ const mcp = program
20
+ .command('mcp')
21
+ .description('Manage MCP (Model Context Protocol) servers');
22
+
23
+ // 'aiexecode mcp add' - MCP 서버 추가 명령어
24
+ // stdio, http, sse 등 다양한 전송 방식을 지원
25
+ mcp.command('add')
26
+ .description('Add an MCP server')
27
+ .option('--transport <type>', 'Transport type: stdio, http, or sse', 'stdio')
28
+ .option('--env <key=value>', 'Environment variable (repeatable)', collect, [])
29
+ .option('--header <key:value>', 'HTTP header (repeatable)', collect, [])
30
+ .argument('<name>', 'Server name')
31
+ .argument('[args...]', 'Command and arguments (for stdio) or URL (for http/sse)')
32
+ .action(addMcpServer);
33
+
34
+ // 'aiexecode mcp list' - 설정된 모든 MCP 서버 목록 조회
35
+ mcp.command('list')
36
+ .description('List all configured MCP servers')
37
+ .action(listMcpServers);
38
+
39
+ // 'aiexecode mcp get <name>' - 특정 MCP 서버의 상세 정보 조회
40
+ mcp.command('get')
41
+ .description('Get details of a specific MCP server')
42
+ .argument('<name>', 'Server name')
43
+ .action(getMcpServer);
44
+
45
+ // 'aiexecode mcp remove <name>' - MCP 서버 설정 제거
46
+ mcp.command('remove')
47
+ .description('Remove an MCP server')
48
+ .argument('<name>', 'Server name')
49
+ .action(removeMcpServer);
50
+
51
+ // 'aiexecode mcp add-json <name> <json>' - JSON 형식으로 MCP 서버 추가
52
+ // 복잡한 설정을 한 번에 추가할 때 유용
53
+ mcp.command('add-json')
54
+ .description('Add an MCP server from JSON configuration')
55
+ .argument('<name>', 'Server name')
56
+ .argument('<json>', 'JSON configuration string')
57
+ .action(addMcpServerFromJson);
58
+ }
59
+
60
+ /**
61
+ * 반복 가능한 옵션 수집 헬퍼 함수
62
+ * Commander.js에서 --env, --header 같은 옵션을 여러 번 사용할 수 있게 함
63
+ * 예: --env KEY1=val1 --env KEY2=val2
64
+ * @param {string} value - 새 값
65
+ * @param {Array} previous - 이전 값들
66
+ * @returns {Array} 누적된 값 배열
67
+ */
68
+ function collect(value, previous) {
69
+ return previous.concat([value]);
70
+ }
@@ -0,0 +1,255 @@
1
+ // MCP CLI 명령어 핸들러
2
+ // 각 CLI 명령어의 실제 동작을 구현
3
+ import chalk from 'chalk';
4
+ import {
5
+ addServerToScope,
6
+ getMcpConfigPath,
7
+ loadMergedMcpConfig,
8
+ removeServerFromScope,
9
+ findServerScope
10
+ } from '../util/mcp_config_manager.js';
11
+
12
+ /**
13
+ * MCP 서버 추가 명령어 핸들러
14
+ * 사용자가 입력한 정보를 바탕으로 MCP 서버 설정을 생성하고 저장
15
+ */
16
+ export async function addMcpServer(name, args, options) {
17
+ const { transport, env, header } = options;
18
+
19
+ try {
20
+ // 전송 방식에 따른 서버 설정 객체 생성
21
+ let serverConfig = { type: transport };
22
+
23
+ if (transport === 'stdio') {
24
+ // stdio 방식: 로컬 프로세스를 실행하여 stdin/stdout으로 MCP 통신
25
+ // 예: npx -y @modelcontextprotocol/server-github
26
+ if (args.length === 0) {
27
+ console.error(chalk.red('Error: stdio transport requires command'));
28
+ console.error(chalk.gray('\nExample:'));
29
+ console.error(chalk.cyan(' aiexecode mcp add --transport stdio github -- npx -y @modelcontextprotocol/server-github'));
30
+ process.exit(1);
31
+ }
32
+
33
+ serverConfig.command = args[0];
34
+ serverConfig.args = args.slice(1);
35
+
36
+ // 환경 변수 파싱 및 추가 (KEY=VALUE 형식)
37
+ if (env.length > 0) {
38
+ serverConfig.env = {};
39
+ env.forEach(item => {
40
+ const [key, value] = item.split('=');
41
+ if (!key || value === undefined) {
42
+ console.error(chalk.red(`Error: Invalid env format '${item}'. Use KEY=VALUE`));
43
+ process.exit(1);
44
+ }
45
+ serverConfig.env[key] = value;
46
+ });
47
+ }
48
+
49
+ } else if (transport === 'http' || transport === 'sse') {
50
+ // http/sse 방식: 원격 HTTP(S) 서버와 통신
51
+ // http: 일반 HTTP 요청/응답
52
+ // sse: Server-Sent Events (실시간 스트리밍)
53
+ if (args.length === 0) {
54
+ console.error(chalk.red(`Error: ${transport} transport requires URL`));
55
+ console.error(chalk.gray('\nExample:'));
56
+ console.error(chalk.cyan(` aiexecode mcp add --transport ${transport} notion https://mcp.notion.com/mcp`));
57
+ process.exit(1);
58
+ }
59
+
60
+ serverConfig.url = args[0];
61
+
62
+ // HTTP 헤더 파싱 및 추가 (KEY:VALUE 형식)
63
+ // 인증 토큰 등을 전달할 때 사용
64
+ if (header.length > 0) {
65
+ serverConfig.headers = {};
66
+ header.forEach(item => {
67
+ const colonIndex = item.indexOf(':');
68
+ if (colonIndex === -1) {
69
+ console.error(chalk.red(`Error: Invalid header format '${item}'. Use KEY:VALUE`));
70
+ process.exit(1);
71
+ }
72
+ const key = item.substring(0, colonIndex).trim();
73
+ const value = item.substring(colonIndex + 1).trim();
74
+ serverConfig.headers[key] = value;
75
+ });
76
+ }
77
+ } else {
78
+ console.error(chalk.red(`Error: Unknown transport type '${transport}'`));
79
+ console.error(chalk.gray('Valid types: stdio, http, sse'));
80
+ process.exit(1);
81
+ }
82
+
83
+ // 생성된 서버 설정을 ~/.aiexe/mcp_config.json에 저장
84
+ const configPath = await addServerToScope(name, serverConfig);
85
+
86
+ console.log(chalk.green(`\n✓ MCP server '${name}' added successfully`));
87
+ console.log(chalk.gray(` Config: ${configPath}`));
88
+
89
+ // 설정 요약 표시
90
+ console.log(chalk.gray('\n Configuration:'));
91
+ console.log(chalk.gray(` Type: ${transport}`));
92
+ if (transport === 'stdio') {
93
+ console.log(chalk.gray(` Command: ${serverConfig.command} ${serverConfig.args?.join(' ') || ''}`));
94
+ } else {
95
+ console.log(chalk.gray(` URL: ${serverConfig.url}`));
96
+ }
97
+
98
+ console.log(chalk.yellow('\nTo test the connection, start aiexecode and use /mcp'));
99
+ process.exit(0);
100
+
101
+ } catch (error) {
102
+ console.error(chalk.red(`\nError: ${error.message}`));
103
+ process.exit(1);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * MCP 서버 목록 표시 명령어 핸들러
109
+ * ~/.aiexe/mcp_config.json에 저장된 모든 MCP 서버 설정을 조회하여 표시
110
+ */
111
+ export async function listMcpServers() {
112
+ try {
113
+ // 저장된 모든 MCP 서버 설정 로드
114
+ const servers = await loadMergedMcpConfig();
115
+ const serverNames = Object.keys(servers);
116
+
117
+ if (serverNames.length === 0) {
118
+ console.log(chalk.yellow('\nNo MCP servers configured'));
119
+ console.log(chalk.gray('\nTo add a server, run:'));
120
+ console.log(chalk.cyan(' aiexecode mcp add --transport stdio <name> -- <command>'));
121
+ console.log(chalk.cyan(' aiexecode mcp add --transport http <name> <url>'));
122
+ console.log(chalk.cyan(' aiexecode mcp add --transport sse <name> <url>'));
123
+ process.exit(0);
124
+ }
125
+
126
+ console.log(chalk.bold(`\nConfigured MCP Servers (${serverNames.length}):\n`));
127
+
128
+ serverNames.forEach(name => {
129
+ const config = servers[name];
130
+
131
+ console.log(chalk.bold(` ${name}`));
132
+
133
+ // command가 있으면 stdio 타입으로 간주
134
+ if (config.command) {
135
+ console.log(chalk.gray(` Type: ${config.type || 'stdio'}`));
136
+ console.log(chalk.gray(` Command: ${config.command} ${config.args?.join(' ') || ''}`));
137
+ if (config.env && Object.keys(config.env).length > 0) {
138
+ console.log(chalk.gray(` Env vars: ${Object.keys(config.env).join(', ')}`));
139
+ }
140
+ } else {
141
+ // url이 있으면 http/sse 타입
142
+ console.log(chalk.gray(` Type: ${config.type || 'http'}`));
143
+ console.log(chalk.gray(` URL: ${config.url}`));
144
+ if (config.headers) {
145
+ console.log(chalk.gray(` Headers: ${Object.keys(config.headers).join(', ')}`));
146
+ }
147
+ }
148
+ console.log();
149
+ });
150
+
151
+ process.exit(0);
152
+
153
+ } catch (error) {
154
+ console.error(chalk.red(`\nError: ${error.message}`));
155
+ process.exit(1);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * 특정 MCP 서버 상세 정보 표시 명령어 핸들러
161
+ * 서버 이름으로 조회하여 전체 설정을 JSON 형식으로 출력
162
+ */
163
+ export async function getMcpServer(name) {
164
+ try {
165
+ const servers = await loadMergedMcpConfig();
166
+ const server = servers[name];
167
+
168
+ if (!server) {
169
+ console.error(chalk.red(`\nServer '${name}' not found`));
170
+ console.error(chalk.gray('\nUse "aiexecode mcp list" to see all configured servers'));
171
+ process.exit(1);
172
+ }
173
+
174
+ console.log(chalk.bold(`\nMCP Server: ${name}`));
175
+ console.log(chalk.gray('─'.repeat(50)));
176
+
177
+ console.log(JSON.stringify(server, null, 2));
178
+
179
+ const configPath = getMcpConfigPath();
180
+ console.log(chalk.gray('\nConfiguration file:'));
181
+ console.log(chalk.gray(` ${configPath}\n`));
182
+ process.exit(0);
183
+
184
+ } catch (error) {
185
+ console.error(chalk.red(`\nError: ${error.message}`));
186
+ process.exit(1);
187
+ }
188
+ }
189
+
190
+ /**
191
+ * MCP 서버 제거 명령어 핸들러
192
+ * ~/.aiexe/mcp_config.json에서 지정된 서버 설정을 삭제
193
+ */
194
+ export async function removeMcpServer(name) {
195
+ try {
196
+ // 서버 존재 여부 확인
197
+ const exists = await findServerScope(name);
198
+ if (!exists) {
199
+ console.error(chalk.red(`\nServer '${name}' not found`));
200
+ console.error(chalk.gray('\nUse "aiexecode mcp list" to see all configured servers'));
201
+ process.exit(1);
202
+ }
203
+
204
+ // 설정 파일에서 서버 제거
205
+ const result = await removeServerFromScope(name);
206
+
207
+ if (result.success) {
208
+ console.log(chalk.green(`\n✓ ${result.message}`));
209
+ process.exit(0);
210
+ } else {
211
+ console.error(chalk.red(`\n${result.message}`));
212
+ process.exit(1);
213
+ }
214
+
215
+ } catch (error) {
216
+ console.error(chalk.red(`\nError: ${error.message}`));
217
+ process.exit(1);
218
+ }
219
+ }
220
+
221
+ /**
222
+ * JSON으로부터 MCP 서버 추가 명령어 핸들러
223
+ * 복잡한 설정을 JSON 문자열로 직접 전달받아 서버 추가
224
+ * CLI 옵션으로 표현하기 어려운 고급 설정에 유용
225
+ */
226
+ export async function addMcpServerFromJson(name, jsonString) {
227
+ try {
228
+ // JSON 문자열을 파싱하여 서버 설정 객체 생성
229
+ const serverConfig = JSON.parse(jsonString);
230
+
231
+ // 기본 필수 필드 검증
232
+ if (!serverConfig.type) {
233
+ console.error(chalk.red('Error: JSON must include "type" field (stdio, http, or sse)'));
234
+ process.exit(1);
235
+ }
236
+
237
+ // 설정 저장
238
+ const configPath = await addServerToScope(name, serverConfig);
239
+
240
+ console.log(chalk.green(`\n✓ MCP server '${name}' added from JSON`));
241
+ console.log(chalk.gray(` Config: ${configPath}\n`));
242
+ process.exit(0);
243
+
244
+ } catch (error) {
245
+ if (error instanceof SyntaxError) {
246
+ console.error(chalk.red('\nError: Invalid JSON'));
247
+ console.error(chalk.gray('Make sure to escape quotes properly in your shell'));
248
+ console.error(chalk.gray('\nExample:'));
249
+ console.error(chalk.cyan(' aiexecode mcp add-json weather \'{"type":"http","url":"https://api.example.com"}\''));
250
+ } else {
251
+ console.error(chalk.red(`\nError: ${error.message}`));
252
+ }
253
+ process.exit(1);
254
+ }
255
+ }