aiexecode 1.0.92 → 1.0.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/README.md +210 -87
- package/index.js +33 -1
- package/package.json +3 -3
- 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/src/LLMClient/client.js +392 -16
- package/src/LLMClient/converters/responses-to-claude.js +67 -18
- package/src/LLMClient/converters/responses-to-zai.js +608 -0
- package/src/LLMClient/errors.js +18 -4
- package/src/LLMClient/index.js +5 -0
- package/src/ai_based/completion_judge.js +35 -4
- package/src/ai_based/orchestrator.js +146 -35
- package/src/commands/agents.js +70 -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 +43 -7
- package/src/commands/skills.js +46 -0
- package/src/config/ai_models.js +96 -5
- package/src/config/constants.js +71 -0
- package/src/frontend/App.js +4 -5
- package/src/frontend/components/ConversationItem.js +25 -24
- package/src/frontend/components/HelpView.js +106 -2
- package/src/frontend/components/SetupWizard.js +53 -8
- package/src/frontend/utils/syntaxHighlighter.js +4 -4
- package/src/frontend/utils/toolUIFormatter.js +261 -0
- package/src/system/agents_loader.js +289 -0
- package/src/system/ai_request.js +147 -9
- package/src/system/command_parser.js +33 -3
- package/src/system/conversation_state.js +265 -0
- package/src/system/custom_command_loader.js +386 -0
- package/src/system/session.js +59 -35
- package/src/system/skill_loader.js +318 -0
- package/src/system/tool_approval.js +10 -0
- package/src/tools/file_reader.js +49 -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 +38 -2
- package/src/util/config_migration.js +174 -0
- package/src/util/path_validator.js +178 -0
- package/src/util/prompt_loader.js +68 -1
- package/src/util/safe_fs.js +43 -3
- package/payload_viewer/out/_next/static/chunks/ecd2072ebf41611f.css +0 -3
- /package/payload_viewer/out/_next/static/{d0-fu2rgYnshgGFPxr1CR → lHmNygVpv4N1VR0LdnwkJ}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{d0-fu2rgYnshgGFPxr1CR → lHmNygVpv4N1VR0LdnwkJ}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{d0-fu2rgYnshgGFPxr1CR → lHmNygVpv4N1VR0LdnwkJ}/_ssgManifest.js +0 -0
package/src/config/ai_models.js
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* 새 모델 추가 시 이 파일만 수정하면 됩니다.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* 공식 지원 Provider 목록
|
|
10
|
+
* 런타임에서 이 목록에 포함된 provider의 모델만 노출됩니다.
|
|
11
|
+
* 다른 provider의 구현 코드는 유지되지만 사용자에게 노출되지 않습니다.
|
|
12
|
+
*/
|
|
13
|
+
export const ENABLED_PROVIDERS = ['openai', 'zai'];
|
|
14
|
+
|
|
8
15
|
export const AI_MODELS = {
|
|
9
16
|
// ========================================
|
|
10
17
|
// Claude 시리즈
|
|
@@ -62,6 +69,40 @@ export const AI_MODELS = {
|
|
|
62
69
|
maxTokens: 4096,
|
|
63
70
|
},
|
|
64
71
|
|
|
72
|
+
// ========================================
|
|
73
|
+
// Z.AI GLM 시리즈 (Anthropic API 호환)
|
|
74
|
+
// - interleaved-thinking 지원
|
|
75
|
+
// - 프롬프트 캐싱 지원 (cache_read_input_tokens)
|
|
76
|
+
// - 서버 측 웹 검색 지원
|
|
77
|
+
// ========================================
|
|
78
|
+
'glm-4.7': {
|
|
79
|
+
provider: 'zai',
|
|
80
|
+
name: 'GLM-4.7',
|
|
81
|
+
contextWindow: 128000,
|
|
82
|
+
maxTokens: 8192,
|
|
83
|
+
supportsThinking: true,
|
|
84
|
+
supportsCaching: true,
|
|
85
|
+
supportsServerTools: true,
|
|
86
|
+
},
|
|
87
|
+
'glm-4.5-air': {
|
|
88
|
+
provider: 'zai',
|
|
89
|
+
name: 'GLM-4.5 Air',
|
|
90
|
+
contextWindow: 128000,
|
|
91
|
+
maxTokens: 8192,
|
|
92
|
+
supportsThinking: true,
|
|
93
|
+
supportsCaching: true,
|
|
94
|
+
supportsServerTools: true,
|
|
95
|
+
},
|
|
96
|
+
'glm-4.5': {
|
|
97
|
+
provider: 'zai',
|
|
98
|
+
name: 'GLM-4.5',
|
|
99
|
+
contextWindow: 128000,
|
|
100
|
+
maxTokens: 8192,
|
|
101
|
+
supportsThinking: true,
|
|
102
|
+
supportsCaching: true,
|
|
103
|
+
supportsServerTools: true,
|
|
104
|
+
},
|
|
105
|
+
|
|
65
106
|
// ========================================
|
|
66
107
|
// Google Gemini 시리즈
|
|
67
108
|
// ========================================
|
|
@@ -137,6 +178,14 @@ export function getModelInfo(modelId) {
|
|
|
137
178
|
return AI_MODELS[modelId] || null;
|
|
138
179
|
}
|
|
139
180
|
|
|
181
|
+
/**
|
|
182
|
+
* 모델이 공식 지원 provider인지 확인
|
|
183
|
+
*/
|
|
184
|
+
export function isModelEnabled(modelId) {
|
|
185
|
+
const model = AI_MODELS[modelId];
|
|
186
|
+
return model ? ENABLED_PROVIDERS.includes(model.provider) : false;
|
|
187
|
+
}
|
|
188
|
+
|
|
140
189
|
/**
|
|
141
190
|
* 모델 ID로 max_tokens 가져오기
|
|
142
191
|
*/
|
|
@@ -154,16 +203,21 @@ export function getContextWindow(modelId) {
|
|
|
154
203
|
}
|
|
155
204
|
|
|
156
205
|
/**
|
|
157
|
-
* 모든 모델 ID 목록 가져오기
|
|
206
|
+
* 모든 모델 ID 목록 가져오기 (공식 지원 provider만)
|
|
158
207
|
*/
|
|
159
208
|
export function getAllModelIds() {
|
|
160
|
-
return Object.keys(AI_MODELS)
|
|
209
|
+
return Object.keys(AI_MODELS).filter(
|
|
210
|
+
modelId => ENABLED_PROVIDERS.includes(AI_MODELS[modelId].provider)
|
|
211
|
+
);
|
|
161
212
|
}
|
|
162
213
|
|
|
163
214
|
/**
|
|
164
|
-
* 특정 제조사의 모델 ID 목록 가져오기
|
|
215
|
+
* 특정 제조사의 모델 ID 목록 가져오기 (공식 지원 provider만)
|
|
165
216
|
*/
|
|
166
217
|
export function getModelsByProvider(provider) {
|
|
218
|
+
if (!ENABLED_PROVIDERS.includes(provider)) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
167
221
|
return Object.keys(AI_MODELS).filter(
|
|
168
222
|
modelId => AI_MODELS[modelId].provider === provider
|
|
169
223
|
);
|
|
@@ -177,14 +231,51 @@ export function getGPT5Models() {
|
|
|
177
231
|
}
|
|
178
232
|
|
|
179
233
|
/**
|
|
180
|
-
* Reasoning 지원 모델 ID 목록
|
|
234
|
+
* Reasoning 지원 모델 ID 목록 (공식 지원 provider만)
|
|
181
235
|
*/
|
|
182
236
|
export function getReasoningModels() {
|
|
183
237
|
return Object.keys(AI_MODELS).filter(
|
|
184
|
-
modelId => AI_MODELS[modelId].supportsReasoning
|
|
238
|
+
modelId => AI_MODELS[modelId].supportsReasoning &&
|
|
239
|
+
ENABLED_PROVIDERS.includes(AI_MODELS[modelId].provider)
|
|
185
240
|
);
|
|
186
241
|
}
|
|
187
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Thinking 지원 모델 ID 목록 (Z.AI GLM 등, 공식 지원 provider만)
|
|
245
|
+
*/
|
|
246
|
+
export function getThinkingModels() {
|
|
247
|
+
return Object.keys(AI_MODELS).filter(
|
|
248
|
+
modelId => AI_MODELS[modelId].supportsThinking &&
|
|
249
|
+
ENABLED_PROVIDERS.includes(AI_MODELS[modelId].provider)
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 캐싱 지원 모델 ID 목록 (공식 지원 provider만)
|
|
255
|
+
*/
|
|
256
|
+
export function getCachingModels() {
|
|
257
|
+
return Object.keys(AI_MODELS).filter(
|
|
258
|
+
modelId => AI_MODELS[modelId].supportsCaching &&
|
|
259
|
+
ENABLED_PROVIDERS.includes(AI_MODELS[modelId].provider)
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 특정 모델이 thinking을 지원하는지 확인
|
|
265
|
+
*/
|
|
266
|
+
export function supportsThinking(modelId) {
|
|
267
|
+
const model = AI_MODELS[modelId];
|
|
268
|
+
return model?.supportsThinking || false;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 특정 모델이 캐싱을 지원하는지 확인
|
|
273
|
+
*/
|
|
274
|
+
export function supportsCaching(modelId) {
|
|
275
|
+
const model = AI_MODELS[modelId];
|
|
276
|
+
return model?.supportsCaching || false;
|
|
277
|
+
}
|
|
278
|
+
|
|
188
279
|
/**
|
|
189
280
|
* 특정 모델이 특정 reasoning effort를 지원하는지 확인
|
|
190
281
|
*/
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 전역 상수 정의
|
|
3
|
+
* 하드코딩된 값들을 중앙에서 관리합니다.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================
|
|
7
|
+
// Session 관련 상수
|
|
8
|
+
// ============================================
|
|
9
|
+
|
|
10
|
+
/** 서브미션 이름의 최대 길이 */
|
|
11
|
+
export const SUB_MISSION_MAX_LENGTH = 120;
|
|
12
|
+
|
|
13
|
+
/** 기본 최대 반복 횟수 */
|
|
14
|
+
export const DEFAULT_MAX_ITERATIONS = 50;
|
|
15
|
+
|
|
16
|
+
/** reasoning만 있는 응답의 최대 허용 횟수 */
|
|
17
|
+
export const MAX_REASONING_ONLY_RESPONSES = 5;
|
|
18
|
+
|
|
19
|
+
// ============================================
|
|
20
|
+
// Ripgrep 관련 상수
|
|
21
|
+
// ============================================
|
|
22
|
+
|
|
23
|
+
/** 기본 타임아웃 (ms) */
|
|
24
|
+
export const RIPGREP_DEFAULT_TIMEOUT_MS = 120000;
|
|
25
|
+
|
|
26
|
+
/** 기본 최대 매치 수 */
|
|
27
|
+
export const RIPGREP_DEFAULT_MAX_COUNT = 500;
|
|
28
|
+
|
|
29
|
+
/** 최대 출력 크기 (bytes) */
|
|
30
|
+
export const RIPGREP_MAX_OUTPUT_SIZE = 30000;
|
|
31
|
+
|
|
32
|
+
// ============================================
|
|
33
|
+
// File Reader 관련 상수
|
|
34
|
+
// ============================================
|
|
35
|
+
|
|
36
|
+
/** 파일 읽기 최대 줄 수 */
|
|
37
|
+
export const FILE_READER_MAX_LINES = 2000;
|
|
38
|
+
|
|
39
|
+
/** 파일 크기 제한 (bytes) - 10MB */
|
|
40
|
+
export const FILE_READER_MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
41
|
+
|
|
42
|
+
// ============================================
|
|
43
|
+
// AI 요청 관련 상수
|
|
44
|
+
// ============================================
|
|
45
|
+
|
|
46
|
+
/** 컨텍스트 trim 후 재시도를 위한 메시지 최소 수 */
|
|
47
|
+
export const MIN_MESSAGES_FOR_RETRY = 3;
|
|
48
|
+
|
|
49
|
+
// ============================================
|
|
50
|
+
// UI/Output 관련 상수
|
|
51
|
+
// ============================================
|
|
52
|
+
|
|
53
|
+
/** 출력 클램핑 최대 길이 */
|
|
54
|
+
export const OUTPUT_CLAMP_MAX_LENGTH = 30000;
|
|
55
|
+
|
|
56
|
+
// ============================================
|
|
57
|
+
// MCP 관련 상수
|
|
58
|
+
// ============================================
|
|
59
|
+
|
|
60
|
+
/** MCP 서버 연결 타임아웃 (ms) */
|
|
61
|
+
export const MCP_CONNECTION_TIMEOUT_MS = 30000;
|
|
62
|
+
|
|
63
|
+
// ============================================
|
|
64
|
+
// 에러 재시도 관련 상수
|
|
65
|
+
// ============================================
|
|
66
|
+
|
|
67
|
+
/** 세션 저장 최대 재시도 횟수 */
|
|
68
|
+
export const SESSION_SAVE_MAX_RETRIES = 3;
|
|
69
|
+
|
|
70
|
+
/** 세션 저장 재시도 지연 (ms) */
|
|
71
|
+
export const SESSION_SAVE_RETRY_DELAY_MS = 1000;
|
package/src/frontend/App.js
CHANGED
|
@@ -1022,14 +1022,15 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
1022
1022
|
);
|
|
1023
1023
|
}
|
|
1024
1024
|
|
|
1025
|
-
return React.createElement(Box, { flexDirection: "column", padding: 0 },
|
|
1025
|
+
return React.createElement(Box, { flexDirection: "column", padding: 0, width: '100%' },
|
|
1026
1026
|
// History area (grows to fill available space, shrinks when needed)
|
|
1027
1027
|
React.createElement(Box, {
|
|
1028
1028
|
flexDirection: "column",
|
|
1029
1029
|
flexGrow: 1,
|
|
1030
1030
|
flexShrink: 1,
|
|
1031
1031
|
overflow: 'hidden', // Prevent history from pushing out fixed elements
|
|
1032
|
-
gap: 0 // No gap between children
|
|
1032
|
+
gap: 0, // No gap between children
|
|
1033
|
+
width: '100%' // Ensure full terminal width
|
|
1033
1034
|
},
|
|
1034
1035
|
// React.createElement(BlankLine, {
|
|
1035
1036
|
// reason: 'separator between static and dynamic',
|
|
@@ -1040,9 +1041,7 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
1040
1041
|
|
|
1041
1042
|
// ===== Static 영역 시작 =====
|
|
1042
1043
|
// Static items: Header와 히스토리 포함
|
|
1043
|
-
React.createElement(
|
|
1044
|
-
React.createElement(Static, { items: staticItems }, (item) => item)
|
|
1045
|
-
),
|
|
1044
|
+
React.createElement(Static, { items: staticItems }, (item) => item),
|
|
1046
1045
|
// ===== Static 영역 끝 =====
|
|
1047
1046
|
|
|
1048
1047
|
// Static과 Dynamic 사이 구분선
|
|
@@ -126,7 +126,7 @@ function CodeExecutionDisplay({ item, hasFollowingResult, nextItem, isPending })
|
|
|
126
126
|
const iconColor = isPending ? theme.text.secondary : config.color;
|
|
127
127
|
|
|
128
128
|
if (isDenied) {
|
|
129
|
-
return React.createElement(Box, { flexDirection: "column", marginBottom, marginLeft: 2 },
|
|
129
|
+
return React.createElement(Box, { flexDirection: "column", marginBottom, marginLeft: 2, width: '100%' },
|
|
130
130
|
React.createElement(Box, { flexDirection: "row", marginBottom: 0 },
|
|
131
131
|
React.createElement(Text, { color: iconColor, bold: true }, showIcon ? config.icon : ' '),
|
|
132
132
|
React.createElement(Text, { color: theme.text.primary }, 'Executing '),
|
|
@@ -138,14 +138,14 @@ function CodeExecutionDisplay({ item, hasFollowingResult, nextItem, isPending })
|
|
|
138
138
|
);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
return React.createElement(Box, { flexDirection: "column", marginBottom, marginLeft: 2 },
|
|
141
|
+
return React.createElement(Box, { flexDirection: "column", marginBottom, marginLeft: 2, width: '100%' },
|
|
142
142
|
React.createElement(Box, { flexDirection: "row", marginBottom: 0 },
|
|
143
143
|
React.createElement(Text, { color: iconColor, bold: true }, showIcon ? config.icon : ' '),
|
|
144
144
|
React.createElement(Text, { color: theme.text.primary }, 'Executing '),
|
|
145
145
|
React.createElement(Text, { color: theme.status.warning, bold: true }, languageName),
|
|
146
146
|
// React.createElement(Text, { color: theme.text.primary }, ' code:')
|
|
147
147
|
),
|
|
148
|
-
React.createElement(Box, { marginLeft: 2 },
|
|
148
|
+
React.createElement(Box, { marginLeft: 2, flexGrow: 1 },
|
|
149
149
|
colorizeCode(item.code, item.language, true)
|
|
150
150
|
)
|
|
151
151
|
);
|
|
@@ -174,15 +174,16 @@ function CodeResultDisplay({ item }) {
|
|
|
174
174
|
borderStyle: "round",
|
|
175
175
|
borderColor: theme.border.default,
|
|
176
176
|
paddingX: 1,
|
|
177
|
-
flexGrow: 1
|
|
177
|
+
flexGrow: 1,
|
|
178
|
+
width: '100%'
|
|
178
179
|
},
|
|
179
|
-
hasStdout && React.createElement(Box, { marginBottom: 0 },
|
|
180
|
+
hasStdout && React.createElement(Box, { marginBottom: 0, width: '100%' },
|
|
180
181
|
React.createElement(Text, { color: theme.text.primary }, item.stdout)
|
|
181
182
|
),
|
|
182
|
-
hasStderr && React.createElement(Box, { marginBottom: 0 },
|
|
183
|
+
hasStderr && React.createElement(Box, { marginBottom: 0, width: '100%' },
|
|
183
184
|
React.createElement(Text, { color: theme.status.error }, item.stderr)
|
|
184
185
|
),
|
|
185
|
-
!hasStdout && !hasStderr && React.createElement(Box, { marginBottom: 0 },
|
|
186
|
+
!hasStdout && !hasStderr && React.createElement(Box, { marginBottom: 0, width: '100%' },
|
|
186
187
|
React.createElement(Text, { color: theme.text.secondary, dimColor: true }, '(no output)')
|
|
187
188
|
)
|
|
188
189
|
)
|
|
@@ -552,7 +553,7 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
552
553
|
|
|
553
554
|
// 중간 안내 메시지 (assistant_progress) - dimColor로 표시
|
|
554
555
|
if (type === 'assistant_progress') {
|
|
555
|
-
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2 },
|
|
556
|
+
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
556
557
|
React.createElement(Text, {
|
|
557
558
|
color: theme.text.secondary,
|
|
558
559
|
dimColor: true
|
|
@@ -565,7 +566,7 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
565
566
|
|
|
566
567
|
// 시스템 메시지는 마크다운 렌더링 적용
|
|
567
568
|
if (type === 'system') {
|
|
568
|
-
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2 },
|
|
569
|
+
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
569
570
|
React.createElement(Text, {
|
|
570
571
|
color: config.color,
|
|
571
572
|
bold: config.bold
|
|
@@ -578,7 +579,7 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
578
579
|
|
|
579
580
|
// 에러 메시지는 빨간색으로 강조하고 마크다운 없이 표시
|
|
580
581
|
if (type === 'error') {
|
|
581
|
-
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2 },
|
|
582
|
+
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
582
583
|
React.createElement(Text, {
|
|
583
584
|
color: config.color,
|
|
584
585
|
bold: config.bold
|
|
@@ -634,8 +635,8 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
634
635
|
// Check if the tool was denied by user
|
|
635
636
|
if (diffData.isDenied) {
|
|
636
637
|
const displayName = getToolDisplayName(toolName);
|
|
637
|
-
|
|
638
|
-
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2 },
|
|
638
|
+
|
|
639
|
+
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
639
640
|
React.createElement(Box, { flexDirection: "row" },
|
|
640
641
|
React.createElement(Text, { color: config.color, bold: config.bold }, config.icon),
|
|
641
642
|
React.createElement(Text, { color: 'white', bold: true }, displayName)
|
|
@@ -645,12 +646,12 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
645
646
|
)
|
|
646
647
|
);
|
|
647
648
|
}
|
|
648
|
-
|
|
649
|
+
|
|
649
650
|
// fallback: 기본 tool 표시 (실패했지만 deny는 아닌 경우)
|
|
650
651
|
const displayName = getToolDisplayName(toolName);
|
|
651
652
|
const formattedArgs = formatToolCall(toolName, effectiveArgs || {});
|
|
652
653
|
|
|
653
|
-
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2 },
|
|
654
|
+
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
654
655
|
React.createElement(Text, { color: config.color, bold: config.bold }, config.icon),
|
|
655
656
|
React.createElement(Text, { color: 'white', bold: true }, displayName),
|
|
656
657
|
React.createElement(Text, { color: theme.text.secondary }, ` ${formattedArgs}`)
|
|
@@ -660,7 +661,7 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
660
661
|
const displayName = getToolDisplayName(toolName);
|
|
661
662
|
const formattedArgs = formatToolCall(toolName, effectiveArgs || {});
|
|
662
663
|
|
|
663
|
-
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2 },
|
|
664
|
+
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
664
665
|
React.createElement(Text, { color: config.color, bold: config.bold }, config.icon),
|
|
665
666
|
React.createElement(Text, { color: 'white', bold: true }, displayName),
|
|
666
667
|
React.createElement(Text, { color: theme.text.secondary }, ` ${formattedArgs}`)
|
|
@@ -709,13 +710,13 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
709
710
|
return result;
|
|
710
711
|
} else if (diffData?.loaded && !diffData.hasContent) {
|
|
711
712
|
debugLog('Rendering FALLBACK (no content) for edit_file_replace');
|
|
712
|
-
|
|
713
|
+
|
|
713
714
|
// Check if the tool was denied by user
|
|
714
715
|
if (diffData.isDenied) {
|
|
715
716
|
debugLog('Tool was DENIED by user');
|
|
716
717
|
const displayName = getToolDisplayName(toolName);
|
|
717
|
-
|
|
718
|
-
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2 },
|
|
718
|
+
|
|
719
|
+
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
719
720
|
React.createElement(Box, { flexDirection: "row" },
|
|
720
721
|
React.createElement(Text, { color: config.color, bold: config.bold }, config.icon),
|
|
721
722
|
React.createElement(Text, { color: 'white', bold: true }, displayName)
|
|
@@ -725,12 +726,12 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
725
726
|
)
|
|
726
727
|
);
|
|
727
728
|
}
|
|
728
|
-
|
|
729
|
+
|
|
729
730
|
// fallback: 기본 tool 표시 (실패했지만 deny는 아닌 경우)
|
|
730
731
|
const displayName = getToolDisplayName(toolName);
|
|
731
732
|
const formattedArgs = formatToolCall(toolName, effectiveArgs || {});
|
|
732
733
|
|
|
733
|
-
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2 },
|
|
734
|
+
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
734
735
|
React.createElement(Text, { color: config.color, bold: config.bold }, config.icon),
|
|
735
736
|
React.createElement(Text, { color: 'white', bold: true }, displayName),
|
|
736
737
|
React.createElement(Text, { color: theme.text.secondary }, ` ${formattedArgs}`)
|
|
@@ -741,7 +742,7 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
741
742
|
const displayName = getToolDisplayName(toolName);
|
|
742
743
|
const formattedArgs = formatToolCall(toolName, effectiveArgs || {});
|
|
743
744
|
|
|
744
|
-
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2 },
|
|
745
|
+
return React.createElement(Box, { flexDirection: "row", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
745
746
|
React.createElement(Text, { color: config.color, bold: config.bold }, config.icon),
|
|
746
747
|
React.createElement(Text, { color: 'white', bold: true }, displayName),
|
|
747
748
|
React.createElement(Text, { color: theme.text.secondary }, ` ${formattedArgs}`)
|
|
@@ -764,7 +765,7 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
764
765
|
const displayIcon = showIcon ? config.icon : ' ';
|
|
765
766
|
|
|
766
767
|
if (isDenied) {
|
|
767
|
-
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2 },
|
|
768
|
+
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
768
769
|
React.createElement(Box, { flexDirection: "row" },
|
|
769
770
|
React.createElement(Text, { color: iconColor, bold: config.bold }, displayIcon),
|
|
770
771
|
React.createElement(Text, { color: 'white', bold: true }, displayName)
|
|
@@ -775,7 +776,7 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
775
776
|
);
|
|
776
777
|
}
|
|
777
778
|
|
|
778
|
-
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2 },
|
|
779
|
+
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
779
780
|
React.createElement(Box, { flexDirection: "row" },
|
|
780
781
|
React.createElement(Text, { color: iconColor, bold: config.bold }, displayIcon),
|
|
781
782
|
React.createElement(Text, {
|
|
@@ -836,7 +837,7 @@ function StandardDisplay({ item, isPending, hasFollowingResult, nextItem, isLast
|
|
|
836
837
|
}, text);
|
|
837
838
|
}
|
|
838
839
|
|
|
839
|
-
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2 },
|
|
840
|
+
return React.createElement(Box, { flexDirection: "column", marginBottom, marginTop, marginLeft: 2, width: '100%' },
|
|
840
841
|
React.createElement(Box, { flexDirection: "row" },
|
|
841
842
|
React.createElement(Text, { color: config.color, bold: config.bold }, config.icon),
|
|
842
843
|
React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { Box, Text } from 'ink';
|
|
7
7
|
|
|
8
|
-
export function HelpView({ commands }) {
|
|
8
|
+
export function HelpView({ commands, customCommands = [], skills = [] }) {
|
|
9
9
|
// 커맨드를 카테고리별로 분류
|
|
10
10
|
const aiCommands = commands.filter(cmd =>
|
|
11
11
|
['model', 'reasoning_effort', 'apikey'].includes(cmd.name)
|
|
@@ -59,6 +59,96 @@ export function HelpView({ commands }) {
|
|
|
59
59
|
);
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
// 커스텀 커맨드 렌더링
|
|
63
|
+
const renderCustomCommands = () => {
|
|
64
|
+
if (customCommands.length === 0) return null;
|
|
65
|
+
|
|
66
|
+
return React.createElement(Box, {
|
|
67
|
+
flexDirection: 'column',
|
|
68
|
+
borderStyle: 'round',
|
|
69
|
+
borderColor: 'gray',
|
|
70
|
+
paddingX: 2,
|
|
71
|
+
paddingY: 1,
|
|
72
|
+
marginBottom: 1
|
|
73
|
+
},
|
|
74
|
+
React.createElement(Text, {
|
|
75
|
+
bold: true,
|
|
76
|
+
color: 'cyan'
|
|
77
|
+
}, '📋 Custom Commands'),
|
|
78
|
+
React.createElement(Text, null),
|
|
79
|
+
|
|
80
|
+
customCommands.map(cmd =>
|
|
81
|
+
React.createElement(Box, {
|
|
82
|
+
key: cmd.name,
|
|
83
|
+
flexDirection: 'column',
|
|
84
|
+
marginBottom: 1
|
|
85
|
+
},
|
|
86
|
+
React.createElement(Box, { flexDirection: 'row', gap: 1 },
|
|
87
|
+
React.createElement(Text, { color: 'green', bold: true }, '•'),
|
|
88
|
+
React.createElement(Text, { color: 'white', bold: true },
|
|
89
|
+
`/${cmd.name}${cmd.argumentHint ? ' ' + cmd.argumentHint : ''}`
|
|
90
|
+
),
|
|
91
|
+
React.createElement(Text, { color: 'gray', dimColor: true },
|
|
92
|
+
` (${cmd.source})`
|
|
93
|
+
)
|
|
94
|
+
),
|
|
95
|
+
cmd.description && React.createElement(Box, {
|
|
96
|
+
flexDirection: 'row',
|
|
97
|
+
marginLeft: 2
|
|
98
|
+
},
|
|
99
|
+
React.createElement(Text, { color: 'gray' },
|
|
100
|
+
cmd.description.substring(0, 60) + (cmd.description.length > 60 ? '...' : '')
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// 스킬 렌더링
|
|
109
|
+
const renderSkills = () => {
|
|
110
|
+
if (skills.length === 0) return null;
|
|
111
|
+
|
|
112
|
+
return React.createElement(Box, {
|
|
113
|
+
flexDirection: 'column',
|
|
114
|
+
borderStyle: 'round',
|
|
115
|
+
borderColor: 'gray',
|
|
116
|
+
paddingX: 2,
|
|
117
|
+
paddingY: 1,
|
|
118
|
+
marginBottom: 1
|
|
119
|
+
},
|
|
120
|
+
React.createElement(Text, {
|
|
121
|
+
bold: true,
|
|
122
|
+
color: 'cyan'
|
|
123
|
+
}, '🎯 Skills'),
|
|
124
|
+
React.createElement(Text, null),
|
|
125
|
+
|
|
126
|
+
skills.map(skill =>
|
|
127
|
+
React.createElement(Box, {
|
|
128
|
+
key: skill.name,
|
|
129
|
+
flexDirection: 'column',
|
|
130
|
+
marginBottom: 1
|
|
131
|
+
},
|
|
132
|
+
React.createElement(Box, { flexDirection: 'row', gap: 1 },
|
|
133
|
+
React.createElement(Text, { color: 'green', bold: true }, '•'),
|
|
134
|
+
React.createElement(Text, { color: 'white', bold: true }, `/${skill.name}`),
|
|
135
|
+
React.createElement(Text, { color: 'gray', dimColor: true },
|
|
136
|
+
` (${skill.source})`
|
|
137
|
+
)
|
|
138
|
+
),
|
|
139
|
+
skill.description && React.createElement(Box, {
|
|
140
|
+
flexDirection: 'row',
|
|
141
|
+
marginLeft: 2
|
|
142
|
+
},
|
|
143
|
+
React.createElement(Text, { color: 'gray' },
|
|
144
|
+
skill.description.substring(0, 60) + (skill.description.length > 60 ? '...' : '')
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
62
152
|
return React.createElement(Box, {
|
|
63
153
|
flexDirection: 'column',
|
|
64
154
|
paddingX: 2,
|
|
@@ -89,6 +179,12 @@ export function HelpView({ commands }) {
|
|
|
89
179
|
// Other Commands
|
|
90
180
|
renderCommandGroup('🔧 Other', otherCommands),
|
|
91
181
|
|
|
182
|
+
// Custom Commands
|
|
183
|
+
renderCustomCommands(),
|
|
184
|
+
|
|
185
|
+
// Skills
|
|
186
|
+
renderSkills(),
|
|
187
|
+
|
|
92
188
|
// Footer
|
|
93
189
|
React.createElement(Box, {
|
|
94
190
|
flexDirection: 'column',
|
|
@@ -104,7 +200,15 @@ export function HelpView({ commands }) {
|
|
|
104
200
|
React.createElement(Text, {
|
|
105
201
|
dimColor: true,
|
|
106
202
|
italic: true
|
|
107
|
-
}, '💡 Press Ctrl+C to cancel current operation')
|
|
203
|
+
}, '💡 Press Ctrl+C to cancel current operation'),
|
|
204
|
+
React.createElement(Text, {
|
|
205
|
+
dimColor: true,
|
|
206
|
+
italic: true
|
|
207
|
+
}, '💡 Custom commands: ~/.aiexe/commands/ or .aiexe/commands/'),
|
|
208
|
+
React.createElement(Text, {
|
|
209
|
+
dimColor: true,
|
|
210
|
+
italic: true
|
|
211
|
+
}, '💡 Skills: ~/.aiexe/skills/ or .aiexe/skills/')
|
|
108
212
|
)
|
|
109
213
|
);
|
|
110
214
|
}
|