@lazyingart/agintiflow 0.19.2 → 0.20.0
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/docs/model-selection.md +4 -0
- package/package.json +1 -1
- package/scripts/smoke-cli-chat.js +20 -1
- package/src/interactive-cli.js +270 -14
package/docs/model-selection.md
CHANGED
|
@@ -34,6 +34,8 @@ Interactive commands:
|
|
|
34
34
|
```text
|
|
35
35
|
/models
|
|
36
36
|
/venice
|
|
37
|
+
/route
|
|
38
|
+
/model
|
|
37
39
|
/route deepseek/deepseek-v4-flash
|
|
38
40
|
/model deepseek/deepseek-v4-pro
|
|
39
41
|
/spare openai/gpt-5.4 medium
|
|
@@ -41,6 +43,8 @@ Interactive commands:
|
|
|
41
43
|
/auxiliary model grsai/nano-banana-2
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
In the interactive CLI, `/route` and `/model` without arguments open a selector. Use Up/Down/Left/Right to move through choices, Enter to confirm, and Esc to cancel. Slash-command hints use the same arrow selection behavior: type a prefix such as `/mo`, use arrows to choose `/model` or `/models`, then press Enter or Tab.
|
|
47
|
+
|
|
44
48
|
`/venice` is a shortcut for:
|
|
45
49
|
|
|
46
50
|
```text
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a web-first coding agent and CLI with DeepSeek routing, sandboxed tools, model providers, canvas artifacts, and optional wrappers.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -137,9 +137,28 @@ try {
|
|
|
137
137
|
const queuedText = queuedPromptLayout.renderedRows
|
|
138
138
|
.map((line) => line.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, ""))
|
|
139
139
|
.join("\n");
|
|
140
|
-
if (
|
|
140
|
+
if (
|
|
141
|
+
!queuedText.includes("run running · tool: apply_patch") ||
|
|
142
|
+
!queuedText.includes("→ apply this") ||
|
|
143
|
+
!queuedText.includes("↳ run this") ||
|
|
144
|
+
!queuedText.includes("cwd /tmp/aginti-project")
|
|
145
|
+
) {
|
|
141
146
|
throw new Error("terminal prompt layout did not render live input queue and cwd footer");
|
|
142
147
|
}
|
|
148
|
+
const hintPromptLayout = buildPromptLayout("/mo", 3, 90, 24, { suggestions: ["/models", "/model"], suggestionIndex: 1 });
|
|
149
|
+
const hintText = hintPromptLayout.renderedRows
|
|
150
|
+
.map((line) => line.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, ""))
|
|
151
|
+
.join("\n");
|
|
152
|
+
if (!hintText.includes("user> /mo") || !hintText.includes("hint /models >/model")) {
|
|
153
|
+
throw new Error("terminal prompt layout did not align user and hint text columns");
|
|
154
|
+
}
|
|
155
|
+
const exactHintLayout = buildPromptLayout("/model", 6, 90, 24);
|
|
156
|
+
const exactHintText = exactHintLayout.renderedRows
|
|
157
|
+
.map((line) => line.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, ""))
|
|
158
|
+
.join("\n");
|
|
159
|
+
if (!exactHintText.includes("hint >/model") || exactHintText.includes("/models")) {
|
|
160
|
+
throw new Error("exact slash commands should not show broader prefix matches");
|
|
161
|
+
}
|
|
143
162
|
if (classifyEscapeAction({ active: false }) !== "noop") {
|
|
144
163
|
throw new Error("idle Esc should not redraw or clear the prompt");
|
|
145
164
|
}
|
package/src/interactive-cli.js
CHANGED
|
@@ -171,9 +171,20 @@ function promptGutter() {
|
|
|
171
171
|
function commandSuggestions(line = "") {
|
|
172
172
|
const trimmed = String(line || "");
|
|
173
173
|
if (!trimmed.startsWith("/") || /\s/.test(trimmed)) return [];
|
|
174
|
+
if (SLASH_COMMANDS.includes(trimmed)) return [trimmed];
|
|
174
175
|
return SLASH_COMMANDS.filter((command) => command.startsWith(trimmed)).slice(0, 8);
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
function renderSuggestionList(suggestions = [], selectedIndex = 0) {
|
|
179
|
+
return suggestions
|
|
180
|
+
.map((suggestion, index) => {
|
|
181
|
+
const active = index === selectedIndex;
|
|
182
|
+
if (!active) return suggestion;
|
|
183
|
+
return useColor ? color(suggestion, ansi.userBg, ansi.bold) : `>${suggestion}`;
|
|
184
|
+
})
|
|
185
|
+
.join(" ");
|
|
186
|
+
}
|
|
187
|
+
|
|
177
188
|
function resolveSlashCommand(command = "") {
|
|
178
189
|
const raw = String(command || "").trim();
|
|
179
190
|
if (!raw) return raw;
|
|
@@ -574,9 +585,9 @@ function printHelp() {
|
|
|
574
585
|
" /memory Alias for /instructions.",
|
|
575
586
|
" /models Show route/main/spare/wrapper/auxiliary model roles.",
|
|
576
587
|
" /venice Shortcut: use Venice Uncensored 1.2 for route and main roles.",
|
|
577
|
-
" /route
|
|
578
|
-
"
|
|
579
|
-
" /model
|
|
588
|
+
" /route [mode|provider/model]",
|
|
589
|
+
" Open route selector, or set routing/fast route model.",
|
|
590
|
+
" /model [provider/model] Open main-model selector, or set the active/main model.",
|
|
580
591
|
" /spare <provider/model> [reasoning]",
|
|
581
592
|
" Set spare model, e.g. /spare openai/gpt-5.4 medium.",
|
|
582
593
|
" /wrapper [on|off|codex model reasoning]",
|
|
@@ -730,14 +741,17 @@ export function buildPromptLayout(buffer = "", cursor = 0, width = terminalWidth
|
|
|
730
741
|
}
|
|
731
742
|
}
|
|
732
743
|
|
|
733
|
-
const suggestions =
|
|
744
|
+
const suggestions = Array.isArray(options.suggestions)
|
|
745
|
+
? options.suggestions
|
|
746
|
+
: commandSuggestions(safeBuffer.split("\n")[0] || "");
|
|
747
|
+
const suggestionIndex = clamp(Number(options.suggestionIndex) || 0, 0, Math.max(suggestions.length - 1, 0));
|
|
734
748
|
const emptyHint = "type a request, /help, Enter to send, Ctrl+J for newline";
|
|
735
749
|
const visible = promptVisibleWindow(rows, cursorRow, height);
|
|
736
750
|
const renderedRows = [];
|
|
737
751
|
let renderedCursorRow = cursorRow - visible.start;
|
|
738
752
|
|
|
739
753
|
if (options.statusLine) {
|
|
740
|
-
renderedRows.push(panelLine(
|
|
754
|
+
renderedRows.push(panelLine(`${labelText("run", { prompt: true })}${compactLine(options.statusLine, lineWidth - 10)}`, ansi.statusBg, lineWidth));
|
|
741
755
|
renderedCursorRow += 1;
|
|
742
756
|
}
|
|
743
757
|
|
|
@@ -767,10 +781,10 @@ export function buildPromptLayout(buffer = "", cursor = 0, width = terminalWidth
|
|
|
767
781
|
}
|
|
768
782
|
|
|
769
783
|
if (suggestions.length > 0) {
|
|
770
|
-
renderedRows.push(panelLine(
|
|
784
|
+
renderedRows.push(panelLine(`${labelText("hint", { prompt: true })}${renderSuggestionList(suggestions, suggestionIndex)}`, ansi.systemBg, lineWidth));
|
|
771
785
|
}
|
|
772
786
|
if (options.commandCwd) {
|
|
773
|
-
renderedRows.push(panelLine(
|
|
787
|
+
renderedRows.push(panelLine(`${labelText("cwd", { prompt: true })}${options.commandCwd}`, ansi.systemBg, lineWidth));
|
|
774
788
|
}
|
|
775
789
|
|
|
776
790
|
return {
|
|
@@ -884,6 +898,8 @@ function readTtyPrompt(options = {}) {
|
|
|
884
898
|
let historyIndex = promptHistory.length;
|
|
885
899
|
let draft = "";
|
|
886
900
|
let redrawHandle = null;
|
|
901
|
+
let suggestionAnchor = "";
|
|
902
|
+
let suggestionIndex = 0;
|
|
887
903
|
|
|
888
904
|
const cleanup = () => {
|
|
889
905
|
if (redrawHandle) {
|
|
@@ -901,14 +917,19 @@ function readTtyPrompt(options = {}) {
|
|
|
901
917
|
clearImmediate(redrawHandle);
|
|
902
918
|
redrawHandle = null;
|
|
903
919
|
}
|
|
904
|
-
|
|
920
|
+
const suggestions = commandSuggestions(suggestionAnchor || buffer.split("\n")[0] || "");
|
|
921
|
+
rendered = renderPromptBuffer(buffer, cursor, rendered, {
|
|
922
|
+
...options,
|
|
923
|
+
suggestions,
|
|
924
|
+
suggestionIndex,
|
|
925
|
+
});
|
|
905
926
|
};
|
|
906
927
|
|
|
907
928
|
const redraw = () => {
|
|
908
929
|
if (redrawHandle) return;
|
|
909
930
|
redrawHandle = setImmediate(() => {
|
|
910
931
|
redrawHandle = null;
|
|
911
|
-
|
|
932
|
+
renderNow();
|
|
912
933
|
});
|
|
913
934
|
};
|
|
914
935
|
|
|
@@ -922,13 +943,33 @@ function readTtyPrompt(options = {}) {
|
|
|
922
943
|
resolve(buffer);
|
|
923
944
|
};
|
|
924
945
|
|
|
925
|
-
const setBuffer = (nextBuffer, nextCursor = nextBuffer.length) => {
|
|
946
|
+
const setBuffer = (nextBuffer, nextCursor = nextBuffer.length, { keepSuggestionAnchor = false } = {}) => {
|
|
926
947
|
buffer = nextBuffer;
|
|
927
948
|
cursor = clamp(nextCursor, 0, buffer.length);
|
|
928
949
|
preferredColumn = null;
|
|
950
|
+
if (!keepSuggestionAnchor) {
|
|
951
|
+
suggestionAnchor = "";
|
|
952
|
+
suggestionIndex = 0;
|
|
953
|
+
}
|
|
929
954
|
redraw();
|
|
930
955
|
};
|
|
931
956
|
|
|
957
|
+
const cycleSuggestion = (delta) => {
|
|
958
|
+
const firstLine = buffer.split("\n")[0] || "";
|
|
959
|
+
if (!suggestionAnchor) suggestionAnchor = firstLine;
|
|
960
|
+
const suggestions = commandSuggestions(suggestionAnchor);
|
|
961
|
+
if (suggestions.length === 0) {
|
|
962
|
+
suggestionAnchor = "";
|
|
963
|
+
suggestionIndex = 0;
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
suggestionIndex = (suggestionIndex + delta + suggestions.length) % suggestions.length;
|
|
967
|
+
setBuffer(suggestions[suggestionIndex], suggestions[suggestionIndex].length, { keepSuggestionAnchor: true });
|
|
968
|
+
return true;
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
const suggestionModeActive = () => commandSuggestions(suggestionAnchor || buffer.split("\n")[0] || "").length > 1;
|
|
972
|
+
|
|
932
973
|
const moveVertical = (direction) => {
|
|
933
974
|
const layout = buildPromptLayout(buffer, cursor);
|
|
934
975
|
const location = cursorLocation(layout, cursor);
|
|
@@ -979,32 +1020,40 @@ function readTtyPrompt(options = {}) {
|
|
|
979
1020
|
if (key.name === "backspace") {
|
|
980
1021
|
({ buffer, cursor } = removeBefore(buffer, cursor));
|
|
981
1022
|
preferredColumn = null;
|
|
1023
|
+
suggestionAnchor = "";
|
|
1024
|
+
suggestionIndex = 0;
|
|
982
1025
|
redraw();
|
|
983
1026
|
return;
|
|
984
1027
|
}
|
|
985
1028
|
if (key.name === "delete") {
|
|
986
1029
|
({ buffer, cursor } = removeAt(buffer, cursor));
|
|
987
1030
|
preferredColumn = null;
|
|
1031
|
+
suggestionAnchor = "";
|
|
1032
|
+
suggestionIndex = 0;
|
|
988
1033
|
redraw();
|
|
989
1034
|
return;
|
|
990
1035
|
}
|
|
991
1036
|
if (key.name === "left") {
|
|
1037
|
+
if (suggestionModeActive() && cycleSuggestion(-1)) return;
|
|
992
1038
|
cursor = Math.max(cursor - 1, 0);
|
|
993
1039
|
preferredColumn = null;
|
|
994
1040
|
redraw();
|
|
995
1041
|
return;
|
|
996
1042
|
}
|
|
997
1043
|
if (key.name === "right") {
|
|
1044
|
+
if (suggestionModeActive() && cycleSuggestion(1)) return;
|
|
998
1045
|
cursor = Math.min(cursor + 1, buffer.length);
|
|
999
1046
|
preferredColumn = null;
|
|
1000
1047
|
redraw();
|
|
1001
1048
|
return;
|
|
1002
1049
|
}
|
|
1003
1050
|
if (key.name === "up") {
|
|
1051
|
+
if (suggestionModeActive() && cycleSuggestion(-1)) return;
|
|
1004
1052
|
moveVertical(-1);
|
|
1005
1053
|
return;
|
|
1006
1054
|
}
|
|
1007
1055
|
if (key.name === "down") {
|
|
1056
|
+
if (suggestionModeActive() && cycleSuggestion(1)) return;
|
|
1008
1057
|
moveVertical(1);
|
|
1009
1058
|
return;
|
|
1010
1059
|
}
|
|
@@ -1036,10 +1085,12 @@ function readTtyPrompt(options = {}) {
|
|
|
1036
1085
|
return;
|
|
1037
1086
|
}
|
|
1038
1087
|
if (key.name === "tab") {
|
|
1039
|
-
const suggestions = commandSuggestions(buffer.split("\n")[0] || "");
|
|
1040
|
-
if (suggestions.length
|
|
1041
|
-
buffer = suggestions[0];
|
|
1088
|
+
const suggestions = commandSuggestions(suggestionAnchor || buffer.split("\n")[0] || "");
|
|
1089
|
+
if (suggestions.length > 0) {
|
|
1090
|
+
buffer = suggestions[clamp(suggestionIndex, 0, suggestions.length - 1)];
|
|
1042
1091
|
cursor = buffer.length;
|
|
1092
|
+
suggestionAnchor = "";
|
|
1093
|
+
suggestionIndex = 0;
|
|
1043
1094
|
}
|
|
1044
1095
|
preferredColumn = null;
|
|
1045
1096
|
redraw();
|
|
@@ -1054,6 +1105,8 @@ function readTtyPrompt(options = {}) {
|
|
|
1054
1105
|
const text = str.replace(/\r/g, "");
|
|
1055
1106
|
({ buffer, cursor } = insertAt(buffer, cursor, text));
|
|
1056
1107
|
preferredColumn = null;
|
|
1108
|
+
suggestionAnchor = "";
|
|
1109
|
+
suggestionIndex = 0;
|
|
1057
1110
|
redraw();
|
|
1058
1111
|
}
|
|
1059
1112
|
};
|
|
@@ -1601,6 +1654,193 @@ function printModelRoles(state) {
|
|
|
1601
1654
|
);
|
|
1602
1655
|
}
|
|
1603
1656
|
|
|
1657
|
+
function modelRoleChoices(role = "main") {
|
|
1658
|
+
const common = [
|
|
1659
|
+
{
|
|
1660
|
+
provider: "deepseek",
|
|
1661
|
+
model: "deepseek-v4-flash",
|
|
1662
|
+
label: "DeepSeek V4 Flash",
|
|
1663
|
+
description: "fast planner, short shell/browser/code work",
|
|
1664
|
+
route: true,
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
provider: "deepseek",
|
|
1668
|
+
model: "deepseek-v4-pro",
|
|
1669
|
+
label: "DeepSeek V4 Pro",
|
|
1670
|
+
description: "complex coding, debugging, writing, and long tasks",
|
|
1671
|
+
main: true,
|
|
1672
|
+
},
|
|
1673
|
+
{
|
|
1674
|
+
provider: "venice",
|
|
1675
|
+
model: "venice-uncensored-1-2",
|
|
1676
|
+
label: "Venice Uncensored 1.2",
|
|
1677
|
+
description: "Venice text route; use /auth venice if missing",
|
|
1678
|
+
route: true,
|
|
1679
|
+
main: true,
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
provider: "openai",
|
|
1683
|
+
model: "gpt-5.5",
|
|
1684
|
+
label: "OpenAI GPT-5.5",
|
|
1685
|
+
description: "frontier spare/manual model",
|
|
1686
|
+
main: true,
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
provider: "openai",
|
|
1690
|
+
model: "gpt-5.4",
|
|
1691
|
+
label: "OpenAI GPT-5.4",
|
|
1692
|
+
description: "strong everyday coding spare",
|
|
1693
|
+
main: true,
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
provider: "openai",
|
|
1697
|
+
model: "gpt-5.4-mini",
|
|
1698
|
+
label: "OpenAI GPT-5.4 Mini",
|
|
1699
|
+
description: "small fast spare route",
|
|
1700
|
+
route: true,
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
provider: "qwen",
|
|
1704
|
+
model: "qwen-plus",
|
|
1705
|
+
label: "Qwen Plus",
|
|
1706
|
+
description: "general Qwen route",
|
|
1707
|
+
route: true,
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
provider: "qwen",
|
|
1711
|
+
model: "qwen-max",
|
|
1712
|
+
label: "Qwen Max",
|
|
1713
|
+
description: "larger Qwen route",
|
|
1714
|
+
main: true,
|
|
1715
|
+
},
|
|
1716
|
+
{
|
|
1717
|
+
provider: "mock",
|
|
1718
|
+
model: "mock-agent",
|
|
1719
|
+
label: "Mock Agent",
|
|
1720
|
+
description: "local deterministic test route",
|
|
1721
|
+
route: true,
|
|
1722
|
+
main: true,
|
|
1723
|
+
},
|
|
1724
|
+
];
|
|
1725
|
+
const filtered = common.filter((item) => (role === "route" ? item.route : item.main));
|
|
1726
|
+
return role === "route"
|
|
1727
|
+
? filtered.sort((a, b) => Number(b.model === "deepseek-v4-flash") - Number(a.model === "deepseek-v4-flash"))
|
|
1728
|
+
: filtered.sort((a, b) => Number(b.model === "deepseek-v4-pro") - Number(a.model === "deepseek-v4-pro"));
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
function modelChoiceLine(option) {
|
|
1732
|
+
return `${option.label} ${option.provider}/${option.model} ${option.description}`;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
function clearSelector(lineCount) {
|
|
1736
|
+
for (let index = 0; index < lineCount; index += 1) {
|
|
1737
|
+
output.write("\x1b[1A\r\x1b[2K");
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
function renderSelector({ title, subtitle, options, selectedIndex, lineCount = 0 }) {
|
|
1742
|
+
if (lineCount > 0) clearSelector(lineCount);
|
|
1743
|
+
const width = Math.min(Math.max(terminalWidth() - 2, 60), 110);
|
|
1744
|
+
const bodyWidth = width - 4;
|
|
1745
|
+
const rows = [
|
|
1746
|
+
`╭${"─".repeat(width - 2)}╮`,
|
|
1747
|
+
`│ ${padVisible(title, bodyWidth)} │`,
|
|
1748
|
+
`│ ${padVisible(subtitle, bodyWidth)} │`,
|
|
1749
|
+
`├${"─".repeat(width - 2)}┤`,
|
|
1750
|
+
...options.map((option, index) => {
|
|
1751
|
+
const marker = index === selectedIndex ? ">" : " ";
|
|
1752
|
+
const rendered = `${marker} ${compactLine(modelChoiceLine(option), bodyWidth - 2)}`;
|
|
1753
|
+
return `│ ${padVisible(index === selectedIndex ? color(rendered, ansi.userBg, ansi.bold) : rendered, bodyWidth)} │`;
|
|
1754
|
+
}),
|
|
1755
|
+
`╰${"─".repeat(width - 2)}╯`,
|
|
1756
|
+
];
|
|
1757
|
+
output.write(`${rows.join("\n")}\n`);
|
|
1758
|
+
return rows.length;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
|
|
1762
|
+
if (!input.isTTY || !output.isTTY || typeof input.setRawMode !== "function") return Promise.resolve(null);
|
|
1763
|
+
return new Promise((resolve) => {
|
|
1764
|
+
emitKeypressEvents(input);
|
|
1765
|
+
const wasRaw = Boolean(input.isRaw);
|
|
1766
|
+
let selectedIndex = clamp(initialIndex, 0, Math.max(options.length - 1, 0));
|
|
1767
|
+
let lineCount = 0;
|
|
1768
|
+
const cleanup = () => {
|
|
1769
|
+
input.off("keypress", handler);
|
|
1770
|
+
if (typeof input.setRawMode === "function") input.setRawMode(wasRaw);
|
|
1771
|
+
input.pause();
|
|
1772
|
+
output.write(ansi.cursorShow);
|
|
1773
|
+
};
|
|
1774
|
+
const redraw = () => {
|
|
1775
|
+
lineCount = renderSelector({ title, subtitle, options, selectedIndex, lineCount });
|
|
1776
|
+
};
|
|
1777
|
+
const finish = (value) => {
|
|
1778
|
+
if (lineCount > 0) clearSelector(lineCount);
|
|
1779
|
+
cleanup();
|
|
1780
|
+
resolve(value);
|
|
1781
|
+
};
|
|
1782
|
+
const handler = (_str = "", key = {}) => {
|
|
1783
|
+
if (key.ctrl && key.name === "c") {
|
|
1784
|
+
finish(null);
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
if (key.name === "escape") {
|
|
1788
|
+
finish(null);
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
if (key.name === "up" || key.name === "left") {
|
|
1792
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
1793
|
+
redraw();
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
if (key.name === "down" || key.name === "right" || key.name === "tab") {
|
|
1797
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
1798
|
+
redraw();
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
if (key.name === "return" || key.name === "enter" || key.sequence === "\r") {
|
|
1802
|
+
finish(options[selectedIndex]);
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
input.resume();
|
|
1806
|
+
input.setRawMode(true);
|
|
1807
|
+
input.on("keypress", handler);
|
|
1808
|
+
output.write(ansi.cursorHide);
|
|
1809
|
+
redraw();
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
async function pickModelRole(role, state) {
|
|
1814
|
+
const options = modelRoleChoices(role);
|
|
1815
|
+
const currentProvider = role === "route" ? state.routeProvider || "deepseek" : state.mainProvider || state.provider || "deepseek";
|
|
1816
|
+
const currentModel = role === "route" ? state.routeModel || "deepseek-v4-flash" : state.mainModel || "deepseek-v4-pro";
|
|
1817
|
+
const initialIndex = Math.max(
|
|
1818
|
+
options.findIndex((item) => item.provider === currentProvider && item.model === currentModel),
|
|
1819
|
+
0
|
|
1820
|
+
);
|
|
1821
|
+
const selected = await selectModelChoice({
|
|
1822
|
+
title: role === "route" ? "Select route model" : "Select main model",
|
|
1823
|
+
subtitle: "Up/Down/Left/Right selects, Enter confirms, Esc cancels.",
|
|
1824
|
+
options,
|
|
1825
|
+
initialIndex,
|
|
1826
|
+
});
|
|
1827
|
+
if (!selected) return false;
|
|
1828
|
+
|
|
1829
|
+
state.routingMode = "smart";
|
|
1830
|
+
state.provider = "deepseek";
|
|
1831
|
+
state.model = "";
|
|
1832
|
+
if (role === "route") {
|
|
1833
|
+
state.routeProvider = selected.provider;
|
|
1834
|
+
state.routeModel = selected.model;
|
|
1835
|
+
printSystemLine(`route=${state.routeProvider}/${state.routeModel}`);
|
|
1836
|
+
} else {
|
|
1837
|
+
state.mainProvider = selected.provider;
|
|
1838
|
+
state.mainModel = selected.model;
|
|
1839
|
+
printSystemLine(`main=${state.mainProvider}/${state.mainModel}`);
|
|
1840
|
+
}
|
|
1841
|
+
return true;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1604
1844
|
async function maybeOnboardDeepSeekKey(state) {
|
|
1605
1845
|
if (!shouldPromptForDeepSeek(state, process.cwd())) return;
|
|
1606
1846
|
|
|
@@ -1843,6 +2083,11 @@ async function handleCommand(line, state, packageDir) {
|
|
|
1843
2083
|
}
|
|
1844
2084
|
if (command === "route") {
|
|
1845
2085
|
if (!value) {
|
|
2086
|
+
if (input.isTTY && output.isTTY && typeof input.setRawMode === "function") {
|
|
2087
|
+
const changed = await pickModelRole("route", state);
|
|
2088
|
+
if (!changed) printSystemLine(`route=${state.routeProvider || "deepseek"}/${state.routeModel || "deepseek-v4-flash"}`);
|
|
2089
|
+
return true;
|
|
2090
|
+
}
|
|
1846
2091
|
printAgentMessage(
|
|
1847
2092
|
`Route model: ${state.routeProvider || "deepseek"}/${state.routeModel || "deepseek-v4-flash"}\nUse /route deepseek/deepseek-v4-flash or /route fast|smart|complex.`
|
|
1848
2093
|
);
|
|
@@ -1860,7 +2105,18 @@ async function handleCommand(line, state, packageDir) {
|
|
|
1860
2105
|
return true;
|
|
1861
2106
|
}
|
|
1862
2107
|
if (command === "main" || command === "model") {
|
|
1863
|
-
if (!value
|
|
2108
|
+
if (!value) {
|
|
2109
|
+
if (input.isTTY && output.isTTY && typeof input.setRawMode === "function") {
|
|
2110
|
+
const changed = await pickModelRole("main", state);
|
|
2111
|
+
if (!changed) printSystemLine(`main=${state.mainProvider || state.provider || "deepseek"}/${state.mainModel || state.model || "deepseek-v4-pro"}`);
|
|
2112
|
+
return true;
|
|
2113
|
+
}
|
|
2114
|
+
printAgentMessage(
|
|
2115
|
+
`Main model: ${state.mainProvider || state.provider || "deepseek"}/${state.mainModel || state.model || "deepseek-v4-pro"}\nUse /model deepseek/deepseek-v4-pro or /model auto.`
|
|
2116
|
+
);
|
|
2117
|
+
return true;
|
|
2118
|
+
}
|
|
2119
|
+
if (value === "auto") {
|
|
1864
2120
|
if (command === "main") {
|
|
1865
2121
|
state.mainProvider = "";
|
|
1866
2122
|
state.mainModel = "";
|