@iola_adm/iola-cli 0.1.48 → 0.1.50

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 +53 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
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
@@ -647,7 +647,13 @@ async function ensureAgentAiReady() {
647
647
  if (readiness.anyReady) {
648
648
  const fallback = getFallbackAiProfile(readiness);
649
649
  if (fallback) {
650
- console.log(`Активный AI-профиль ${readiness.activeProfile} (${readiness.activeProvider}) недоступен. Для текстовых запросов будет использован доступный профиль ${fallback.name} (${fallback.provider}).`);
650
+ const shouldSwitch = await confirm(`Активный AI-профиль ${readiness.activeProfile} (${readiness.activeProvider}) недоступен, найден ${fallback.name} (${fallback.provider}). Переключить активный профиль на ${fallback.name}? [Y/n] `);
651
+ if (shouldSwitch) {
652
+ await setActiveAiProfile(fallback.name, fallback);
653
+ console.log(`Активный AI-профиль: ${fallback.name} (${fallback.provider}, ${fallback.model || "-"})`);
654
+ } else {
655
+ console.log(`Для текстовых запросов будет использован доступный профиль ${fallback.name} (${fallback.provider}).`);
656
+ }
651
657
  return readiness;
652
658
  }
653
659
  }
@@ -744,7 +750,7 @@ async function hasUsableCodexAuth() {
744
750
  }
745
751
 
746
752
  async function startAgentReadline() {
747
- const rl = readline.createInterface({ input, output, prompt: "iola> " });
753
+ const rl = readline.createInterface({ input, output, prompt: "> " });
748
754
  const state = {
749
755
  history: [],
750
756
  };
@@ -835,7 +841,7 @@ async function startAgentRawInput() {
835
841
  render();
836
842
  continue;
837
843
  }
838
- output.write(`iola> ${line}\n`);
844
+ output.write(`> ${line}\n`);
839
845
  try {
840
846
  const shouldExit = await handleAgentLine(line, state);
841
847
  if (shouldExit) break;
@@ -1285,7 +1291,7 @@ function currentSlashMatches(state) {
1285
1291
 
1286
1292
  function renderAgentInput(state) {
1287
1293
  clearAgentInputArea(state);
1288
- const prompt = "iola> ";
1294
+ const prompt = "> ";
1289
1295
  const lines = state.buffer.split("\n");
1290
1296
  const inputLines = [`${prompt}${lines[0] || ""}`, ...lines.slice(1).map((line) => ` ${line}`)];
1291
1297
  const cwdLine = colorMuted(` ${process.cwd()}`);
@@ -4384,6 +4390,13 @@ async function useAiProfile(name) {
4384
4390
  throw new Error(`AI-профиль не найден: ${name}`);
4385
4391
  }
4386
4392
 
4393
+ await setActiveAiProfile(name, profile, config);
4394
+
4395
+ console.log(`Активный AI-профиль: ${name} (${profile.provider}, ${profile.model || "-"})`);
4396
+ }
4397
+
4398
+ async function setActiveAiProfile(name, profile, loadedConfig = null) {
4399
+ const config = loadedConfig || await loadConfig();
4387
4400
  await saveConfig({
4388
4401
  ai: {
4389
4402
  ...config.ai,
@@ -4393,8 +4406,6 @@ async function useAiProfile(name) {
4393
4406
  baseUrl: profile.baseUrl || config.ai.baseUrl,
4394
4407
  },
4395
4408
  });
4396
-
4397
- console.log(`Активный AI-профиль: ${name} (${profile.provider}, ${profile.model || "-"})`);
4398
4409
  }
4399
4410
 
4400
4411
  async function deleteAiProfile(name) {
@@ -5893,7 +5904,8 @@ async function aiAsk(args, context = {}) {
5893
5904
  return localToolAsk(question, providerConfig, options);
5894
5905
  }
5895
5906
  applyRuntimeConfig(providerConfig, options.config);
5896
- const dataContext = options.bare ? { layers: [], query: { text: question, terms: [], patterns: { numbers: [], inns: [], streets: [], targetLayers: [] } }, schools: [], kindergartens: [] } : await buildDataContext(question);
5907
+ const useDataContext = !options.bare && shouldUseDataContext(question, options);
5908
+ const dataContext = useDataContext ? await buildDataContext(question) : emptyDataContext(question);
5897
5909
  emitEvent(options, "context_loaded", { schools: dataContext.schools.length, kindergartens: dataContext.kindergartens.length });
5898
5910
  const historyEnabled = !options.bare && !options["no-history"] && isFeatureEnabled("sqlite-history");
5899
5911
  const sessionId = historyEnabled && isFeatureEnabled("sessions") ? ensureSessionForAsk(options, providerConfig, question) : null;
@@ -6295,6 +6307,29 @@ async function buildDataContext(question) {
6295
6307
  };
6296
6308
  }
6297
6309
 
6310
+ function emptyDataContext(question) {
6311
+ return {
6312
+ enabled: false,
6313
+ layers: [],
6314
+ query: {
6315
+ text: question,
6316
+ terms: [],
6317
+ patterns: { numbers: [], inns: [], streets: [], targetLayers: [] },
6318
+ },
6319
+ schools: [],
6320
+ kindergartens: [],
6321
+ };
6322
+ }
6323
+
6324
+ function shouldUseDataContext(question, options = {}) {
6325
+ if (options.tools || options.files || options.schema || options.output) return true;
6326
+ const normalized = question.toLocaleLowerCase("ru-RU").trim();
6327
+ if (/^(привет|здравствуй|здравствуйте|добрый день|доброе утро|добрый вечер|hi|hello|hey)[!.?\s]*$/iu.test(normalized)) return false;
6328
+ if (/^(спасибо|благодарю|ок|окей|понял|поняла|ясно|хорошо|да|нет)[!.?\s]*$/iu.test(normalized)) return false;
6329
+ if (normalized.length <= 24 && /^(как дела|что нового|ты тут|ты здесь|кто ты)[?.!\s]*$/iu.test(normalized)) return false;
6330
+ return /\b(школ|сад|детсад|детский сад|лицей|гимнази|инн|адрес|телефон|почт|email|сайт|лиценз|руководител|директор|слой|слои|данн|отчет|отчёт|выгруз|csv|json|найди|покажи|список|карточк|организац|учрежден|йошкар|ола|петрова|строител|советск|первомайск)\b/iu.test(normalized);
6331
+ }
6332
+
6298
6333
  function extractSearchTerms(question) {
6299
6334
  const normalized = question
6300
6335
  .toLocaleLowerCase("ru-RU")
@@ -6391,13 +6426,14 @@ async function buildAiMessages(question, dataContext, history, options = {}, con
6391
6426
  const memoryText = options.bare ? "" : buildMemoryText();
6392
6427
  const projectContext = options.bare ? "" : await buildProjectContextText();
6393
6428
  const skillsText = options.bare ? "" : await buildSkillsText(config);
6429
+ const hasDataContext = dataContext.enabled !== false;
6394
6430
  const system = [
6395
- "Ты терминальный AI-ассистент CLI-проекта Йошкар-Олы.",
6396
- "Отвечай на русском языке.",
6397
- "Используй только данные из переданного контекста.",
6398
- "Если в контексте нет нужных сведений, прямо напиши, что данных недостаточно.",
6399
- "Не выдумывай адреса, телефоны, лицензии и руководителей.",
6400
- "Если отвечаешь по конкретным организациям, укажи источник в конце: слой, название и ИНН.",
6431
+ "Ты терминальный AI-агент городского округа Йошкар-Ола.",
6432
+ "Отвечай на русском языке естественно и по смыслу запроса пользователя.",
6433
+ hasDataContext ? "Используй только данные из переданного контекста открытых данных." : "Для обычного диалога отвечай как полноценный AI-ассистент, не перечисляй слои и возможности без запроса пользователя.",
6434
+ hasDataContext ? "Если в контексте нет нужных сведений, прямо напиши, что данных недостаточно." : "",
6435
+ hasDataContext ? "Не выдумывай адреса, телефоны, лицензии и руководителей." : "",
6436
+ hasDataContext ? "Если отвечаешь по конкретным организациям, укажи источник в конце: слой, название и ИНН." : "",
6401
6437
  options.schema === "json" ? "Верни валидный JSON без markdown-обертки." : "",
6402
6438
  options.schema === "table" ? "Если уместно, верни ответ в виде markdown-таблицы." : "",
6403
6439
  memoryText ? `Учитывай пользовательскую память:\n${memoryText}` : "",
@@ -6407,14 +6443,14 @@ async function buildAiMessages(question, dataContext, history, options = {}, con
6407
6443
  ].filter(Boolean).join(" ");
6408
6444
  const contextText = JSON.stringify(dataContext, null, 2);
6409
6445
  const recentHistory = history.slice(-6);
6446
+ const userContent = hasDataContext
6447
+ ? `Контекст открытых данных городского округа "Город Йошкар-Ола":\n${contextText}\n\nКраткие источники контекста:\n${sourceLines}\n\nВопрос пользователя: ${question}`
6448
+ : question;
6410
6449
 
6411
6450
  return [
6412
6451
  { role: "system", content: system },
6413
6452
  ...recentHistory,
6414
- {
6415
- role: "user",
6416
- content: `Контекст открытых данных городского округа "Город Йошкар-Ола":\n${contextText}\n\nКраткие источники контекста:\n${sourceLines}\n\nВопрос пользователя: ${question}`,
6417
- },
6453
+ { role: "user", content: userContent },
6418
6454
  ];
6419
6455
  }
6420
6456