@iola_adm/iola-cli 0.1.37 → 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 +40 -11
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";
|
|
@@ -331,13 +332,7 @@ const SLASH_COMMANDS = [
|
|
|
331
332
|
];
|
|
332
333
|
const BANNER = `\x1b[38;5;45m┌────────────────────────────────────────────────────────────────────────────┐
|
|
333
334
|
│ │
|
|
334
|
-
│\x1b[38;5;
|
|
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│
|
|
335
|
+
│\x1b[38;5;213m CLI-Йошкар-Ола \x1b[38;5;45m│
|
|
341
336
|
│ │
|
|
342
337
|
│\x1b[38;5;250m открытые данные • MCP • локальный AI \x1b[38;5;45m│
|
|
343
338
|
│ │
|
|
@@ -645,6 +640,7 @@ async function startAgent() {
|
|
|
645
640
|
rl.on("close", () => {
|
|
646
641
|
closed = true;
|
|
647
642
|
});
|
|
643
|
+
const detachSlashSuggestions = attachSlashSuggestions(rl);
|
|
648
644
|
safePrompt(rl);
|
|
649
645
|
|
|
650
646
|
for await (const rawLine of rl) {
|
|
@@ -670,6 +666,7 @@ async function startAgent() {
|
|
|
670
666
|
if (!closed) {
|
|
671
667
|
rl.close();
|
|
672
668
|
}
|
|
669
|
+
detachSlashSuggestions();
|
|
673
670
|
await runHooks("SessionEnd", { mode: "agent" });
|
|
674
671
|
}
|
|
675
672
|
|
|
@@ -1064,10 +1061,10 @@ function printAgentHelp() {
|
|
|
1064
1061
|
console.log("Обычный текст без slash-команды отправляется в настроенный AI-провайдер.");
|
|
1065
1062
|
}
|
|
1066
1063
|
|
|
1067
|
-
function printSlashMenu(filter = "") {
|
|
1064
|
+
function printSlashMenu(filter = "", options = {}) {
|
|
1068
1065
|
const normalized = String(filter || "").replace(/^\//, "");
|
|
1069
1066
|
const rows = getSlashCommandMatches(normalized)
|
|
1070
|
-
.slice(0, 30)
|
|
1067
|
+
.slice(0, Number(options.limit || 30))
|
|
1071
1068
|
.map((item) => ({ command: item.command, description: item.description }));
|
|
1072
1069
|
if (rows.length === 0) {
|
|
1073
1070
|
console.log(`Нет slash-команд по фильтру: ${filter}`);
|
|
@@ -1076,7 +1073,7 @@ function printSlashMenu(filter = "") {
|
|
|
1076
1073
|
}
|
|
1077
1074
|
console.log(normalized ? `Slash-команды по фильтру "${filter}":` : "Slash-команды:");
|
|
1078
1075
|
printTable(rows, [["command", "Команда"], ["description", "Описание"]]);
|
|
1079
|
-
if (SLASH_COMMANDS.length > rows.length && !normalized) {
|
|
1076
|
+
if (!options.compact && SLASH_COMMANDS.length > rows.length && !normalized) {
|
|
1080
1077
|
console.log(`Показано ${rows.length} из ${SLASH_COMMANDS.length}. Введите /текст для фильтра.`);
|
|
1081
1078
|
}
|
|
1082
1079
|
}
|
|
@@ -1126,12 +1123,44 @@ function safePrompt(rl, closed = false) {
|
|
|
1126
1123
|
}
|
|
1127
1124
|
|
|
1128
1125
|
try {
|
|
1126
|
+
writePromptBottomPadding();
|
|
1129
1127
|
rl.prompt();
|
|
1130
1128
|
} catch {
|
|
1131
1129
|
// The input stream can close while an async slash-command is still running.
|
|
1132
1130
|
}
|
|
1133
1131
|
}
|
|
1134
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
|
+
|
|
1135
1164
|
async function showBanner(options = {}) {
|
|
1136
1165
|
const version = getPackageVersion();
|
|
1137
1166
|
const latest = options.skipUpdate ? null : await getLatestNpmVersion("@iola_adm/iola-cli");
|
|
@@ -1146,7 +1175,7 @@ async function showBanner(options = {}) {
|
|
|
1146
1175
|
return;
|
|
1147
1176
|
}
|
|
1148
1177
|
|
|
1149
|
-
console.log(`CLI
|
|
1178
|
+
console.log(`CLI-Йошкар-Ола ${updateAvailable ? `v${version} -> v${latest}` : `v${version}`}`);
|
|
1150
1179
|
console.log("открытые данные • MCP • локальный AI");
|
|
1151
1180
|
if (updateAvailable) console.log("Обновить: npm install -g @iola_adm/iola-cli@latest");
|
|
1152
1181
|
}
|