@kernel.chat/kbot 2.5.0 → 2.7.0

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 (56) hide show
  1. package/README.md +98 -42
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +116 -21
  4. package/dist/agent.js.map +1 -1
  5. package/dist/auth.d.ts +11 -1
  6. package/dist/auth.d.ts.map +1 -1
  7. package/dist/auth.js +165 -7
  8. package/dist/auth.js.map +1 -1
  9. package/dist/cli.js +207 -22
  10. package/dist/cli.js.map +1 -1
  11. package/dist/ide/acp-server.js +2 -2
  12. package/dist/ide/acp-server.js.map +1 -1
  13. package/dist/ide/mcp-server.js +1 -1
  14. package/dist/matrix.d.ts +6 -0
  15. package/dist/matrix.d.ts.map +1 -1
  16. package/dist/matrix.js +98 -0
  17. package/dist/matrix.js.map +1 -1
  18. package/dist/planner.d.ts.map +1 -1
  19. package/dist/planner.js +29 -0
  20. package/dist/planner.js.map +1 -1
  21. package/dist/provider-fallback.d.ts +51 -0
  22. package/dist/provider-fallback.d.ts.map +1 -0
  23. package/dist/provider-fallback.js +237 -0
  24. package/dist/provider-fallback.js.map +1 -0
  25. package/dist/repo-map.d.ts +9 -0
  26. package/dist/repo-map.d.ts.map +1 -0
  27. package/dist/repo-map.js +280 -0
  28. package/dist/repo-map.js.map +1 -0
  29. package/dist/self-eval.d.ts +45 -0
  30. package/dist/self-eval.d.ts.map +1 -0
  31. package/dist/self-eval.js +232 -0
  32. package/dist/self-eval.js.map +1 -0
  33. package/dist/streaming.d.ts.map +1 -1
  34. package/dist/streaming.js +22 -2
  35. package/dist/streaming.js.map +1 -1
  36. package/dist/task-ledger.d.ts +71 -0
  37. package/dist/task-ledger.d.ts.map +1 -0
  38. package/dist/task-ledger.js +282 -0
  39. package/dist/task-ledger.js.map +1 -0
  40. package/dist/tools/computer.js +3 -3
  41. package/dist/tools/computer.js.map +1 -1
  42. package/dist/tools/index.d.ts.map +1 -1
  43. package/dist/tools/index.js +7 -1
  44. package/dist/tools/index.js.map +1 -1
  45. package/dist/tools/memory-tools.d.ts +2 -0
  46. package/dist/tools/memory-tools.d.ts.map +1 -0
  47. package/dist/tools/memory-tools.js +228 -0
  48. package/dist/tools/memory-tools.js.map +1 -0
  49. package/dist/tools/quality.d.ts +2 -0
  50. package/dist/tools/quality.d.ts.map +1 -0
  51. package/dist/tools/quality.js +313 -0
  52. package/dist/tools/quality.js.map +1 -0
  53. package/dist/ui.d.ts.map +1 -1
  54. package/dist/ui.js +33 -18
  55. package/dist/ui.js.map +1 -1
  56. package/package.json +27 -3
package/dist/cli.js CHANGED
@@ -16,20 +16,20 @@ import { gatherContext } from './context.js';
16
16
  import { registerAllTools } from './tools/index.js';
17
17
  import { clearHistory, clearMemory, compactHistory, restoreHistory } from './memory.js';
18
18
  import { saveSession, loadSession, listSessions, deleteSession, formatSessionList, } from './sessions.js';
19
- import { createAgent, removeAgent, getAgent, formatAgentList, formatAgentDetail, PRESETS, getMatrixAgentIds, activateMimic, listMimicProfiles, getMimicProfile, } from './matrix.js';
19
+ import { createAgent, removeAgent, getAgent, formatAgentList, formatAgentDetail, PRESETS, getMatrixAgentIds, activateMimic, listMimicProfiles, getMimicProfile, registerBuiltinAgents, formatBuiltinAgentList, formatBuiltinAgentDetail, } from './matrix.js';
20
20
  import { getExtendedStats, incrementSessions, learnFact, selfTrain, getTrainingLog, flushPendingWrites } from './learning.js';
21
21
  import { banner, bannerCompact, bannerAuth, prompt as kbotPrompt, printError, printSuccess, printInfo, printResponse, printHelp, printGoodbye, setQuiet, } from './ui.js';
