codemini-cli 0.1.12 → 0.1.13
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/package.json +1 -1
- package/src/core/agent-loop.js +17 -0
- package/src/core/chat-runtime.js +364 -44
- package/src/core/config-store.js +39 -3
- package/src/core/reply-language.js +25 -0
- package/src/core/shell-profile.js +1 -1
- package/src/core/soul.js +3 -1
- package/src/core/tools.js +884 -8
- package/src/tui/chat-app.js +89 -15
package/src/tui/chat-app.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Box, Text, useApp, useInput } from 'ink';
|
|
|
3
3
|
import { shouldCaptureEscapeSequence } from './input-escape.js';
|
|
4
4
|
|
|
5
5
|
const h = React.createElement;
|
|
6
|
+
const SUGGESTION_PAGE_SIZE = 8;
|
|
6
7
|
const BANNER = [
|
|
7
8
|
' ██████ ██████ ██████ ███████ ███ ███ ██ ███ ██ ██ ',
|
|
8
9
|
'██ ██ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ',
|
|
@@ -122,13 +123,13 @@ const TUI_COPY = {
|
|
|
122
123
|
},
|
|
123
124
|
suggestion: {
|
|
124
125
|
singleTab: 'Tab 补全当前命令',
|
|
125
|
-
navFill: 'Tab
|
|
126
|
-
navEnter: 'Tab 进入切换模式,再用 ↑↓
|
|
126
|
+
navFill: 'Tab 保持切换模式,↑↓选择,←→翻页,Enter 填入',
|
|
127
|
+
navEnter: 'Tab 进入切换模式,再用 ↑↓ 选择,←→翻页',
|
|
127
128
|
noSuggestions: '/ 查看命令,Tab 自动补全,↑↓ 历史,Ctrl+T 展开工具',
|
|
128
129
|
oneNav: 'Tab 或 Enter 填入当前命令,↑↓ 历史',
|
|
129
130
|
oneIdle: 'Tab 补全当前唯一候选,Enter 直接发送,↑↓ 历史',
|
|
130
|
-
manyNav: (count) => `Tab
|
|
131
|
-
manyIdle: (count) => `Tab 进入候选切换 (${count} 项),↑↓
|
|
131
|
+
manyNav: (count) => `Tab 切换候选,↑↓选择,←→翻页,Enter 填入 (${count} 项)`,
|
|
132
|
+
manyIdle: (count) => `Tab 进入候选切换 (${count} 项),↑↓ 历史,←→翻页`
|
|
132
133
|
},
|
|
133
134
|
runtime: {
|
|
134
135
|
sendingToGateway: '正在发送到网关',
|
|
@@ -224,13 +225,13 @@ const TUI_COPY = {
|
|
|
224
225
|
},
|
|
225
226
|
suggestion: {
|
|
226
227
|
singleTab: 'Tab completes the current command',
|
|
227
|
-
navFill: 'Tab stays in pick mode, ↑↓ select, Enter applies',
|
|
228
|
-
navEnter: 'Tab enters pick mode, then use ↑↓ to choose',
|
|
228
|
+
navFill: 'Tab stays in pick mode, ↑↓ select, ←→ page, Enter applies',
|
|
229
|
+
navEnter: 'Tab enters pick mode, then use ↑↓ to choose, ←→ page',
|
|
229
230
|
noSuggestions: '/ shows commands, Tab autocompletes, ↑↓ history, Ctrl+T tools',
|
|
230
231
|
oneNav: 'Tab or Enter applies the current command, ↑↓ history',
|
|
231
232
|
oneIdle: 'Tab completes the only candidate, Enter sends, ↑↓ history',
|
|
232
|
-
manyNav: (count) => `Tab cycles candidates, ↑↓ select, Enter applies (${count} items)`,
|
|
233
|
-
manyIdle: (count) => `Tab enters candidate mode (${count} items), ↑↓ history`
|
|
233
|
+
manyNav: (count) => `Tab cycles candidates, ↑↓ select, ←→ page, Enter applies (${count} items)`,
|
|
234
|
+
manyIdle: (count) => `Tab enters candidate mode (${count} items), ↑↓ history, ←→ page`
|
|
234
235
|
},
|
|
235
236
|
runtime: {
|
|
236
237
|
sendingToGateway: 'sending to gateway',
|
|
@@ -883,6 +884,66 @@ function getSuggestionDisplay(item) {
|
|
|
883
884
|
return typeof item === 'string' ? item : String(item?.display || item?.value || '');
|
|
884
885
|
}
|
|
885
886
|
|
|
887
|
+
function getSuggestionDescription(item) {
|
|
888
|
+
return typeof item === 'string' ? '' : String(item?.description || '');
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
export function formatSuggestionDescription(text, maxChars = 40) {
|
|
892
|
+
const value = String(text || '').replace(/\s+/g, ' ').trim();
|
|
893
|
+
if (!value) return '';
|
|
894
|
+
const limit = Math.max(4, Number(maxChars) || 40);
|
|
895
|
+
if (value.length <= limit) return value;
|
|
896
|
+
return `${value.slice(0, limit - 3)}...`;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
export function getSuggestionPageState(commandSuggestions, menuIndex, pageSize = SUGGESTION_PAGE_SIZE) {
|
|
900
|
+
const items = Array.isArray(commandSuggestions) ? commandSuggestions : [];
|
|
901
|
+
const normalizedPageSize = Math.max(1, Number(pageSize) || SUGGESTION_PAGE_SIZE);
|
|
902
|
+
const safeIndex = items.length === 0 ? 0 : Math.max(0, Math.min(Number(menuIndex) || 0, items.length - 1));
|
|
903
|
+
const pageIndex = Math.floor(safeIndex / normalizedPageSize);
|
|
904
|
+
const pageCount = Math.max(1, Math.ceil(items.length / normalizedPageSize));
|
|
905
|
+
const pageStart = pageIndex * normalizedPageSize;
|
|
906
|
+
const pageEnd = Math.min(items.length, pageStart + normalizedPageSize);
|
|
907
|
+
return {
|
|
908
|
+
pageSize: normalizedPageSize,
|
|
909
|
+
safeIndex,
|
|
910
|
+
pageIndex,
|
|
911
|
+
pageCount,
|
|
912
|
+
pageStart,
|
|
913
|
+
pageEnd,
|
|
914
|
+
pageItems: items.slice(pageStart, pageEnd)
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
export function moveSuggestionSelection(currentIndex, itemCount, direction, pageSize = SUGGESTION_PAGE_SIZE) {
|
|
919
|
+
const total = Math.max(0, Number(itemCount) || 0);
|
|
920
|
+
if (total <= 0) return 0;
|
|
921
|
+
const normalizedPageSize = Math.max(1, Number(pageSize) || SUGGESTION_PAGE_SIZE);
|
|
922
|
+
const safeIndex = Math.max(0, Math.min(Number(currentIndex) || 0, total - 1));
|
|
923
|
+
|
|
924
|
+
if (direction === 'left') {
|
|
925
|
+
if (safeIndex < normalizedPageSize) return 0;
|
|
926
|
+
return Math.max(0, safeIndex - normalizedPageSize);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if (direction === 'right') {
|
|
930
|
+
const currentPageStart = Math.floor(safeIndex / normalizedPageSize) * normalizedPageSize;
|
|
931
|
+
const currentPageEnd = Math.min(total, currentPageStart + normalizedPageSize);
|
|
932
|
+
if (currentPageEnd >= total) return safeIndex;
|
|
933
|
+
return Math.min(total - 1, safeIndex + normalizedPageSize);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
if (direction === 'up') {
|
|
937
|
+
return Math.max(0, safeIndex - 1);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if (direction === 'down') {
|
|
941
|
+
return Math.min(total - 1, safeIndex + 1);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
return safeIndex;
|
|
945
|
+
}
|
|
946
|
+
|
|
886
947
|
function MessageBubble({ msg, loaderTick, showToolDetails, rowWindow = null, contentWidth = 72, copy }) {
|
|
887
948
|
if (msg?.planSummary || parseAutoPlanSummaryMessage(msg?.text)) {
|
|
888
949
|
return h(PlanSummaryBubble, { msg, copy });
|
|
@@ -1028,7 +1089,8 @@ function MessageList({ messages, loaderTick, showToolDetails, contentWidth = 72,
|
|
|
1028
1089
|
|
|
1029
1090
|
function SuggestionPanel({ commandSuggestions, suggestionNav, menuIndex, copy }) {
|
|
1030
1091
|
if (commandSuggestions.length === 0) return null;
|
|
1031
|
-
const
|
|
1092
|
+
const pageState = getSuggestionPageState(commandSuggestions, menuIndex);
|
|
1093
|
+
const grouped = groupCommandSuggestions(pageState.pageItems);
|
|
1032
1094
|
let flatIndex = -1;
|
|
1033
1095
|
const panelHint =
|
|
1034
1096
|
commandSuggestions.length === 1
|
|
@@ -1050,7 +1112,7 @@ function SuggestionPanel({ commandSuggestions, suggestionNav, menuIndex, copy })
|
|
|
1050
1112
|
Box,
|
|
1051
1113
|
{ marginBottom: 1 },
|
|
1052
1114
|
h(Text, { color: 'magentaBright' }, suggestionNav ? copy.generic.commandPaletteGroupedSelect : copy.generic.commandPaletteGroupedSuggestions),
|
|
1053
|
-
h(Text, { color: 'gray' }, ` ${panelHint}`)
|
|
1115
|
+
h(Text, { color: 'gray' }, ` ${panelHint} · ${pageState.pageIndex + 1}/${pageState.pageCount}`)
|
|
1054
1116
|
),
|
|
1055
1117
|
...grouped.flatMap(([group, items]) => {
|
|
1056
1118
|
const nodes = [
|
|
@@ -1063,13 +1125,17 @@ function SuggestionPanel({ commandSuggestions, suggestionNav, menuIndex, copy })
|
|
|
1063
1125
|
];
|
|
1064
1126
|
items.forEach((c) => {
|
|
1065
1127
|
flatIndex += 1;
|
|
1066
|
-
const active = suggestionNav && menuIndex === flatIndex;
|
|
1128
|
+
const active = suggestionNav && menuIndex === pageState.pageStart + flatIndex;
|
|
1067
1129
|
const label = getSuggestionDisplay(c);
|
|
1130
|
+
const description = formatSuggestionDescription(getSuggestionDescription(c), 42);
|
|
1068
1131
|
nodes.push(
|
|
1069
1132
|
h(
|
|
1070
1133
|
Box,
|
|
1071
1134
|
{ key: `opt-${group}-${getSuggestionValue(c)}` },
|
|
1072
|
-
h(Text, { color: active ? 'black' : 'magenta', backgroundColor: active ? 'magentaBright' : undefined }, `${active ? ' > ' : ' '}${label}`)
|
|
1135
|
+
h(Text, { color: active ? 'black' : 'magenta', backgroundColor: active ? 'magentaBright' : undefined }, `${active ? ' > ' : ' '}${label}`),
|
|
1136
|
+
description
|
|
1137
|
+
? h(Text, { color: active ? 'black' : 'gray', backgroundColor: active ? 'magentaBright' : undefined }, ` ${description}`)
|
|
1138
|
+
: null
|
|
1073
1139
|
)
|
|
1074
1140
|
);
|
|
1075
1141
|
});
|
|
@@ -1301,7 +1367,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
1301
1367
|
|
|
1302
1368
|
const commandSuggestions =
|
|
1303
1369
|
inputValue.startsWith('/')
|
|
1304
|
-
?
|
|
1370
|
+
? runtime.getCompletionOptions(inputValue) || []
|
|
1305
1371
|
: [];
|
|
1306
1372
|
const hasTransientPanels =
|
|
1307
1373
|
commandSuggestions.length > 0 || pendingQueue.length > 0 || debugKeys || Boolean(planState?.total);
|
|
@@ -1853,7 +1919,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
1853
1919
|
|
|
1854
1920
|
if (key.upArrow) {
|
|
1855
1921
|
if (suggestionNav && commandSuggestions.length > 0) {
|
|
1856
|
-
setMenuIndex((prev) =>
|
|
1922
|
+
setMenuIndex((prev) => moveSuggestionSelection(prev, commandSuggestions.length, 'up'));
|
|
1857
1923
|
return;
|
|
1858
1924
|
}
|
|
1859
1925
|
if (history.length === 0) return;
|
|
@@ -1879,7 +1945,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
1879
1945
|
|
|
1880
1946
|
if (key.downArrow) {
|
|
1881
1947
|
if (suggestionNav && commandSuggestions.length > 0) {
|
|
1882
|
-
setMenuIndex((prev) =>
|
|
1948
|
+
setMenuIndex((prev) => moveSuggestionSelection(prev, commandSuggestions.length, 'down'));
|
|
1883
1949
|
return;
|
|
1884
1950
|
}
|
|
1885
1951
|
if (history.length === 0 || historyIndex === null) return;
|
|
@@ -1900,6 +1966,10 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
1900
1966
|
return;
|
|
1901
1967
|
}
|
|
1902
1968
|
if (key.leftArrow) {
|
|
1969
|
+
if (suggestionNav && commandSuggestions.length > 0) {
|
|
1970
|
+
setMenuIndex((prev) => moveSuggestionSelection(prev, commandSuggestions.length, 'left'));
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1903
1973
|
setSuggestionNav(false);
|
|
1904
1974
|
const next = Math.max(0, cursorIndexRef.current - 1);
|
|
1905
1975
|
cursorIndexRef.current = next;
|
|
@@ -1907,6 +1977,10 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
1907
1977
|
return;
|
|
1908
1978
|
}
|
|
1909
1979
|
if (key.rightArrow) {
|
|
1980
|
+
if (suggestionNav && commandSuggestions.length > 0) {
|
|
1981
|
+
setMenuIndex((prev) => moveSuggestionSelection(prev, commandSuggestions.length, 'right'));
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1910
1984
|
setSuggestionNav(false);
|
|
1911
1985
|
const next = Math.min(inputValue.length, cursorIndexRef.current + 1);
|
|
1912
1986
|
cursorIndexRef.current = next;
|