@iola_adm/iola-cli 0.1.36 → 0.1.38
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/cli.js +146 -87
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ import { createServer } from "node:http";
|
|
|
5
5
|
import { appendFile, copyFile, cp, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
import path from "node:path";
|
|
8
|
+
import { emitKeypressEvents } from "node:readline";
|
|
8
9
|
import readline from "node:readline/promises";
|
|
9
10
|
import { stdin as input, stdout as output } from "node:process";
|
|
10
11
|
import { DatabaseSync } from "node:sqlite";
|
|
@@ -264,15 +265,74 @@ const DATASETS = {
|
|
|
264
265
|
endpoint: "kindergartens",
|
|
265
266
|
},
|
|
266
267
|
};
|
|
268
|
+
const SLASH_COMMANDS = [
|
|
269
|
+
{ command: "/help", description: "список slash-команд" },
|
|
270
|
+
{ command: "/health", description: "проверка публичного API/MCP" },
|
|
271
|
+
{ command: "/doctor", description: "диагностика CLI" },
|
|
272
|
+
{ command: "/wizard", description: "мастер настройки" },
|
|
273
|
+
{ command: "/db status", description: "статус локальной SQLite-БД" },
|
|
274
|
+
{ command: "/sessions", description: "AI-сессии" },
|
|
275
|
+
{ command: "/resume SESSION_ID", description: "продолжить сессию" },
|
|
276
|
+
{ command: "/features list", description: "feature flags" },
|
|
277
|
+
{ command: "/gosuslugi status", description: "личное подключение Госуслуг" },
|
|
278
|
+
{ command: "/wiki", description: "ссылки на документацию" },
|
|
279
|
+
{ command: "/context list", description: "локальный контекст проекта" },
|
|
280
|
+
{ command: "/skills list", description: "skills" },
|
|
281
|
+
{ command: "/permissions", description: "разрешения" },
|
|
282
|
+
{ command: "/tools", description: "tools и toolsets" },
|
|
283
|
+
{ command: "/files status", description: "локальные файловые операции" },
|
|
284
|
+
{ command: "/archive doctor", description: "архиватор" },
|
|
285
|
+
{ command: "/changes list", description: "подготовленные изменения" },
|
|
286
|
+
{ command: "/index status", description: "индекс документов" },
|
|
287
|
+
{ command: "/reports list", description: "пакеты отчетов" },
|
|
288
|
+
{ command: "/plugins list", description: "plugins" },
|
|
289
|
+
{ command: "/workspace status", description: "workspace" },
|
|
290
|
+
{ command: "/tasks list", description: "задачи" },
|
|
291
|
+
{ command: "/artifacts list", description: "artifacts" },
|
|
292
|
+
{ command: "/trace last", description: "последние tools trace" },
|
|
293
|
+
{ command: "/policy use safe", description: "переключить policy" },
|
|
294
|
+
{ command: "/cron list", description: "cron-задачи" },
|
|
295
|
+
{ command: "/daemon status", description: "локальный daemon" },
|
|
296
|
+
{ command: "/rpc call status", description: "RPC status" },
|
|
297
|
+
{ command: "/memory show", description: "память агента" },
|
|
298
|
+
{ command: "/hooks list", description: "hooks" },
|
|
299
|
+
{ command: "/agents list", description: "agents" },
|
|
300
|
+
{ command: "/mcp status", description: "MCP" },
|
|
301
|
+
{ command: "/cache status", description: "cache" },
|
|
302
|
+
{ command: "/sync", description: "обновить локальные данные" },
|
|
303
|
+
{ command: "/diff", description: "изменения данных" },
|
|
304
|
+
{ command: "/card школа 29", description: "карточка объекта" },
|
|
305
|
+
{ command: "/quality", description: "качество данных" },
|
|
306
|
+
{ command: "/views", description: "saved views" },
|
|
307
|
+
{ command: "/config get", description: "конфигурация" },
|
|
308
|
+
{ command: "/layers", description: "слои данных" },
|
|
309
|
+
{ command: "/data schools --limit 10", description: "данные слоя" },
|
|
310
|
+
{ command: "/schools --limit 10", description: "школы" },
|
|
311
|
+
{ command: "/kindergartens --search 29", description: "детские сады" },
|
|
312
|
+
{ command: "/search лицей --limit 3", description: "поиск" },
|
|
313
|
+
{ command: "/mcp-info", description: "публичный MCP" },
|
|
314
|
+
{ command: "/profiles", description: "AI-профили" },
|
|
315
|
+
{ command: "/models openrouter --search qwen", description: "модели" },
|
|
316
|
+
{ command: "/ai doctor", description: "AI diagnostics" },
|
|
317
|
+
{ command: "/ai setup ollama", description: "настройка Ollama" },
|
|
318
|
+
{ command: "/use openai", description: "выбрать OpenAI" },
|
|
319
|
+
{ command: "/use ollama", description: "выбрать Ollama" },
|
|
320
|
+
{ command: "/key status", description: "API-ключи" },
|
|
321
|
+
{ command: "/history", description: "история текущей сессии" },
|
|
322
|
+
{ command: "/new", description: "новая agent-сессия" },
|
|
323
|
+
{ command: "/retry", description: "повторить последний вопрос" },
|
|
324
|
+
{ command: "/undo", description: "удалить последний обмен" },
|
|
325
|
+
{ command: "/compact", description: "сжать контекст" },
|
|
326
|
+
{ command: "/usage", description: "использование контекста" },
|
|
327
|
+
{ command: "/clear", description: "очистить историю agent-сессии" },
|
|
328
|
+
{ command: "/banner", description: "показать баннер" },
|
|
329
|
+
{ command: "/update", description: "проверить обновления" },
|
|
330
|
+
{ command: "/init", description: "проверить окружение" },
|
|
331
|
+
{ command: "/exit", description: "выйти" },
|
|
332
|
+
];
|
|
267
333
|
const BANNER = `\x1b[38;5;45m┌────────────────────────────────────────────────────────────────────────────┐
|
|
268
334
|
│ │
|
|
269
|
-
│\x1b[38;5;
|
|
270
|
-
│\x1b[38;5;51m / ___| | |_ _| \\ \\ / / ___|| _ \\| | | | |/ / / \\ | _ \\ \x1b[38;5;45m│
|
|
271
|
-
│\x1b[38;5;51m | | | | | | _____ \\ V /\\___ \\| | | | |_| | ' / / _ \\ | |_) | \x1b[38;5;45m│
|
|
272
|
-
│\x1b[38;5;51m | |___| |___ | | |_____| | | ___) | |_| | _ | . \\ / ___ \\| _ < \x1b[38;5;45m│
|
|
273
|
-
│\x1b[38;5;51m \\____|_____|___| |_| |____/|____/|_| |_|_|\\_\\/_/ \\_\\_| \\_\\ \x1b[38;5;45m│
|
|
274
|
-
│ │
|
|
275
|
-
│\x1b[38;5;213m CLI-ЙОШКАР-ОЛА \x1b[38;5;45m│
|
|
335
|
+
│\x1b[38;5;213m CLI-Йошкар-Ола \x1b[38;5;45m│
|
|
276
336
|
│ │
|
|
277
337
|
│\x1b[38;5;250m открытые данные • MCP • локальный AI \x1b[38;5;45m│
|
|
278
338
|
│ │
|
|
@@ -580,6 +640,7 @@ async function startAgent() {
|
|
|
580
640
|
rl.on("close", () => {
|
|
581
641
|
closed = true;
|
|
582
642
|
});
|
|
643
|
+
const detachSlashSuggestions = attachSlashSuggestions(rl);
|
|
583
644
|
safePrompt(rl);
|
|
584
645
|
|
|
585
646
|
for await (const rawLine of rl) {
|
|
@@ -605,6 +666,7 @@ async function startAgent() {
|
|
|
605
666
|
if (!closed) {
|
|
606
667
|
rl.close();
|
|
607
668
|
}
|
|
669
|
+
detachSlashSuggestions();
|
|
608
670
|
await runHooks("SessionEnd", { mode: "agent" });
|
|
609
671
|
}
|
|
610
672
|
|
|
@@ -616,9 +678,19 @@ async function handleAgentLine(line, state) {
|
|
|
616
678
|
return false;
|
|
617
679
|
}
|
|
618
680
|
|
|
681
|
+
if (line === "/") {
|
|
682
|
+
printSlashMenu("");
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
|
|
619
686
|
const [command, ...args] = splitCommandLine(line.slice(1));
|
|
620
687
|
state.lastCommand = { command, args };
|
|
621
688
|
|
|
689
|
+
if (!command) {
|
|
690
|
+
printSlashMenu("");
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
|
|
622
694
|
if (command === "exit" || command === "quit") {
|
|
623
695
|
return true;
|
|
624
696
|
}
|
|
@@ -968,8 +1040,13 @@ async function handleAgentLine(line, state) {
|
|
|
968
1040
|
}[command];
|
|
969
1041
|
|
|
970
1042
|
if (!mapped) {
|
|
971
|
-
|
|
972
|
-
|
|
1043
|
+
const matches = getSlashCommandMatches(command);
|
|
1044
|
+
if (matches.length > 0) {
|
|
1045
|
+
printSlashMenu(command);
|
|
1046
|
+
} else {
|
|
1047
|
+
console.log(`Неизвестная slash-команда: /${command}`);
|
|
1048
|
+
printSlashMenu(command);
|
|
1049
|
+
}
|
|
973
1050
|
return false;
|
|
974
1051
|
}
|
|
975
1052
|
|
|
@@ -979,83 +1056,33 @@ async function handleAgentLine(line, state) {
|
|
|
979
1056
|
}
|
|
980
1057
|
|
|
981
1058
|
function printAgentHelp() {
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
/daemon status
|
|
1010
|
-
/rpc call status
|
|
1011
|
-
/memory show
|
|
1012
|
-
/hooks list
|
|
1013
|
-
/agents list
|
|
1014
|
-
/mcp status
|
|
1015
|
-
/cache status
|
|
1016
|
-
/sync
|
|
1017
|
-
/diff
|
|
1018
|
-
/card школа 29
|
|
1019
|
-
/quality
|
|
1020
|
-
/views
|
|
1021
|
-
/config get
|
|
1022
|
-
/config set api.baseUrl URL
|
|
1023
|
-
/layers
|
|
1024
|
-
/data schools --limit 10
|
|
1025
|
-
/schools --limit 10
|
|
1026
|
-
/schools get --inn 1215067180
|
|
1027
|
-
/kindergartens --search 29
|
|
1028
|
-
/kindergartens get --inn 1215077421
|
|
1029
|
-
/search лицей --limit 3
|
|
1030
|
-
/mcp-info
|
|
1031
|
-
/context школа 29
|
|
1032
|
-
/profiles
|
|
1033
|
-
/profile use local
|
|
1034
|
-
/models openrouter --search qwen
|
|
1035
|
-
/ai doctor
|
|
1036
|
-
/ai setup ollama
|
|
1037
|
-
/use openai
|
|
1038
|
-
/use ollama
|
|
1039
|
-
/key status
|
|
1040
|
-
/key set openai
|
|
1041
|
-
/config
|
|
1042
|
-
/provider
|
|
1043
|
-
/model
|
|
1044
|
-
/history
|
|
1045
|
-
/history --limit 20
|
|
1046
|
-
/new
|
|
1047
|
-
/reset
|
|
1048
|
-
/retry
|
|
1049
|
-
/undo
|
|
1050
|
-
/compact
|
|
1051
|
-
/usage
|
|
1052
|
-
/clear
|
|
1053
|
-
/banner
|
|
1054
|
-
/update
|
|
1055
|
-
/init
|
|
1056
|
-
/exit
|
|
1057
|
-
|
|
1058
|
-
Обычный текст без slash-команды отправляется в настроенный AI-провайдер.`);
|
|
1059
|
+
printSlashMenu("");
|
|
1060
|
+
console.log("");
|
|
1061
|
+
console.log("Обычный текст без slash-команды отправляется в настроенный AI-провайдер.");
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function printSlashMenu(filter = "", options = {}) {
|
|
1065
|
+
const normalized = String(filter || "").replace(/^\//, "");
|
|
1066
|
+
const rows = getSlashCommandMatches(normalized)
|
|
1067
|
+
.slice(0, Number(options.limit || 30))
|
|
1068
|
+
.map((item) => ({ command: item.command, description: item.description }));
|
|
1069
|
+
if (rows.length === 0) {
|
|
1070
|
+
console.log(`Нет slash-команд по фильтру: ${filter}`);
|
|
1071
|
+
console.log("Введите / для списка команд.");
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
console.log(normalized ? `Slash-команды по фильтру "${filter}":` : "Slash-команды:");
|
|
1075
|
+
printTable(rows, [["command", "Команда"], ["description", "Описание"]]);
|
|
1076
|
+
if (!options.compact && SLASH_COMMANDS.length > rows.length && !normalized) {
|
|
1077
|
+
console.log(`Показано ${rows.length} из ${SLASH_COMMANDS.length}. Введите /текст для фильтра.`);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function getSlashCommandMatches(filter = "") {
|
|
1082
|
+
const normalized = String(filter || "").replace(/^\//, "").toLocaleLowerCase("ru-RU");
|
|
1083
|
+
return SLASH_COMMANDS.filter((item) => !normalized
|
|
1084
|
+
|| item.command.toLocaleLowerCase("ru-RU").includes(normalized)
|
|
1085
|
+
|| item.description.toLocaleLowerCase("ru-RU").includes(normalized));
|
|
1059
1086
|
}
|
|
1060
1087
|
|
|
1061
1088
|
function printAgentHistory(history) {
|
|
@@ -1096,12 +1123,44 @@ function safePrompt(rl, closed = false) {
|
|
|
1096
1123
|
}
|
|
1097
1124
|
|
|
1098
1125
|
try {
|
|
1126
|
+
writePromptBottomPadding();
|
|
1099
1127
|
rl.prompt();
|
|
1100
1128
|
} catch {
|
|
1101
1129
|
// The input stream can close while an async slash-command is still running.
|
|
1102
1130
|
}
|
|
1103
1131
|
}
|
|
1104
1132
|
|
|
1133
|
+
function attachSlashSuggestions(rl) {
|
|
1134
|
+
if (!input.isTTY) return () => {};
|
|
1135
|
+
emitKeypressEvents(input, rl);
|
|
1136
|
+
let lastFilter = null;
|
|
1137
|
+
const onKeypress = () => {
|
|
1138
|
+
setTimeout(() => {
|
|
1139
|
+
const line = rl.line || "";
|
|
1140
|
+
if (!line.startsWith("/")) {
|
|
1141
|
+
lastFilter = null;
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
const filter = line.slice(1);
|
|
1145
|
+
if (filter === lastFilter) return;
|
|
1146
|
+
lastFilter = filter;
|
|
1147
|
+
output.write("\n");
|
|
1148
|
+
printSlashMenu(filter, { compact: true, limit: 10 });
|
|
1149
|
+
writePromptBottomPadding();
|
|
1150
|
+
rl.prompt(true);
|
|
1151
|
+
}, 0);
|
|
1152
|
+
};
|
|
1153
|
+
input.on("keypress", onKeypress);
|
|
1154
|
+
return () => input.off("keypress", onKeypress);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
function writePromptBottomPadding() {
|
|
1158
|
+
if (!output.isTTY) return;
|
|
1159
|
+
const padding = Math.max(0, Math.min(5, Number(process.env.IOLA_PROMPT_BOTTOM_PADDING || 2)));
|
|
1160
|
+
if (padding === 0) return;
|
|
1161
|
+
output.write(`${"\n".repeat(padding)}\x1b[${padding}A`);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1105
1164
|
async function showBanner(options = {}) {
|
|
1106
1165
|
const version = getPackageVersion();
|
|
1107
1166
|
const latest = options.skipUpdate ? null : await getLatestNpmVersion("@iola_adm/iola-cli");
|
|
@@ -1116,7 +1175,7 @@ async function showBanner(options = {}) {
|
|
|
1116
1175
|
return;
|
|
1117
1176
|
}
|
|
1118
1177
|
|
|
1119
|
-
console.log(`CLI
|
|
1178
|
+
console.log(`CLI-Йошкар-Ола ${updateAvailable ? `v${version} -> v${latest}` : `v${version}`}`);
|
|
1120
1179
|
console.log("открытые данные • MCP • локальный AI");
|
|
1121
1180
|
if (updateAvailable) console.log("Обновить: npm install -g @iola_adm/iola-cli@latest");
|
|
1122
1181
|
}
|