@iola_adm/iola-cli 0.1.39 → 0.1.41

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 +35 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
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
@@ -674,7 +674,7 @@ async function startAgentReadline() {
674
674
  }
675
675
 
676
676
  async function startAgentRawInput() {
677
- const state = { history: [], buffer: "", selected: 0, slashOpen: false, running: false };
677
+ const state = { history: [], buffer: "", selected: 0, slashOpen: false, running: false, renderedInputLines: 0 };
678
678
  emitKeypressEvents(input);
679
679
  const wasRaw = input.isRaw;
680
680
  input.setRawMode(true);
@@ -728,11 +728,12 @@ async function startAgentRawInput() {
728
728
  const line = state.buffer.trim();
729
729
  state.buffer = "";
730
730
  state.slashOpen = false;
731
- clearAgentInputArea();
731
+ clearAgentInputArea(state);
732
732
  if (!line) {
733
733
  render();
734
734
  continue;
735
735
  }
736
+ output.write(`iola> ${line}\n`);
736
737
  try {
737
738
  const shouldExit = await handleAgentLine(line, state);
738
739
  if (shouldExit) break;
@@ -1179,29 +1180,49 @@ function currentSlashMatches(state) {
1179
1180
  }
1180
1181
 
1181
1182
  function renderAgentInput(state) {
1182
- clearAgentInputArea();
1183
+ clearAgentInputArea(state);
1183
1184
  const prompt = "iola> ";
1184
1185
  const lines = state.buffer.split("\n");
1185
- output.write(`${prompt}${lines[0] || ""}\n`);
1186
- for (const line of lines.slice(1)) output.write(` ${line}\n`);
1186
+ const inputLines = [`${prompt}${lines[0] || ""}`, ...lines.slice(1).map((line) => ` ${line}`)];
1187
+ const menuLines = [];
1187
1188
  if (state.slashOpen) {
1188
1189
  const matches = currentSlashMatches(state);
1189
1190
  if (matches.length === 0) {
1190
- output.write(" нет команд\n");
1191
+ menuLines.push(" нет команд");
1191
1192
  } else {
1192
1193
  for (let index = 0; index < matches.length; index += 1) {
1193
- const marker = index === state.selected ? ">" : " ";
1194
- output.write(`${marker} ${matches[index].command.padEnd(24)} ${matches[index].description}\n`);
1194
+ const selected = index === state.selected;
1195
+ const marker = selected ? ">" : " ";
1196
+ const row = `${marker} ${matches[index].command.padEnd(24)} ${matches[index].description}`;
1197
+ menuLines.push(selected ? colorSlashSelection(row) : ` ${row.slice(2)}`);
1195
1198
  }
1196
- output.write(" ↑/↓ выбрать • Enter вставить/выполнить • Esc закрыть\n");
1199
+ menuLines.push(" ↑/↓ выбрать • Enter вставить/выполнить • Esc закрыть");
1197
1200
  }
1198
1201
  }
1199
- writePromptBottomPadding();
1202
+
1203
+ const renderedLines = [...inputLines, ...menuLines];
1204
+ output.write(renderedLines.join("\n"));
1205
+ if (menuLines.length > 0 && output.isTTY) {
1206
+ output.write(`\x1b[${menuLines.length}A`);
1207
+ }
1208
+ if (output.isTTY) {
1209
+ const cursorColumn = visibleLength(inputLines[inputLines.length - 1]);
1210
+ output.write(`\x1b[${cursorColumn + 1}G`);
1211
+ }
1212
+ state.renderedInputLines = inputLines.length;
1200
1213
  }
1201
1214
 
1202
- function clearAgentInputArea() {
1215
+ function clearAgentInputArea(state = null) {
1203
1216
  if (!output.isTTY) return;
1204
- output.write("\x1b[2K\r");
1217
+ const inputLines = Math.max(1, Number(state?.renderedInputLines || 1));
1218
+ if (inputLines > 1) output.write(`\x1b[${inputLines - 1}A`);
1219
+ output.write("\r\x1b[0J");
1220
+ if (state) state.renderedInputLines = 0;
1221
+ }
1222
+
1223
+ function colorSlashSelection(row) {
1224
+ if (!output.isTTY || process.env.NO_COLOR === "1") return row;
1225
+ return `\x1b[38;5;213m${row}\x1b[0m`;
1205
1226
  }
1206
1227
 
1207
1228
  function readKeypress() {
@@ -1259,7 +1280,6 @@ function safePrompt(rl, closed = false) {
1259
1280
  }
1260
1281
 
1261
1282
  try {
1262
- writePromptBottomPadding();
1263
1283
  rl.prompt();
1264
1284
  } catch {
1265
1285
  // The input stream can close while an async slash-command is still running.
@@ -1282,7 +1302,6 @@ function attachSlashSuggestions(rl) {
1282
1302
  lastFilter = filter;
1283
1303
  output.write("\n");
1284
1304
  printSlashMenu(filter, { compact: true, limit: 10 });
1285
- writePromptBottomPadding();
1286
1305
  rl.prompt(true);
1287
1306
  }, 0);
1288
1307
  };
@@ -1290,13 +1309,6 @@ function attachSlashSuggestions(rl) {
1290
1309
  return () => input.off("keypress", onKeypress);
1291
1310
  }
1292
1311
 
1293
- function writePromptBottomPadding() {
1294
- if (!output.isTTY) return;
1295
- const padding = Math.max(0, Math.min(5, Number(process.env.IOLA_PROMPT_BOTTOM_PADDING || 2)));
1296
- if (padding === 0) return;
1297
- output.write(`${"\n".repeat(padding)}\x1b[${padding}A`);
1298
- }
1299
-
1300
1312
  async function showBanner(options = {}) {
1301
1313
  const version = getPackageVersion();
1302
1314
  const latest = options.skipUpdate ? null : await getLatestNpmVersion("@iola_adm/iola-cli");
@@ -1312,7 +1324,7 @@ async function showBanner(options = {}) {
1312
1324
  }
1313
1325
 
1314
1326
  console.log(`CLI-Йошкар-Ола ${updateAvailable ? `v${version} -> v${latest}` : `v${version}`}`);
1315
- console.log("открытые данные • MCP • локальный AI");
1327
+ console.log("Йошкар-Ола • MCP • локальный AI");
1316
1328
  if (updateAvailable) console.log("Обновить: npm install -g @iola_adm/iola-cli@latest");
1317
1329
  }
1318
1330
 
@@ -1333,7 +1345,7 @@ function renderBanner(versionLine, color = false) {
1333
1345
  line(),
1334
1346
  line("CLI-Йошкар-Ола", c.title),
1335
1347
  line(),
1336
- line("открытые данные • MCP • локальный AI", c.muted),
1348
+ line("Йошкар-Ола • MCP • локальный AI", c.muted),
1337
1349
  line(),
1338
1350
  line(versionLine, c.version),
1339
1351
  `${c.border}└${"─".repeat(BANNER_WIDTH)}┘${c.reset}`,