22
22
  import { checkForUpdate, selfUpdate } from './updater.js';
23
23
  import { syncOnStartup, schedulePush, flushCloudSync, isCloudSyncEnabled, setCloudToken, getCloudToken } from './cloud-sync.js';
24
24
  import chalk from 'chalk';
25
- const VERSION = '2.5.0';
25
+ const VERSION = '2.7.0';
26
26
  async function main() {
27
27
  const program = new Command();
28
28
  program
29
29
  .name('kbot')
30
30
  .description('K:BOT — Open-source terminal AI agent. Bring your own key, pick your model, run locally.')
31
31
  .version(VERSION)
32
- .option('-a, --agent <agent>', 'Force a specific agent (kernel, researcher, coder, writer, analyst)')
32
+ .option('-a, --agent <agent>', 'Force a specific agent (kernel, researcher, coder, writer, analyst, hacker, operator, dreamer)')
33
33
  .option('-m, --model <model>', 'Override AI model (auto, sonnet, haiku)')
34
34
  .option('-s, --stream', 'Stream the response')
35
35
  .option('-p, --pipe', 'Pipe mode — raw text output for scripting')
@@ -40,6 +40,7 @@ async function main() {
40
40
  .option('--computer-use', 'Enable computer use tools')
41
41
  .option('-t, --thinking', 'Show AI reasoning steps')
42
42
  .option('--thinking-budget <tokens>', 'Thinking token budget (default: 10000)')
43
+ .option('--self-eval', 'Enable self-evaluation loop (score and retry low-quality responses)')
43
44
  .option('--safe', 'Confirm destructive operations')
44
45
  .option('--strict', 'Confirm ALL operations')
45
46
  .argument('[prompt...]', 'One-shot prompt')
@@ -86,7 +87,7 @@ async function main() {
86
87
  });
87
88
  program
88
89
  .command('byok')
89
- .description('Bring Your Own Key — configure your LLM API key (14 providers)')
90
+ .description('Bring Your Own Key — configure your LLM API key (19 providers)')
90
91
  .option('--off', 'Disable BYOK mode')
91
92
  .action(async (opts) => {
92
93
  if (opts.off) {
@@ -370,7 +371,7 @@ async function main() {
370
371
  });
371
372
  program
372
373
  .command('serve')
373
- .description('Start HTTP server — expose all 60+ tools for kernel.chat or any client')
374
+ .description('Start HTTP server — expose all 93 tools for kernel.chat or any client')
374
375
  .option('-p, --port <port>', 'Port to listen on', '7437')
375
376
  .option('--token <token>', 'Require auth token for all requests')
376
377
  .option('--computer-use', 'Enable computer use tools')
@@ -401,6 +402,27 @@ async function main() {
401
402
  printError('Cannot connect to OpenClaw gateway. Start it first.');
402
403
  }
403
404
  });
405
+ program
406
+ .command('agents [name]')
407
+ .description('List all available agents, or show details for one')
408
+ .action(async (name) => {
409
+ registerBuiltinAgents();
410
+ if (name) {
411
+ const detail = formatBuiltinAgentDetail(name);
412
+ if (detail) {
413
+ console.log(detail);
414
+ }
415
+ else {
416
+ printError(`Agent "${name}" not found.`);
417
+ printInfo('Run `kbot agents` to see all available agents.');
418
+ }
419
+ }
420
+ else {
421
+ console.log(formatBuiltinAgentList());
422
+ console.log();
423
+ printInfo('Use: kbot --agent <name> "prompt"');
424
+ }
425
+ });
404
426
  program.parse(process.argv);
405
427
  const opts = program.opts();
406
428
  const promptArgs = program.args;
@@ -408,7 +430,7 @@ async function main() {
408
430
  if (opts.quiet)
409
431
  setQuiet(true);
410
432
  // If a sub-command was run, we're done
411
- if (['byok', 'auth', 'ide', 'ollama', 'openclaw', 'pull', 'doctor', 'serve'].includes(program.args[0]))
433
+ if (['byok', 'auth', 'ide', 'ollama', 'openclaw', 'pull', 'doctor', 'serve', 'agents'].includes(program.args[0]))
412
434
  return;
413
435
  // Check for API key (BYOK or local provider)
414
436
  let byokActive = isByokEnabled();
@@ -422,9 +444,10 @@ async function main() {
422
444
  localActive = isLocalProvider(envDetected);
423
445
  printSuccess(`Auto-detected ${PROVIDERS[envDetected].name} from environment.`);
424
446
  }
425
- // Priority 2: Check local providers in PARALLEL (both at once, use whichever responds first)
447
+ // Priority 2: Check local providers in PARALLEL (all at once, use whichever responds first)
426
448
  if (!byokActive) {
427
- const [ollamaResult, openclawResult] = await Promise.allSettled([
449
+ const { isLmStudioRunning, isJanRunning, setupLmStudio, setupJan } = await import('./auth.js');
450
+ const [ollamaResult, lmstudioResult, janResult, openclawResult] = await Promise.allSettled([
428
451
  // Check Ollama
429
452
  (async () => {
430
453
  const up = await isOllamaRunning();
@@ -436,6 +459,22 @@ async function main() {
436
459
  const ok = await setupOllama();
437
460
  return ok ? { provider: 'ollama', models: models.length } : null;
438
461
  })(),
462
+ // Check LM Studio
463
+ (async () => {
464
+ const up = await isLmStudioRunning();
465
+ if (!up)
466
+ return null;
467
+ const ok = await setupLmStudio();
468
+ return ok ? { provider: 'lmstudio' } : null;
469
+ })(),
470
+ // Check Jan
471
+ (async () => {
472
+ const up = await isJanRunning();
473
+ if (!up)
474
+ return null;
475
+ const ok = await setupJan();
476
+ return ok ? { provider: 'jan' } : null;
477
+ })(),
439
478
  // Check OpenClaw
440
479
  (async () => {
441
480
  try {
@@ -450,14 +489,26 @@ async function main() {
450
489
  }
451
490
  })(),
452
491
  ]);
453
- // Prefer Ollama if both available (more models, more control)
492
+ // Prefer Ollama > LM Studio > Jan > OpenClaw
454
493
  const ollamaOk = ollamaResult.status === 'fulfilled' && ollamaResult.value;
494
+ const lmstudioOk = lmstudioResult.status === 'fulfilled' && lmstudioResult.value;
495
+ const janOk = janResult.status === 'fulfilled' && janResult.value;
455
496
  const openclawOk = openclawResult.status === 'fulfilled' && openclawResult.value;
456
497
  if (ollamaOk) {
457
498
  byokActive = true;
458
499
  localActive = true;
459
500
  printSuccess(`Auto-configured Ollama (${ollamaOk.models} models). Ready — $0 cost!`);
460
501
  }
502
+ else if (lmstudioOk) {
503
+ byokActive = true;
504
+ localActive = true;
505
+ printSuccess('Auto-configured LM Studio. Ready — $0 cost!');
506
+ }
507
+ else if (janOk) {
508
+ byokActive = true;
509
+ localActive = true;
510
+ printSuccess('Auto-configured Jan. Ready — $0 cost!');
511
+ }
461
512
  else if (openclawOk) {
462
513
  byokActive = true;
463
514
  localActive = true;
@@ -488,6 +539,9 @@ async function main() {
488
539
  { env: 'PERPLEXITY_API_KEY', provider: 'perplexity' },
489
540
  { env: 'COHERE_API_KEY', provider: 'cohere' },
490
541
  { env: 'NVIDIA_API_KEY', provider: 'nvidia' },
542
+ { env: 'SAMBANOVA_API_KEY', provider: 'sambanova' },
543
+ { env: 'CEREBRAS_API_KEY', provider: 'cerebras' },
544
+ { env: 'OPENROUTER_API_KEY', provider: 'openrouter' },
491
545
  ];
492
546
  for (const { env, provider } of envKeys) {
493
547
  if (process.env[env])
@@ -513,6 +567,8 @@ async function main() {
513
567
  setPermissionMode('permissive');
514
568
  }
515
569
  }
570
+ // Register built-in agents (hacker, operator, dreamer) so --agent flag works
571
+ registerBuiltinAgents();
516
572
  // Parallel startup: register tools, gather context, check updates, and cloud sync
517
573
  const [, context, , syncMsg] = await Promise.all([
518
574
  registerAllTools({ computerUse: opts.computerUse }),
@@ -542,6 +598,11 @@ async function main() {
542
598
  thinking: opts.thinking || false,
543
599
  thinkingBudget: opts.thinkingBudget ? (parseInt(opts.thinkingBudget, 10) || 10000) : undefined,
544
600
  };
601
+ // Enable self-evaluation if requested
602
+ if (opts.selfEval) {
603
+ const { setSelfEvalEnabled } = await import('./self-eval.js');
604
+ setSelfEvalEnabled(true);
605
+ }
545
606
  // Pipe mode: echo "prompt" | kbot -p OR kbot -p "prompt"
546
607
  if (opts.pipe) {
547
608
  let message = promptArgs.join(' ');
@@ -739,12 +800,13 @@ async function byokFlow() {
739
800
  async function guidedSetup() {
740
801
  console.log(banner(VERSION));
741
802
  console.log();
742
- console.log(chalk.bold(' Welcome! Let\'s get you set up.'));
803
+ console.log(chalk.bold(' Hey! I\'m K:BOT your AI assistant for the terminal.'));
743
804
  console.log();
744
- console.log(chalk.dim(' K:BOT needs an AI brain to work. You have two options:'));
805
+ console.log(chalk.dim(' I can write code, answer questions, search the web, manage git,'));
806
+ console.log(chalk.dim(' and a lot more. First, I need an AI brain. Pick one:'));
745
807
  console.log();
746
- console.log(` ${chalk.bold('1.')} ${chalk.hex('#6B8E6B')('Free & Private')} — Run AI on your computer (no account needed)`);
747
- console.log(` ${chalk.bold('2.')} ${chalk.hex('#5B8BA0')('Cloud AI')} — Use an API key from OpenAI, Google, Anthropic, etc.`);
808
+ console.log(` ${chalk.bold('1.')} ${chalk.hex('#6B8E6B')('Free & Private')} — Run AI on your computer (no account, no cost)`);
809
+ console.log(` ${chalk.bold('2.')} ${chalk.hex('#5B8BA0')('Cloud AI')} — Use an API key (OpenAI, Google, Anthropic, etc.)`);
748
810
  console.log();
749
811
  const rl = createInterface({ input: process.stdin, output: process.stdout });
750
812
  const choice = await new Promise((resolve) => {
@@ -885,14 +947,48 @@ async function startRepl(agentOpts, context, tier, byokActive = false, localActi
885
947
  printInfo(`${p.name}`);
886
948
  }
887
949
  const sessionCount = incrementSessions();
950
+ // Return-visit greeting — show kbot's growth
951
+ if (sessionCount > 1) {
952
+ const stats = getExtendedStats();
953
+ const parts = [];
954
+ if (stats.patternsCount > 0)
955
+ parts.push(`${stats.patternsCount} patterns learned`);
956
+ if (stats.solutionsCount > 0)
957
+ parts.push(`${stats.solutionsCount} solutions cached`);
958
+ if (stats.knowledgeCount > 0)
959
+ parts.push(`${stats.knowledgeCount} facts remembered`);
960
+ if (parts.length > 0) {
961
+ printInfo(`Session ${stats.sessions} · ${parts.join(' · ')}`);
962
+ }
963
+ }
888
964
  const rl = createInterface({
889
965
  input: process.stdin,
890
966
  output: process.stdout,
891
967
  prompt: kbotPrompt(),
892
968
  });
893
- // First time? Show a gentle hint
969
+ // First time? Show a friendly walkthrough
894
970
  if (sessionCount <= 1) {
895
- printInfo('Try: "hello" or "create a python script" or /help');
971
+ console.log();
972
+ console.log(chalk.dim(' ┌─────────────────────────────────────────────┐'));
973
+ console.log(chalk.dim(' │') + chalk.bold(' Welcome! Here are some things you can try: ') + chalk.dim(' │'));
974
+ console.log(chalk.dim(' │ │'));
975
+ console.log(chalk.dim(' │') + ' "explain this project" ' + chalk.dim(' │'));
976
+ console.log(chalk.dim(' │') + ' "write a function that sorts names" ' + chalk.dim(' │'));
977
+ console.log(chalk.dim(' │') + ' "find all TODO comments in this repo" ' + chalk.dim(' │'));
978
+ console.log(chalk.dim(' │') + ' "what files are in this directory?" ' + chalk.dim(' │'));
979
+ console.log(chalk.dim(' │ │'));
980
+ console.log(chalk.dim(' │') + chalk.dim(' Type /help for more commands. ') + chalk.dim(' │'));
981
+ console.log(chalk.dim(' └─────────────────────────────────────────────┘'));
982
+ console.log();
983
+ }
984
+ else if (sessionCount <= 3) {
985
+ // Second and third time — show a quick tip they might not know
986
+ const tips = [
987
+ 'Tip: kbot picks the right specialist for you. Try asking about science, code, or writing.',
988
+ 'Tip: Type /save to keep this conversation. /resume to pick it up later.',
989
+ 'Tip: kbot learns from you. The more you use it, the better it gets.',
990
+ ];
991
+ printInfo(tips[Math.min(sessionCount - 1, tips.length - 1)]);
896
992
  }
897
993
  console.log();
898
994
  rl.prompt();
@@ -929,12 +1025,28 @@ async function startRepl(agentOpts, context, tier, byokActive = false, localActi
929
1025
  if (!response.streamed) {
930
1026
  printResponse(response.agent, response.content);
931
1027
  }
932
- // Usage footer — subtle, compact like Claude Code
933
- if (response.usage) {
934
- const tokens = response.usage.input_tokens + response.usage.output_tokens;
935
- const cost = response.usage.cost_usd === 0 ? 'free' : `$${response.usage.cost_usd.toFixed(4)}`;
936
- const tools = response.toolCalls > 0 ? ` · ${response.toolCalls} tools` : '';
937
- printInfo(`${tokens} tokens${tools} · ${cost}`);
1028
+ // Mentor footer — teach the user what kbot did (agent, tools, cost)
1029
+ {
1030
+ const parts = [];
1031
+ // Show which agent handled it (teaches routing)
1032
+ if (response.agent && response.agent !== 'kernel') {
1033
+ parts.push(`${response.agent} agent`);
1034
+ }
1035
+ if (response.toolCalls > 0) {
1036
+ parts.push(`${response.toolCalls} tool${response.toolCalls > 1 ? 's' : ''} used`);
1037
+ }
1038
+ if (response.usage) {
1039
+ const tokens = response.usage.input_tokens + response.usage.output_tokens;
1040
+ const cost = response.usage.cost_usd === 0 ? 'free' : `$${response.usage.cost_usd.toFixed(4)}`;
1041
+ parts.push(`${tokens} tokens · ${cost}`);
1042
+ }
1043
+ if (parts.length > 0) {
1044
+ printInfo(parts.join(' · '));
1045
+ }
1046
+ // On first 5 sessions, teach what just happened
1047
+ if (sessionCount <= 5 && response.agent && response.agent !== 'kernel') {
1048
+ printInfo(chalk.dim(` I picked the ${response.agent} agent for this. You can also say /agent ${response.agent} to use it directly.`));
1049
+ }
938
1050
  }
939
1051
  // Schedule cloud sync push (debounced — batches writes)
940
1052
  schedulePush();
@@ -1071,6 +1183,47 @@ async function handleSlashCommand(input, opts, rl) {
1071
1183
  case 'help':
1072
1184
  printHelp();
1073
1185
  break;
1186
+ case 'tutorial': {
1187
+ console.log();
1188
+ console.log(chalk.bold(' Let\'s build something together. Pick a track:'));
1189
+ console.log();
1190
+ console.log(` ${chalk.bold('1.')} ${chalk.hex('#4ADE80')('Build a website')} — Create an HTML page from scratch`);
1191
+ console.log(` ${chalk.bold('2.')} ${chalk.hex('#60A5FA')('Fix a bug')} — Find and fix a problem in code`);
1192
+ console.log(` ${chalk.bold('3.')} ${chalk.hex('#FB923C')('Write a script')} — Automate something with Python or JavaScript`);
1193
+ console.log(` ${chalk.bold('4.')} ${chalk.hex('#F472B6')('Research a topic')} — Deep-dive into any subject`);
1194
+ console.log(` ${chalk.bold('5.')} ${chalk.hex('#A78BFA')('Explore this project')} — Understand the code in this folder`);
1195
+ console.log();
1196
+ console.log(chalk.dim(' Pick a number, or just type what you want to build.'));
1197
+ console.log();
1198
+ const tutorialInput = await new Promise((resolve) => {
1199
+ rl.question(' Your choice: ', (answer) => resolve(answer.trim()));
1200
+ });
1201
+ const tutorialPrompts = {
1202
+ '1': 'Create a simple, beautiful HTML page with a heading, a paragraph about me, and some CSS styling. Save it as index.html. Walk me through what each part does.',
1203
+ '2': 'Look at the files in this directory. Find any issues, bugs, or things that could be improved. Explain what you found and fix the most important one.',
1204
+ '3': 'Write a short script that lists all files in the current directory, sorted by size. Use the language that makes the most sense for this project. Explain each line.',
1205
+ '4': 'I want to understand how AI agents work. Research this topic and explain it simply — what are agents, how do they think, and what tools do they use? Give real examples.',
1206
+ '5': 'Explore this project directory. What language is it written in? What does it do? What are the most important files? Give me a quick tour.',
1207
+ };
1208
+ const prompt = tutorialPrompts[tutorialInput] || tutorialInput;
1209
+ if (prompt) {
1210
+ console.log();
1211
+ printInfo('Great choice! Let me work on that...');
1212
+ console.log();
1213
+ try {
1214
+ const response = await runAgent(prompt, opts);
1215
+ if (!response.streamed) {
1216
+ printResponse(response.agent, response.content);
1217
+ }
1218
+ console.log();
1219
+ printInfo(chalk.dim('That\'s the basics! Keep asking questions — I learn from every conversation.'));
1220
+ }
1221
+ catch (err) {
1222
+ printError(err instanceof Error ? err.message : String(err));
1223
+ }
1224
+ }
1225
+ break;
1226
+ }
1074
1227
  case 'agent':
1075
1228
  if (args[0]) {
1076
1229
  opts.agent = args[0];
@@ -1241,6 +1394,37 @@ async function handleSlashCommand(input, opts, rl) {
1241
1394
  if (opts.thinking)
1242
1395
  printInfo('AI will show reasoning steps before responding.');
1243
1396
  break;
1397
+ case 'self-eval':
1398
+ case 'selfeval': {
1399
+ const { isSelfEvalEnabled, setSelfEvalEnabled } = await import('./self-eval.js');
1400
+ const newState = !isSelfEvalEnabled();
1401
+ setSelfEvalEnabled(newState);
1402
+ printSuccess(`Self-evaluation: ${newState ? 'ON' : 'OFF'}`);
1403
+ if (newState)
1404
+ printInfo('Responses will be scored for quality and retried if below threshold.');
1405
+ break;
1406
+ }
1407
+ case 'health':
1408
+ case 'providers': {
1409
+ const { getProviderHealth } = await import('./provider-fallback.js');
1410
+ const health = getProviderHealth();
1411
+ const active = health.filter(h => h.lastSuccess || h.lastFailure);
1412
+ if (active.length === 0) {
1413
+ printInfo('No provider calls recorded yet.');
1414
+ }
1415
+ else {
1416
+ console.log();
1417
+ printInfo('Provider Health:');
1418
+ for (const h of active) {
1419
+ const status = h.isHealthy ? chalk.green('healthy') : chalk.red('unhealthy');
1420
+ const latency = h.avgLatencyMs ? `${h.avgLatencyMs}ms` : '-';
1421
+ const failures = h.consecutiveFailures > 0 ? chalk.yellow(` (${h.consecutiveFailures} failures)`) : '';
1422
+ printInfo(` ${h.provider}: ${status} · ${latency}${failures}`);
1423
+ }
1424
+ console.log();
1425
+ }
1426
+ break;
1427
+ }
1244
1428
  case 'plan': {
1245
1429
  const planTask = args.join(' ');
1246
1430
  if (!planTask) {
@@ -1513,7 +1697,8 @@ async function handleSlashCommand(input, opts, rl) {
1513
1697
  rl.close();
1514
1698
  break;
1515
1699
  default:
1516
- printError(`Unknown command: /${cmd}. Type /help for available commands.`);
1700
+ printError(`I don't know "/${cmd}". Here are some you can try:`);
1701
+ printInfo(' /save — save this chat | /agent — pick a specialist | /help — see everything');
1517
1702
  }
1518
1703
  }
1519
1704
  // Prevent unhandled rejections from killing the process