@iola_adm/iola-cli 0.1.35 → 0.1.37

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +146 -98
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
4
4
  "description": "CLI и AI-агент городского округа Йошкар-Ола.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/adm-iola/iola-cli#readme",
package/src/cli.js CHANGED
@@ -264,18 +264,84 @@ const DATASETS = {
264
264
  endpoint: "kindergartens",
265
265
  },
266
266
  };
267
+ const SLASH_COMMANDS = [
268
+ { command: "/help", description: "список slash-команд" },
269
+ { command: "/health", description: "проверка публичного API/MCP" },
270
+ { command: "/doctor", description: "диагностика CLI" },
271
+ { command: "/wizard", description: "мастер настройки" },
272
+ { command: "/db status", description: "статус локальной SQLite-БД" },
273
+ { command: "/sessions", description: "AI-сессии" },
274
+ { command: "/resume SESSION_ID", description: "продолжить сессию" },
275
+ { command: "/features list", description: "feature flags" },
276
+ { command: "/gosuslugi status", description: "личное подключение Госуслуг" },
277
+ { command: "/wiki", description: "ссылки на документацию" },
278
+ { command: "/context list", description: "локальный контекст проекта" },
279
+ { command: "/skills list", description: "skills" },
280
+ { command: "/permissions", description: "разрешения" },
281
+ { command: "/tools", description: "tools и toolsets" },
282
+ { command: "/files status", description: "локальные файловые операции" },
283
+ { command: "/archive doctor", description: "архиватор" },
284
+ { command: "/changes list", description: "подготовленные изменения" },
285
+ { command: "/index status", description: "индекс документов" },
286
+ { command: "/reports list", description: "пакеты отчетов" },
287
+ { command: "/plugins list", description: "plugins" },
288
+ { command: "/workspace status", description: "workspace" },
289
+ { command: "/tasks list", description: "задачи" },
290
+ { command: "/artifacts list", description: "artifacts" },
291
+ { command: "/trace last", description: "последние tools trace" },
292
+ { command: "/policy use safe", description: "переключить policy" },
293
+ { command: "/cron list", description: "cron-задачи" },
294
+ { command: "/daemon status", description: "локальный daemon" },
295
+ { command: "/rpc call status", description: "RPC status" },
296
+ { command: "/memory show", description: "память агента" },
297
+ { command: "/hooks list", description: "hooks" },
298
+ { command: "/agents list", description: "agents" },
299
+ { command: "/mcp status", description: "MCP" },
300
+ { command: "/cache status", description: "cache" },
301
+ { command: "/sync", description: "обновить локальные данные" },
302
+ { command: "/diff", description: "изменения данных" },
303
+ { command: "/card школа 29", description: "карточка объекта" },
304
+ { command: "/quality", description: "качество данных" },
305
+ { command: "/views", description: "saved views" },
306
+ { command: "/config get", description: "конфигурация" },
307
+ { command: "/layers", description: "слои данных" },
308
+ { command: "/data schools --limit 10", description: "данные слоя" },
309
+ { command: "/schools --limit 10", description: "школы" },
310
+ { command: "/kindergartens --search 29", description: "детские сады" },
311
+ { command: "/search лицей --limit 3", description: "поиск" },
312
+ { command: "/mcp-info", description: "публичный MCP" },
313
+ { command: "/profiles", description: "AI-профили" },
314
+ { command: "/models openrouter --search qwen", description: "модели" },
315
+ { command: "/ai doctor", description: "AI diagnostics" },
316
+ { command: "/ai setup ollama", description: "настройка Ollama" },
317
+ { command: "/use openai", description: "выбрать OpenAI" },
318
+ { command: "/use ollama", description: "выбрать Ollama" },
319
+ { command: "/key status", description: "API-ключи" },
320
+ { command: "/history", description: "история текущей сессии" },
321
+ { command: "/new", description: "новая agent-сессия" },
322
+ { command: "/retry", description: "повторить последний вопрос" },
323
+ { command: "/undo", description: "удалить последний обмен" },
324
+ { command: "/compact", description: "сжать контекст" },
325
+ { command: "/usage", description: "использование контекста" },
326
+ { command: "/clear", description: "очистить историю agent-сессии" },
327
+ { command: "/banner", description: "показать баннер" },
328
+ { command: "/update", description: "проверить обновления" },
329
+ { command: "/init", description: "проверить окружение" },
330
+ { command: "/exit", description: "выйти" },
331
+ ];
267
332
  const BANNER = `\x1b[38;5;45m┌────────────────────────────────────────────────────────────────────────────┐
268
- │\x1b[38;5;51m ____ _ ___ ____ ____ ___ _____ _ _______ \x1b[38;5;45m│
269
- │\x1b[38;5;51m / ___| | |_ _| | _ \\| _ \\ / _ \\| ____| |/ /_ _| \x1b[38;5;45m│
270
- │\x1b[38;5;51m | | | | | |_____| |_) | |_) | | | | _| | ' / | | \x1b[38;5;45m│
271
- │\x1b[38;5;51m | |___| |___ | |_____| __/| _ <| |_| | |___| . \\ | | \x1b[38;5;45m│
272
- │\x1b[38;5;51m \\____|_____|___| |_| |_| \\_\\\\___/|_____|_|\\_\\ |_| \x1b[38;5;45m│
273
333
  │ │
274
- │\x1b[38;5;213m C L I - Й О Ш К А Р - О Л А \x1b[38;5;45m│
334
+ │\x1b[38;5;51m ____ _ ___ __ ______ ____ _ _ _ __ _ ____ \x1b[38;5;45m│
335
+ │\x1b[38;5;51m / ___| | |_ _| \\ \\ / / ___|| _ \\| | | | |/ / / \\ | _ \\ \x1b[38;5;45m│
336
+ │\x1b[38;5;51m | | | | | | _____ \\ V /\\___ \\| | | | |_| | ' / / _ \\ | |_) | \x1b[38;5;45m│
337
+ │\x1b[38;5;51m | |___| |___ | | |_____| | | ___) | |_| | _ | . \\ / ___ \\| _ < \x1b[38;5;45m│
338
+ │\x1b[38;5;51m \\____|_____|___| |_| |____/|____/|_| |_|_|\\_\\/_/ \\_\\_| \\_\\ \x1b[38;5;45m│
339
+ │ │
340
+ │\x1b[38;5;213m CLI-ЙОШКАР-ОЛА \x1b[38;5;45m│
275
341
  │ │
276
342
  │\x1b[38;5;250m открытые данные • MCP • локальный AI \x1b[38;5;45m│
277
343
  │ │
278
- │\x1b[38;5;82m > iola help \x1b[38;5;45m│
344
+ │\x1b[38;5;82m VERSION_LINE \x1b[38;5;45m│
279
345
  └────────────────────────────────────────────────────────────────────────────┘\x1b[0m`;
