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.
- package/LICENSE +68 -0
- package/README.md +347 -0
- package/config_template/mcp_config.json +3 -0
- package/config_template/package_name_store.json +5 -0
- package/config_template/settings.json +5 -0
- package/index.js +879 -0
- package/mcp-agent-lib/example/01-basic-usage.js +82 -0
- package/mcp-agent-lib/example/02-quick-start.js +52 -0
- package/mcp-agent-lib/example/03-http-server.js +76 -0
- package/mcp-agent-lib/example/04-multiple-servers.js +117 -0
- package/mcp-agent-lib/example/05-error-handling.js +116 -0
- package/mcp-agent-lib/example/06-resources-and-prompts.js +174 -0
- package/mcp-agent-lib/example/07-advanced-configuration.js +191 -0
- package/mcp-agent-lib/example/08-real-world-chatbot.js +331 -0
- package/mcp-agent-lib/example/README.md +346 -0
- package/mcp-agent-lib/index.js +19 -0
- package/mcp-agent-lib/init.sh +3 -0
- package/mcp-agent-lib/package-lock.json +1216 -0
- package/mcp-agent-lib/package.json +53 -0
- package/mcp-agent-lib/sampleFastMCPClient/client.py +25 -0
- package/mcp-agent-lib/sampleFastMCPClient/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServer/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServer/server.py +12 -0
- package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/server.py +43 -0
- package/mcp-agent-lib/sampleFastMCPServerRootsRequest/server.py +63 -0
- package/mcp-agent-lib/sampleMCPHost/index.js +386 -0
- package/mcp-agent-lib/sampleMCPHost/mcp_config.json +24 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/elicitation.js +151 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/index.js +166 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/roots.js +197 -0
- package/mcp-agent-lib/src/mcp_client.js +1860 -0
- package/mcp-agent-lib/src/mcp_message_logger.js +517 -0
- package/package.json +72 -0
- package/payload_viewer/out/404/index.html +1 -0
- package/payload_viewer/out/404.html +1 -0
- package/payload_viewer/out/_next/static/chunks/060f9a97930f3d04.js +1 -0
- package/payload_viewer/out/_next/static/chunks/103c802c8f4a5ea1.js +1 -0
- package/payload_viewer/out/_next/static/chunks/16474fd6c6910c45.js +1 -0
- package/payload_viewer/out/_next/static/chunks/17722e3ac4e00587.js +1 -0
- package/payload_viewer/out/_next/static/chunks/305b077a9873cf54.js +1 -0
- package/payload_viewer/out/_next/static/chunks/4c1d05c6741c2bdd.js +5 -0
- package/payload_viewer/out/_next/static/chunks/538cc02e54714b23.js +1 -0
- package/payload_viewer/out/_next/static/chunks/6251fa5907d2b226.js +5 -0
- package/payload_viewer/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- package/payload_viewer/out/_next/static/chunks/b6c0459f3789d25c.js +1 -0
- package/payload_viewer/out/_next/static/chunks/b75131b58f8ca46a.css +3 -0
- package/payload_viewer/out/_next/static/chunks/bd2dcf98c9b362f6.js +1 -0
- package/payload_viewer/out/_next/static/chunks/c8a542ae21335479.js +1 -0
- package/payload_viewer/out/_next/static/chunks/cdd12d5c1a5a6064.js +1 -0
- package/payload_viewer/out/_next/static/chunks/e411019f55d87c42.js +1 -0
- package/payload_viewer/out/_next/static/chunks/e60ef129113f6e24.js +1 -0
- package/payload_viewer/out/_next/static/chunks/f1ac9047ac4a3fde.js +1 -0
- package/payload_viewer/out/_next/static/chunks/turbopack-0ac29803ce3c3c7a.js +3 -0
- package/payload_viewer/out/_next/static/chunks/turbopack-89db4c64206a73e4.js +3 -0
- package/payload_viewer/out/_next/static/chunks/turbopack-a5b8235fa59d7119.js +3 -0
- package/payload_viewer/out/_next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/favicon.0b3bf435.ico +0 -0
- package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_buildManifest.js +14 -0
- package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_clientMiddlewareManifest.json +1 -0
- package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_ssgManifest.js +1 -0
- package/payload_viewer/out/favicon.ico +0 -0
- package/payload_viewer/out/file.svg +1 -0
- package/payload_viewer/out/globe.svg +1 -0
- package/payload_viewer/out/index.html +1 -0
- package/payload_viewer/out/index.txt +23 -0
- package/payload_viewer/out/next.svg +1 -0
- package/payload_viewer/out/vercel.svg +1 -0
- package/payload_viewer/out/window.svg +1 -0
- package/payload_viewer/web_server.js +861 -0
- package/prompts/completion_judge.txt +128 -0
- package/prompts/orchestrator.txt +1213 -0
- package/src/LLMClient/client.js +1375 -0
- package/src/LLMClient/converters/input-normalizer.js +238 -0
- package/src/LLMClient/converters/responses-to-claude.js +503 -0
- package/src/LLMClient/converters/responses-to-gemini.js +648 -0
- package/src/LLMClient/converters/responses-to-ollama.js +348 -0
- package/src/LLMClient/converters/responses-to-zai.js +667 -0
- package/src/LLMClient/errors.js +398 -0
- package/src/LLMClient/index.js +36 -0
- package/src/ai_based/completion_judge.js +421 -0
- package/src/ai_based/orchestrator.js +527 -0
- package/src/ai_based/pip_package_installer.js +173 -0
- package/src/ai_based/pip_package_lookup.js +197 -0
- package/src/cli/mcp_cli.js +70 -0
- package/src/cli/mcp_commands.js +255 -0
- package/src/commands/agents.js +18 -0
- package/src/commands/apikey.js +55 -0
- package/src/commands/bg.js +140 -0
- package/src/commands/commands.js +56 -0
- package/src/commands/debug.js +54 -0
- package/src/commands/exit.js +19 -0
- package/src/commands/help.js +35 -0
- package/src/commands/mcp.js +128 -0
- package/src/commands/model.js +176 -0
- package/src/commands/setup.js +13 -0
- package/src/commands/skills.js +51 -0
- package/src/commands/tools.js +165 -0
- package/src/commands/viewer.js +147 -0
- package/src/config/ai_models.js +312 -0
- package/src/config/config.js +10 -0
- package/src/config/constants.js +71 -0
- package/src/config/feature_flags.js +15 -0
- package/src/frontend/App.js +1263 -0
- package/src/frontend/README.md +81 -0
- package/src/frontend/components/AutocompleteMenu.js +47 -0
- package/src/frontend/components/BackgroundProcessList.js +175 -0
- package/src/frontend/components/BlankLine.js +62 -0
- package/src/frontend/components/ConversationItem.js +893 -0
- package/src/frontend/components/CurrentModelView.js +43 -0
- package/src/frontend/components/FileDiffViewer.js +616 -0
- package/src/frontend/components/Footer.js +25 -0
- package/src/frontend/components/Header.js +42 -0
- package/src/frontend/components/HelpView.js +154 -0
- package/src/frontend/components/Input.js +344 -0
- package/src/frontend/components/LoadingIndicator.js +31 -0
- package/src/frontend/components/ModelListView.js +49 -0
- package/src/frontend/components/ModelUpdatedView.js +22 -0
- package/src/frontend/components/SessionSpinner.js +66 -0
- package/src/frontend/components/SetupWizard.js +242 -0
- package/src/frontend/components/StreamOutput.js +34 -0
- package/src/frontend/components/TodoList.js +56 -0
- package/src/frontend/components/ToolApprovalPrompt.js +452 -0
- package/src/frontend/design/themeColors.js +42 -0
- package/src/frontend/hooks/useCompletion.js +84 -0
- package/src/frontend/hooks/useFileCompletion.js +467 -0
- package/src/frontend/hooks/useKeypress.js +145 -0
- package/src/frontend/index.js +65 -0
- package/src/frontend/utils/GridRenderer.js +140 -0
- package/src/frontend/utils/InlineFormatter.js +156 -0
- package/src/frontend/utils/diffUtils.js +235 -0
- package/src/frontend/utils/inputBuffer.js +441 -0
- package/src/frontend/utils/markdownParser.js +377 -0
- package/src/frontend/utils/outputRedirector.js +47 -0
- package/src/frontend/utils/renderInkComponent.js +42 -0
- package/src/frontend/utils/syntaxHighlighter.js +149 -0
- package/src/frontend/utils/toolUIFormatter.js +261 -0
- package/src/system/agents_loader.js +170 -0
- package/src/system/ai_request.js +737 -0
- package/src/system/background_process.js +317 -0
- package/src/system/code_executer.js +1233 -0
- package/src/system/command_loader.js +40 -0
- package/src/system/command_parser.js +133 -0
- package/src/system/conversation_state.js +265 -0
- package/src/system/conversation_trimmer.js +265 -0
- package/src/system/custom_command_loader.js +395 -0
- package/src/system/file_integrity.js +466 -0
- package/src/system/import_analyzer.py +174 -0
- package/src/system/log.js +82 -0
- package/src/system/mcp_integration.js +304 -0
- package/src/system/output_helper.js +89 -0
- package/src/system/session.js +1393 -0
- package/src/system/session_memory.js +481 -0
- package/src/system/skill_loader.js +324 -0
- package/src/system/system_info.js +483 -0
- package/src/system/tool_approval.js +160 -0
- package/src/system/tool_registry.js +184 -0
- package/src/system/ui_events.js +279 -0
- package/src/tools/code_editor.js +792 -0
- package/src/tools/file_reader.js +385 -0
- package/src/tools/glob.js +263 -0
- package/src/tools/response_message.js +30 -0
- package/src/tools/ripgrep.js +554 -0
- package/src/tools/skill_tool.js +122 -0
- package/src/tools/todo_write.js +182 -0
- package/src/tools/web_download.py +74 -0
- package/src/tools/web_downloader.js +83 -0
- package/src/util/clone.js +174 -0
- package/src/util/config.js +203 -0
- package/src/util/config_migration.js +174 -0
- package/src/util/debug_log.js +49 -0
- package/src/util/exit_handler.js +53 -0
- package/src/util/file_reference_parser.js +132 -0
- package/src/util/mcp_config_manager.js +159 -0
- package/src/util/output_formatter.js +50 -0
- package/src/util/path_helper.js +27 -0
- package/src/util/path_validator.js +178 -0
- package/src/util/prompt_loader.js +184 -0
- package/src/util/rag_helper.js +101 -0
- package/src/util/safe_fs.js +645 -0
- package/src/util/setup_wizard.js +62 -0
- package/src/util/text_formatter.js +33 -0
- package/src/util/version_check.js +116 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header component with ASCII art logo
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
import Gradient from 'ink-gradient';
|
|
8
|
+
import { theme } from '../design/themeColors.js';
|
|
9
|
+
|
|
10
|
+
// const ASCII_LOGO = `
|
|
11
|
+
// d8888 8888888 8888888888 Y88b d88P 8888888888 888
|
|
12
|
+
// d88888 888 888 Y88b d88P 888 888
|
|
13
|
+
// d88P888 888 888 Y88o88P 888 888
|
|
14
|
+
// d88P 888 888 8888888 Y888P 8888888 .d8888b .d88b. .d88888 .d88b.
|
|
15
|
+
// d88P 888 888 888 d888b 888 d88P" d88""88b d88" 888 d8P Y8b
|
|
16
|
+
// d88P 888 888 888 d88888b 888 888 888 888 888 888 88888888
|
|
17
|
+
// d8888888888 888 888 d88P Y88b 888 Y88b. Y88..88P Y88b 888 Y8b.
|
|
18
|
+
// d88P 888 8888888 8888888888 d88P Y88b 8888888888 "Y8888P "Y88P" "Y88888 "Y8888
|
|
19
|
+
// `;
|
|
20
|
+
|
|
21
|
+
export function Header({ version = '1.0.0', updateInfo = null }) {
|
|
22
|
+
const ASCII_LOGO = `
|
|
23
|
+
▞▀▖▜▘▛▀▘▌ ▌▛▀▘ ▌
|
|
24
|
+
▙▄▌▐ ▙▄ ▝▞ ▙▄ ▞▀▖▞▀▖▞▀▌▞▀▖
|
|
25
|
+
▌ ▌▐ ▌ ▞▝▖▌ ▌ ▖▌ ▌▌ ▌▛▀
|
|
26
|
+
▘ ▘▀▘▀▀▘▘ ▘▀▀▘▝▀ ▝▀ ▝▀▘▝▀▘
|
|
27
|
+
https://cokac.com
|
|
28
|
+
`.split('\n').map(line => line.trim()).join("\n");
|
|
29
|
+
return React.createElement(Box, { flexDirection: "column", marginBottom: 1, marginLeft: 2 },
|
|
30
|
+
React.createElement(Text, { color: theme.brand.light }, ASCII_LOGO),
|
|
31
|
+
React.createElement(Box, { justifyContent: "flex-left" },
|
|
32
|
+
React.createElement(Text, { color: theme.text.secondary }, `AIEXEcode v${version}`),
|
|
33
|
+
updateInfo && updateInfo.updateAvailable && React.createElement(Text, null,
|
|
34
|
+
React.createElement(Text, { color: '#666666' }, ' → '),
|
|
35
|
+
React.createElement(Text, { color: theme.status.warning }, `v${updateInfo.remoteVersion}`),
|
|
36
|
+
React.createElement(Text, null, ' available ('),
|
|
37
|
+
React.createElement(Text, { color: '#FFD700' }, 'npm install aiexecode -g'),
|
|
38
|
+
React.createElement(Text, null, ')')
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Help View Component - Ink-based UI for displaying available commands
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
|
|
8
|
+
export function HelpView({ commands, customCommands = [], skills = [] }) {
|
|
9
|
+
// 커맨드를 카테고리별로 분류
|
|
10
|
+
const aiCommands = commands.filter(cmd =>
|
|
11
|
+
['model', 'apikey'].includes(cmd.name)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const sessionCommands = commands.filter(cmd =>
|
|
15
|
+
['exit'].includes(cmd.name)
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const otherCommands = commands.filter(cmd =>
|
|
19
|
+
!aiCommands.includes(cmd) && !sessionCommands.includes(cmd)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const renderCommandGroup = (title, commandList) => {
|
|
23
|
+
if (commandList.length === 0) return null;
|
|
24
|
+
|
|
25
|
+
return React.createElement(Box, {
|
|
26
|
+
flexDirection: 'column',
|
|
27
|
+
marginBottom: 1
|
|
28
|
+
},
|
|
29
|
+
React.createElement(Text, {
|
|
30
|
+
bold: true,
|
|
31
|
+
color: 'cyan'
|
|
32
|
+
}, title),
|
|
33
|
+
|
|
34
|
+
commandList.map(cmd =>
|
|
35
|
+
React.createElement(Box, {
|
|
36
|
+
key: cmd.name,
|
|
37
|
+
flexDirection: 'row',
|
|
38
|
+
marginLeft: 2
|
|
39
|
+
},
|
|
40
|
+
React.createElement(Text, { color: 'white', bold: true }, cmd.usage),
|
|
41
|
+
React.createElement(Text, { color: 'gray' }, ' ' + cmd.description)
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// 커스텀 커맨드 렌더링
|
|
48
|
+
const renderCustomCommands = () => {
|
|
49
|
+
if (customCommands.length === 0) return null;
|
|
50
|
+
|
|
51
|
+
return React.createElement(Box, {
|
|
52
|
+
flexDirection: 'column',
|
|
53
|
+
marginBottom: 1
|
|
54
|
+
},
|
|
55
|
+
React.createElement(Text, {
|
|
56
|
+
bold: true,
|
|
57
|
+
color: 'cyan'
|
|
58
|
+
}, 'Custom Commands'),
|
|
59
|
+
|
|
60
|
+
customCommands.map(cmd =>
|
|
61
|
+
React.createElement(Box, {
|
|
62
|
+
key: cmd.name,
|
|
63
|
+
flexDirection: 'row',
|
|
64
|
+
marginLeft: 2
|
|
65
|
+
},
|
|
66
|
+
React.createElement(Text, { color: 'white', bold: true },
|
|
67
|
+
`/${cmd.name}${cmd.argumentHint ? ' ' + cmd.argumentHint : ''}`
|
|
68
|
+
),
|
|
69
|
+
React.createElement(Text, { color: 'gray', dimColor: true },
|
|
70
|
+
` (${cmd.source})`
|
|
71
|
+
),
|
|
72
|
+
cmd.description && React.createElement(Text, { color: 'gray' },
|
|
73
|
+
' ' + cmd.description.substring(0, 50) + (cmd.description.length > 50 ? '...' : '')
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// 스킬 렌더링
|
|
81
|
+
const renderSkills = () => {
|
|
82
|
+
if (skills.length === 0) return null;
|
|
83
|
+
|
|
84
|
+
return React.createElement(Box, {
|
|
85
|
+
flexDirection: 'column',
|
|
86
|
+
marginBottom: 1
|
|
87
|
+
},
|
|
88
|
+
React.createElement(Text, {
|
|
89
|
+
bold: true,
|
|
90
|
+
color: 'cyan'
|
|
91
|
+
}, 'Skills'),
|
|
92
|
+
|
|
93
|
+
skills.map(skill =>
|
|
94
|
+
React.createElement(Box, {
|
|
95
|
+
key: skill.name,
|
|
96
|
+
flexDirection: 'row',
|
|
97
|
+
marginLeft: 2
|
|
98
|
+
},
|
|
99
|
+
React.createElement(Text, { color: 'white', bold: true }, `/${skill.name}`),
|
|
100
|
+
React.createElement(Text, { color: 'gray', dimColor: true },
|
|
101
|
+
` (${skill.source})`
|
|
102
|
+
),
|
|
103
|
+
skill.description && React.createElement(Text, { color: 'gray' },
|
|
104
|
+
' ' + skill.description.substring(0, 50) + (skill.description.length > 50 ? '...' : '')
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return React.createElement(Box, {
|
|
112
|
+
flexDirection: 'column',
|
|
113
|
+
paddingY: 1
|
|
114
|
+
},
|
|
115
|
+
// Header
|
|
116
|
+
React.createElement(Text, {
|
|
117
|
+
bold: true,
|
|
118
|
+
color: 'whiteBright'
|
|
119
|
+
}, 'Available Commands'),
|
|
120
|
+
|
|
121
|
+
React.createElement(Text, null),
|
|
122
|
+
|
|
123
|
+
// AI Configuration Commands
|
|
124
|
+
renderCommandGroup('AI Configuration', aiCommands),
|
|
125
|
+
|
|
126
|
+
// Session Commands
|
|
127
|
+
renderCommandGroup('Session', sessionCommands),
|
|
128
|
+
|
|
129
|
+
// Other Commands
|
|
130
|
+
renderCommandGroup('Other', otherCommands),
|
|
131
|
+
|
|
132
|
+
// Custom Commands
|
|
133
|
+
renderCustomCommands(),
|
|
134
|
+
|
|
135
|
+
// Skills
|
|
136
|
+
renderSkills(),
|
|
137
|
+
|
|
138
|
+
// Footer
|
|
139
|
+
React.createElement(Box, {
|
|
140
|
+
flexDirection: 'column',
|
|
141
|
+
marginTop: 1
|
|
142
|
+
},
|
|
143
|
+
React.createElement(Text, {
|
|
144
|
+
dimColor: true
|
|
145
|
+
}, 'Type any command followed by arguments to execute'),
|
|
146
|
+
React.createElement(Text, {
|
|
147
|
+
dimColor: true
|
|
148
|
+
}, 'Custom commands: ~/.aiexe/commands/ or .aiexe/commands/'),
|
|
149
|
+
React.createElement(Text, {
|
|
150
|
+
dimColor: true
|
|
151
|
+
}, 'Skills: ~/.aiexe/skills/ or .aiexe/skills/')
|
|
152
|
+
)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced Input component with multi-line support
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useState, useRef, useEffect, useMemo } from 'react';
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
import { theme } from '../design/themeColors.js';
|
|
8
|
+
import { useKeypress, keyMatchers, Command } from '../hooks/useKeypress.js';
|
|
9
|
+
import { useCompletion } from '../hooks/useCompletion.js';
|
|
10
|
+
import { useFileCompletion } from '../hooks/useFileCompletion.js';
|
|
11
|
+
import { AutocompleteMenu } from './AutocompleteMenu.js';
|
|
12
|
+
import { cpSlice, cpLen } from '../utils/inputBuffer.js';
|
|
13
|
+
import { uiEvents } from '../../system/ui_events.js';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import stringWidth from 'string-width';
|
|
16
|
+
|
|
17
|
+
// 20개의 placeholder 메시지
|
|
18
|
+
const PLACEHOLDER_MESSAGES = [
|
|
19
|
+
' Fix all bugs in the codebase',
|
|
20
|
+
' Add unit tests for the API',
|
|
21
|
+
' Refactor the authentication module',
|
|
22
|
+
' Optimize database queries',
|
|
23
|
+
' Implement user profile page',
|
|
24
|
+
' Add error handling to services',
|
|
25
|
+
' Write documentation for utils',
|
|
26
|
+
' Create REST API endpoints',
|
|
27
|
+
' Set up CI/CD pipeline',
|
|
28
|
+
' Improve code performance',
|
|
29
|
+
' Add input validation',
|
|
30
|
+
' Implement caching layer',
|
|
31
|
+
' Fix linting errors',
|
|
32
|
+
' Add logging functionality',
|
|
33
|
+
' Create data migration scripts',
|
|
34
|
+
' Implement search feature',
|
|
35
|
+
' Add authentication middleware',
|
|
36
|
+
' Optimize bundle size',
|
|
37
|
+
' Update dependencies',
|
|
38
|
+
' Add responsive design'
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
// 무작위 placeholder 선택 함수
|
|
42
|
+
function getRandomPlaceholder() {
|
|
43
|
+
return PLACEHOLDER_MESSAGES[Math.floor(Math.random() * PLACEHOLDER_MESSAGES.length)];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function InputPromptComponent({ buffer, onSubmit, onClearScreen, onExit, commands = [], placeholder, focus = true, isSessionRunning = false }) {
|
|
47
|
+
const [isExiting, setIsExiting] = useState(false);
|
|
48
|
+
|
|
49
|
+
// placeholder가 제공되지 않으면 무작위로 선택 (컴포넌트 마운트 시 한 번만)
|
|
50
|
+
const defaultPlaceholder = useMemo(() => placeholder || getRandomPlaceholder(), []);
|
|
51
|
+
|
|
52
|
+
const commandCompletionRaw = useCompletion(buffer, commands);
|
|
53
|
+
const fileCompletionRaw = useFileCompletion(buffer);
|
|
54
|
+
|
|
55
|
+
// Stabilize completion objects to prevent handleInput from being recreated
|
|
56
|
+
const commandCompletion = useMemo(() => commandCompletionRaw, [
|
|
57
|
+
commandCompletionRaw.showSuggestions,
|
|
58
|
+
commandCompletionRaw.suggestions.length,
|
|
59
|
+
commandCompletionRaw.activeSuggestionIndex,
|
|
60
|
+
commandCompletionRaw.handleAutocomplete,
|
|
61
|
+
commandCompletionRaw.navigateUp,
|
|
62
|
+
commandCompletionRaw.navigateDown
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
const fileCompletion = useMemo(() => fileCompletionRaw, [
|
|
66
|
+
fileCompletionRaw.showSuggestions,
|
|
67
|
+
fileCompletionRaw.suggestions.length,
|
|
68
|
+
fileCompletionRaw.activeSuggestionIndex,
|
|
69
|
+
fileCompletionRaw.handleAutocomplete,
|
|
70
|
+
fileCompletionRaw.navigateUp,
|
|
71
|
+
fileCompletionRaw.navigateDown
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
// 파일 자동완성 우선, 그 다음 명령어 자동완성
|
|
75
|
+
const completion = fileCompletion.showSuggestions ? fileCompletion : commandCompletion;
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
const handleSubmitAndClear = useCallback((submittedValue) => {
|
|
79
|
+
buffer.setText('');
|
|
80
|
+
onSubmit(submittedValue);
|
|
81
|
+
}, [onSubmit, buffer]);
|
|
82
|
+
|
|
83
|
+
const handleInput = useCallback((key) => {
|
|
84
|
+
if (!focus) return;
|
|
85
|
+
|
|
86
|
+
// Block all input if exiting
|
|
87
|
+
if (isExiting) return;
|
|
88
|
+
|
|
89
|
+
// PRIORITY 1: Handle paste events
|
|
90
|
+
if (key.paste) {
|
|
91
|
+
buffer.handleInput(key);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// PRIORITY 2: Exit commands
|
|
96
|
+
if (keyMatchers[Command.QUIT](key)) {
|
|
97
|
+
// Direct exit without confirmation
|
|
98
|
+
setIsExiting(true);
|
|
99
|
+
if (onExit) {
|
|
100
|
+
onExit();
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (keyMatchers[Command.EXIT](key)) {
|
|
106
|
+
if (buffer.text.length > 0) return;
|
|
107
|
+
// Direct exit
|
|
108
|
+
setIsExiting(true);
|
|
109
|
+
if (onExit) {
|
|
110
|
+
onExit();
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// PRIORITY 4: Escape handling
|
|
116
|
+
if (keyMatchers[Command.ESCAPE](key)) {
|
|
117
|
+
// 1순위: 자동완성 목록이 열려있으면 목록만 닫기
|
|
118
|
+
if (completion.showSuggestions) {
|
|
119
|
+
completion.resetCompletionState();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 2순위: 입력 텍스트가 있으면 지우기
|
|
124
|
+
if (buffer.text !== '') {
|
|
125
|
+
buffer.setText('');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 3순위: 세션이 실행 중이면 중단 요청
|
|
130
|
+
if (isSessionRunning) {
|
|
131
|
+
// 중단 요청 전송
|
|
132
|
+
uiEvents.requestSessionInterrupt();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 4순위: 아무것도 없으면 무시
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// PRIORITY 5: Clear screen
|
|
141
|
+
if (keyMatchers[Command.CLEAR_SCREEN](key)) {
|
|
142
|
+
onClearScreen();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// PRIORITY 6: Submit handling
|
|
147
|
+
if (keyMatchers[Command.SUBMIT](key)) {
|
|
148
|
+
// Block submission if session is running
|
|
149
|
+
if (isSessionRunning) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// If suggestions are shown, accept the active suggestion
|
|
154
|
+
if (completion.showSuggestions && completion.suggestions.length > 0) {
|
|
155
|
+
const targetIndex = completion.activeSuggestionIndex === -1 ? 0 : completion.activeSuggestionIndex;
|
|
156
|
+
const suggestion = completion.suggestions[targetIndex];
|
|
157
|
+
|
|
158
|
+
// 파일 자동완성인 경우 (@로 시작) 완성만 하고 submit하지 않음
|
|
159
|
+
if (suggestion.value.startsWith('@')) {
|
|
160
|
+
completion.handleAutocomplete(targetIndex);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 명령어 자동완성인 경우 완성 후 바로 실행
|
|
165
|
+
completion.handleAutocomplete(targetIndex);
|
|
166
|
+
const completedCommand = suggestion.value;
|
|
167
|
+
handleSubmitAndClear(completedCommand);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (buffer.text.trim()) {
|
|
172
|
+
const [row, col] = buffer.cursor;
|
|
173
|
+
const line = buffer.lines[row];
|
|
174
|
+
const charBefore = col > 0 ? cpSlice(line, col - 1, col) : '';
|
|
175
|
+
|
|
176
|
+
if (charBefore === '\\') {
|
|
177
|
+
buffer.backspace();
|
|
178
|
+
buffer.newline();
|
|
179
|
+
} else {
|
|
180
|
+
handleSubmitAndClear(buffer.text);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// PRIORITY 7: Newline
|
|
187
|
+
if (keyMatchers[Command.NEWLINE](key)) {
|
|
188
|
+
buffer.newline();
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// PRIORITY 8: Navigation commands
|
|
193
|
+
if (keyMatchers[Command.HOME](key)) {
|
|
194
|
+
buffer.move('home');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (keyMatchers[Command.END](key)) {
|
|
199
|
+
buffer.move('end');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (keyMatchers[Command.KILL_LINE_LEFT](key)) {
|
|
204
|
+
buffer.killLineLeft();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (keyMatchers[Command.KILL_LINE_RIGHT](key)) {
|
|
209
|
+
buffer.killLineRight();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (keyMatchers[Command.DELETE_WORD_BACKWARD](key)) {
|
|
214
|
+
buffer.deleteWordLeft();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// PRIORITY 9: Autocomplete
|
|
219
|
+
if (keyMatchers[Command.ACCEPT_SUGGESTION](key)) {
|
|
220
|
+
if (completion.showSuggestions && completion.suggestions.length > 0) {
|
|
221
|
+
const targetIndex = completion.activeSuggestionIndex === -1 ? 0 : completion.activeSuggestionIndex;
|
|
222
|
+
completion.handleAutocomplete(targetIndex);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// PRIORITY 10: Arrow keys (completion or navigation)
|
|
228
|
+
if (key.upArrow) {
|
|
229
|
+
if (completion.showSuggestions && completion.suggestions.length > 1) {
|
|
230
|
+
completion.navigateUp();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
buffer.move('up');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (key.downArrow) {
|
|
237
|
+
if (completion.showSuggestions && completion.suggestions.length > 1) {
|
|
238
|
+
completion.navigateDown();
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
buffer.move('down');
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (key.leftArrow) {
|
|
245
|
+
buffer.move('left');
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (key.rightArrow) {
|
|
249
|
+
buffer.move('right');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// PRIORITY 11: Backspace and Delete
|
|
254
|
+
if (key.backspace) {
|
|
255
|
+
const count = key.repeatCount || 1;
|
|
256
|
+
for (let i = 0; i < count; i++) {
|
|
257
|
+
buffer.backspace();
|
|
258
|
+
}
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (key.delete) {
|
|
263
|
+
const count = key.repeatCount || 1;
|
|
264
|
+
for (let i = 0; i < count; i++) {
|
|
265
|
+
buffer.delete();
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// PRIORITY 12: Default input handling
|
|
271
|
+
buffer.handleInput(key);
|
|
272
|
+
}, [
|
|
273
|
+
focus,
|
|
274
|
+
buffer,
|
|
275
|
+
onClearScreen,
|
|
276
|
+
handleSubmitAndClear,
|
|
277
|
+
completion,
|
|
278
|
+
isSessionRunning,
|
|
279
|
+
isExiting,
|
|
280
|
+
onExit
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
useKeypress(handleInput, { isActive: focus });
|
|
284
|
+
|
|
285
|
+
// Extract all needed values in one go to avoid multiple getter calls
|
|
286
|
+
const linesToRender = useMemo(() => buffer.viewportVisualLines, [buffer.viewportVisualLines]);
|
|
287
|
+
const [cursorVisualRowAbsolute, cursorVisualColAbsolute] = useMemo(() => buffer.visualCursor, [buffer.visualCursor]);
|
|
288
|
+
const scrollVisualRow = useMemo(() => buffer.visualScrollRow, [buffer.visualScrollRow]);
|
|
289
|
+
const cursorVisualRow = cursorVisualRowAbsolute - scrollVisualRow;
|
|
290
|
+
const bufferTextLength = useMemo(() => buffer.text.length, [buffer.text]);
|
|
291
|
+
|
|
292
|
+
return React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
|
|
293
|
+
React.createElement(Box, {
|
|
294
|
+
borderStyle: "round",
|
|
295
|
+
borderColor: theme.brand.light,
|
|
296
|
+
paddingX: 1,
|
|
297
|
+
flexDirection: "row",
|
|
298
|
+
alignItems: "flex-start",
|
|
299
|
+
minHeight: 3,
|
|
300
|
+
maxHeight: 12, // Limit input height to prevent pushing other elements
|
|
301
|
+
flexShrink: 0 // Prevent input from being squeezed by other elements
|
|
302
|
+
// Don't set explicit width - let Ink calculate it
|
|
303
|
+
},
|
|
304
|
+
React.createElement(Text, { color: theme.brand.light }, '> '),
|
|
305
|
+
React.createElement(Box, { flexGrow: 1, flexDirection: "column" },
|
|
306
|
+
bufferTextLength === 0 && defaultPlaceholder
|
|
307
|
+
? (focus
|
|
308
|
+
? React.createElement(Text, null,
|
|
309
|
+
chalk.inverse(defaultPlaceholder.slice(0, 1)),
|
|
310
|
+
React.createElement(Text, { color: theme.text.secondary }, defaultPlaceholder.slice(1)))
|
|
311
|
+
: React.createElement(Text, { color: theme.text.secondary }, defaultPlaceholder))
|
|
312
|
+
: linesToRender.map((lineText, visualIdxInRenderedSet) => {
|
|
313
|
+
const isOnCursorLine = focus && visualIdxInRenderedSet === cursorVisualRow;
|
|
314
|
+
let display = lineText;
|
|
315
|
+
|
|
316
|
+
// Render cursor inline
|
|
317
|
+
if (isOnCursorLine && cursorVisualColAbsolute < cpLen(lineText)) {
|
|
318
|
+
const charToHighlight = cpSlice(lineText, cursorVisualColAbsolute, cursorVisualColAbsolute + 1);
|
|
319
|
+
const highlighted = chalk.inverse(charToHighlight);
|
|
320
|
+
display =
|
|
321
|
+
cpSlice(lineText, 0, cursorVisualColAbsolute) +
|
|
322
|
+
highlighted +
|
|
323
|
+
cpSlice(lineText, cursorVisualColAbsolute + 1);
|
|
324
|
+
} else if (isOnCursorLine && cursorVisualColAbsolute === cpLen(lineText)) {
|
|
325
|
+
display = lineText + chalk.inverse(' ');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return React.createElement(Box, { key: `line-${visualIdxInRenderedSet}`, height: 1 },
|
|
329
|
+
React.createElement(Text, { color: theme.text.primary }, display)
|
|
330
|
+
);
|
|
331
|
+
})
|
|
332
|
+
)
|
|
333
|
+
),
|
|
334
|
+
|
|
335
|
+
completion.showSuggestions && React.createElement(AutocompleteMenu, {
|
|
336
|
+
suggestions: completion.suggestions,
|
|
337
|
+
activeIndex: completion.activeSuggestionIndex
|
|
338
|
+
})
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Don't memoize - buffer state changes frequently and we need to react to it
|
|
343
|
+
// The buffer object reference is stable (from useRef), but its internal state changes
|
|
344
|
+
export const Input = InputPromptComponent;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loading Indicator component
|
|
3
|
+
* Displays a loading spinner during initialization
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { Box, Text } from 'ink';
|
|
8
|
+
import Spinner from 'ink-spinner';
|
|
9
|
+
|
|
10
|
+
export function LoadingIndicator({ tasks = [] }) {
|
|
11
|
+
return React.createElement(Box, {
|
|
12
|
+
flexDirection: "column",
|
|
13
|
+
alignItems: "center",
|
|
14
|
+
justifyContent: "center",
|
|
15
|
+
height: 10
|
|
16
|
+
},
|
|
17
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
18
|
+
React.createElement(Text, { color: "cyan" },
|
|
19
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
20
|
+
" Initializing..."
|
|
21
|
+
)
|
|
22
|
+
),
|
|
23
|
+
tasks.length > 0 && tasks.map((task) =>
|
|
24
|
+
React.createElement(Text, {
|
|
25
|
+
key: task.id,
|
|
26
|
+
color: "gray",
|
|
27
|
+
dimColor: true
|
|
28
|
+
}, task.text)
|
|
29
|
+
)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model List View Component - Ink-based UI for displaying available models
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
import { theme } from '../design/themeColors.js';
|
|
8
|
+
|
|
9
|
+
export function ModelListView({ modelsByProvider }) {
|
|
10
|
+
const sections = [
|
|
11
|
+
React.createElement(Text, {
|
|
12
|
+
key: 'header',
|
|
13
|
+
bold: true,
|
|
14
|
+
color: 'whiteBright'
|
|
15
|
+
}, 'Available AI Models'),
|
|
16
|
+
|
|
17
|
+
React.createElement(Text, { key: 'spacer1' }, null)
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
// 모든 모델을 하나의 리스트로 통합
|
|
21
|
+
const allModels = [];
|
|
22
|
+
Object.keys(modelsByProvider).forEach(provider => {
|
|
23
|
+
const models = modelsByProvider[provider];
|
|
24
|
+
models.forEach(model => {
|
|
25
|
+
allModels.push(model);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// 모델 리스트 추가
|
|
30
|
+
allModels.forEach((model, index) => {
|
|
31
|
+
sections.push(
|
|
32
|
+
React.createElement(Text, {
|
|
33
|
+
key: model.id,
|
|
34
|
+
color: 'white'
|
|
35
|
+
}, ` • ${model.id}`)
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
sections.push(
|
|
40
|
+
React.createElement(Text, { key: 'spacer2' }, null),
|
|
41
|
+
React.createElement(Text, { key: 'usage', bold: true }, 'Usage:'),
|
|
42
|
+
React.createElement(Text, { key: 'usage-cmd' }, ' /model <model-id>'),
|
|
43
|
+
React.createElement(Text, { key: 'usage-example', dimColor: true }, ' Example: /model glm-4.5')
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return React.createElement(Box, {
|
|
47
|
+
flexDirection: 'column'
|
|
48
|
+
}, ...sections);
|
|
49
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Updated View Component - Ink-based UI for model update confirmation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
import { theme } from '../design/themeColors.js';
|
|
8
|
+
|
|
9
|
+
export function ModelUpdatedView({ provider, modelId, modelInfo, settingsFile, warning }) {
|
|
10
|
+
return React.createElement(Box, {
|
|
11
|
+
flexDirection: 'column'
|
|
12
|
+
},
|
|
13
|
+
React.createElement(Text, {
|
|
14
|
+
color: 'green'
|
|
15
|
+
}, `Model updated: ${modelId}`),
|
|
16
|
+
|
|
17
|
+
// Warning if API key not configured
|
|
18
|
+
warning && React.createElement(Text, {
|
|
19
|
+
color: 'yellow'
|
|
20
|
+
}, `Warning: ${warning.message} ${warning.hint}`)
|
|
21
|
+
);
|
|
22
|
+
}
|