280
346
 
281
347
  const COMMANDS = new Map([
@@ -405,7 +471,7 @@ export async function main(argv) {
405
471
  }
406
472
 
407
473
  async function showHelp() {
408
- showBanner();
474
+ await showBanner();
409
475
  console.log(`iola - CLI и AI-агент городского округа "Город Йошкар-Ола"
410
476
 
411
477
  Запуск:
@@ -434,7 +500,7 @@ Requirements:
434
500
  }
435
501
 
436
502
  async function showCommands() {
437
- showBanner();
503
+ await showBanner();
438
504
  console.log(`iola - CLI для открытых данных городского округа "Город Йошкар-Ола"
439
505
 
440
506
  Usage:
@@ -554,7 +620,7 @@ async function runDefaultCli() {
554
620
 
555
621
  initDatabase();
556
622
  if (!isFirstRunCompleted()) {
557
- showBanner();
623
+ await showBanner();
558
624
  console.log("Первый запуск iola-cli. Сейчас откроется мастер настройки.");
559
625
  console.log("После мастера запустится интерактивный агент.");
560
626
  console.log("");
@@ -567,7 +633,7 @@ async function runDefaultCli() {
567
633
  }
568
634
 
569
635
  async function startAgent() {
570
- showBanner();
636
+ await showBanner();
571
637
  console.log("Интерактивный режим. Введите /help для списка команд, /exit для выхода.");
572
638
  await runHooks("SessionStart", { mode: "agent" });
573
639
 
@@ -615,9 +681,19 @@ async function handleAgentLine(line, state) {
615
681
  return false;
616
682
  }
617
683
 
684
+ if (line === "/") {
685
+ printSlashMenu("");
686
+ return false;
687
+ }
688
+
618
689
  const [command, ...args] = splitCommandLine(line.slice(1));
619
690
  state.lastCommand = { command, args };
620
691
 
692
+ if (!command) {
693
+ printSlashMenu("");
694
+ return false;
695
+ }
696
+
621
697
  if (command === "exit" || command === "quit") {
622
698
  return true;
623
699
  }
@@ -897,7 +973,7 @@ async function handleAgentLine(line, state) {
897
973
  }
898
974
 
899
975
  if (command === "banner") {
900
- showBanner();
976
+ await showBanner();
901
977
  return false;
902
978
  }
903
979
 
@@ -967,8 +1043,13 @@ async function handleAgentLine(line, state) {
967
1043
  }[command];
968
1044
 
969
1045
  if (!mapped) {
970
- console.log(`Неизвестная slash-команда: /${command}`);
971
- printAgentHelp();
1046
+ const matches = getSlashCommandMatches(command);
1047
+ if (matches.length > 0) {
1048
+ printSlashMenu(command);
1049
+ } else {
1050
+ console.log(`Неизвестная slash-команда: /${command}`);
1051
+ printSlashMenu(command);
1052
+ }
972
1053
  return false;
973
1054
  }
974
1055
 
@@ -978,83 +1059,33 @@ async function handleAgentLine(line, state) {
978
1059
  }
979
1060
 
980
1061
  function printAgentHelp() {
981
- console.log(`Slash-команды:
982
- /help
983
- /health
984
- /doctor
985
- /wizard
986
- /db status
987
- /sessions
988
- /resume SESSION_ID
989
- /features list
990
- /gosuslugi status
991
- /wiki
992
- /context list
993
- /skills list
994
- /permissions
995
- /tools
996
- /files status
997
- /archive doctor
998
- /changes list
999
- /index status
1000
- /reports list
1001
- /plugins list
1002
- /workspace status
1003
- /tasks list
1004
- /artifacts list
1005
- /trace last
1006
- /policy use safe
1007
- /cron list
1008
- /daemon status
1009
- /rpc call status
1010
- /memory show
1011
- /hooks list
1012
- /agents list
1013
- /mcp status
1014
- /cache status
1015
- /sync
1016
- /diff
1017
- /card школа 29
1018
- /quality
1019
- /views
1020
- /config get
1021
- /config set api.baseUrl URL
1022
- /layers
1023
- /data schools --limit 10
1024
- /schools --limit 10
1025
- /schools get --inn 1215067180
1026
- /kindergartens --search 29
1027
- /kindergartens get --inn 1215077421
1028
- /search лицей --limit 3
1029
- /mcp-info
1030
- /context школа 29
1031
- /profiles
1032
- /profile use local
1033
- /models openrouter --search qwen
1034
- /ai doctor
1035
- /ai setup ollama
1036
- /use openai
1037
- /use ollama
1038
- /key status
1039
- /key set openai
1040
- /config
1041
- /provider
1042
- /model
1043
- /history
1044
- /history --limit 20
1045
- /new
1046
- /reset
1047
- /retry
1048
- /undo
1049
- /compact
1050
- /usage
1051
- /clear
1052
- /banner
1053
- /update
1054
- /init
1055
- /exit
1056
-
1057
- Обычный текст без slash-команды отправляется в настроенный AI-провайдер.`);
1062
+ printSlashMenu("");
1063
+ console.log("");
1064
+ console.log("Обычный текст без slash-команды отправляется в настроенный AI-провайдер.");
1065
+ }
1066
+
1067
+ function printSlashMenu(filter = "") {
1068
+ const normalized = String(filter || "").replace(/^\//, "");
1069
+ const rows = getSlashCommandMatches(normalized)
1070
+ .slice(0, 30)
1071
+ .map((item) => ({ command: item.command, description: item.description }));
1072
+ if (rows.length === 0) {
1073
+ console.log(`Нет slash-команд по фильтру: ${filter}`);
1074
+ console.log("Введите / для списка команд.");
1075
+ return;
1076
+ }
1077
+ console.log(normalized ? `Slash-команды по фильтру "${filter}":` : "Slash-команды:");
1078
+ printTable(rows, [["command", "Команда"], ["description", "Описание"]]);
1079
+ if (SLASH_COMMANDS.length > rows.length && !normalized) {
1080
+ console.log(`Показано ${rows.length} из ${SLASH_COMMANDS.length}. Введите /текст для фильтра.`);
1081
+ }
1082
+ }
1083
+
1084
+ function getSlashCommandMatches(filter = "") {
1085
+ const normalized = String(filter || "").replace(/^\//, "").toLocaleLowerCase("ru-RU");
1086
+ return SLASH_COMMANDS.filter((item) => !normalized
1087
+ || item.command.toLocaleLowerCase("ru-RU").includes(normalized)
1088
+ || item.description.toLocaleLowerCase("ru-RU").includes(normalized));
1058
1089
  }
1059
1090
 
1060
1091
  function printAgentHistory(history) {
@@ -1101,15 +1132,32 @@ function safePrompt(rl, closed = false) {
1101
1132
  }
1102
1133
  }
1103
1134
 
1104
- function showBanner() {
1135
+ async function showBanner(options = {}) {
1105
1136
  const version = getPackageVersion();
1137
+ const latest = options.skipUpdate ? null : await getLatestNpmVersion("@iola_adm/iola-cli");
1138
+ const updateAvailable = latest && compareVersions(latest, version) > 0;
1139
+ const versionLine = updateAvailable ? `v${version} -> v${latest} • npm install -g @iola_adm/iola-cli@latest` : `v${version} • iola help`;
1106
1140
  if (process.stdout.isTTY && process.env.NO_COLOR !== "1") {
1107
- console.log(BANNER.replace("> iola help", `v${version} • iola help`));
1141
+ console.log(BANNER.replace("VERSION_LINE", padBannerLine(versionLine)));
1142
+ if (updateAvailable) {
1143
+ console.log(`Доступно обновление: v${version} -> v${latest}`);
1144
+ console.log("Обновить: npm install -g @iola_adm/iola-cli@latest");
1145
+ }
1108
1146
  return;
1109
1147
  }
1110
1148
 
1111
- console.log(`CLI-ЙОШКАР-ОЛА v${version}`);
1149
+ console.log(`CLI-ЙОШКАР-ОЛА ${updateAvailable ? `v${version} -> v${latest}` : `v${version}`}`);
1112
1150
  console.log("открытые данные • MCP • локальный AI");
1151
+ if (updateAvailable) console.log("Обновить: npm install -g @iola_adm/iola-cli@latest");
1152
+ }
1153
+
1154
+ function padBannerLine(value) {
1155
+ const text = String(value).slice(0, 62);
1156
+ return `${text}${" ".repeat(Math.max(0, 62 - bannerVisibleLength(text)))}`;
1157
+ }
1158
+
1159
+ function bannerVisibleLength(value) {
1160
+ return [...String(value)].length;
1113
1161
  }
1114
1162
 
1115
1163
  function getPackageVersion() {
@@ -1392,7 +1440,7 @@ async function initCli(args = []) {
1392
1440
  const options = parseOptions(args);
1393
1441
  const nodeStatus = getNodeRequirementStatus();
1394
1442
 
1395
- showBanner();
1443
+ await showBanner();
1396
1444
  console.log("Проверка окружения");
1397
1445
  initDatabase();
1398
1446
  const dbStatus = getDbStatus();
@@ -1439,7 +1487,7 @@ async function handleAi(args) {
1439
1487
  const [subcommand = "help", ...rest] = args;
1440
1488
 
1441
1489
  if (subcommand === "help") {
1442
- showBanner();
1490
+ await showBanner();
1443
1491
  console.log(`AI-команды:
1444
1492
  iola ai ask TEXT [--provider ollama|openai|openrouter] [--model MODEL]
1445
1493
  iola ai context TEXT [--json]
@@ -3711,7 +3759,7 @@ async function aiSetup(args) {
3711
3759
  const [provider] = args;
3712
3760
 
3713
3761
  if (!provider) {
3714
- showBanner();
3762
+ await showBanner();
3715
3763
  const selected = await chooseAiProvider();
3716
3764
  await aiSetup([selected]);
3717
3765
  return;
@@ -6338,7 +6386,7 @@ async function setupClient(args) {
6338
6386
 
6339
6387
  async function onboard(args = []) {
6340
6388
  const options = parseOptions(args);
6341
- showBanner();
6389
+ await showBanner();
6342
6390
  console.log("Мастер настройки iola-cli.");
6343
6391
  console.log("Повторный запуск обновляет только выбранные разделы и не сбрасывает остальные настройки.");
6344
6392
  console.log("");