@kernel.chat/kbot 2.4.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 (60) 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 +220 -21
  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/serve.d.ts +8 -0
  34. package/dist/serve.d.ts.map +1 -0
  35. package/dist/serve.js +159 -0
  36. package/dist/serve.js.map +1 -0
  37. package/dist/streaming.d.ts.map +1 -1
  38. package/dist/streaming.js +22 -2
  39. package/dist/streaming.js.map +1 -1
  40. package/dist/task-ledger.d.ts +71 -0
  41. package/dist/task-ledger.d.ts.map +1 -0
  42. package/dist/task-ledger.js +282 -0
  43. package/dist/task-ledger.js.map +1 -0
  44. package/dist/tools/computer.js +3 -3
  45. package/dist/tools/computer.js.map +1 -1
  46. package/dist/tools/index.d.ts.map +1 -1
  47. package/dist/tools/index.js +7 -1
  48. package/dist/tools/index.js.map +1 -1
  49. package/dist/tools/memory-tools.d.ts +2 -0
  50. package/dist/tools/memory-tools.d.ts.map +1 -0
  51. package/dist/tools/memory-tools.js +228 -0
  52. package/dist/tools/memory-tools.js.map +1 -0
  53. package/dist/tools/quality.d.ts +2 -0
  54. package/dist/tools/quality.d.ts.map +1 -0
  55. package/dist/tools/quality.js +313 -0
  56. package/dist/tools/quality.js.map +1 -0
  57. package/dist/ui.d.ts.map +1 -1
  58. package/dist/ui.js +33 -18
  59. package/dist/ui.js.map +1 -1
  60. 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.4.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) {
@@ -368,6 +369,20 @@ async function main() {
368
369
  printInfo(`Or pull all: ${missing.map(m => `ollama pull ${m.name}`).join(' && ')}`);
369
370
  }
370
371
  });
372
+ program
373
+ .command('serve')
374
+ .description('Start HTTP server — expose all 93 tools for kernel.chat or any client')
375
+ .option('-p, --port <port>', 'Port to listen on', '7437')
376
+ .option('--token <token>', 'Require auth token for all requests')
377
+ .option('--computer-use', 'Enable computer use tools')
378
+ .action(async (opts) => {
379
+ const { startServe } = await import('./serve.js');
380
+ await startServe({
381
+ port: parseInt(opts.port, 10),
382
+ token: opts.token,
383
+ computerUse: opts.computerUse,
384
+ });
385
+ });
371
386
  program
372
387
  .command('openclaw')
373
388
  .description('Use OpenClaw gateway as AI provider')
@@ -387,6 +402,27 @@ async function main() {
387
402
  printError('Cannot connect to OpenClaw gateway. Start it first.');
388
403
  }
389
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
+ });
390
426
  program.parse(process.argv);
391
427
  const opts = program.opts();
392
428
  const promptArgs = program.args;
@@ -394,7 +430,7 @@ async function main() {
394
430
  if (opts.quiet)
395
431
  setQuiet(true);
396
432
  // If a sub-command was run, we're done
397
- if (['byok', 'auth', 'ide', 'ollama', 'openclaw', 'pull', 'doctor'].includes(program.args[0]))
433
+ if (['byok', 'auth', 'ide', 'ollama', 'openclaw', 'pull', 'doctor', 'serve', 'agents'].includes(program.args[0]))
398
434
  return;
399
435
  // Check for API key (BYOK or local provider)
400
436
  let byokActive = isByokEnabled();
@@ -408,9 +444,10 @@ async function main() {
408
444
  localActive = isLocalProvider(envDetected);
409
445
  printSuccess(`Auto-detected ${PROVIDERS[envDetected].name} from environment.`);
410
446
  }
411
- // 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)
412
448
  if (!byokActive) {
413
- 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([
414
451
  // Check Ollama
415
452
  (async () => {
416
453
  const up = await isOllamaRunning();
@@ -422,6 +459,22 @@ async function main() {
422
459
  const ok = await setupOllama();
423
460
  return ok ? { provider: 'ollama', models: models.length } : null;
424
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
+ })(),
425
478
  // Check OpenClaw
426
479
  (async () => {
427
480
  try {
@@ -436,14 +489,26 @@ async function main() {
436
489
  }
437
490
  })(),
438
491
  ]);
439
- // Prefer Ollama if both available (more models, more control)
492
+ // Prefer Ollama > LM Studio > Jan > OpenClaw
440
493
  const ollamaOk = ollamaResult.status === 'fulfilled' && ollamaResult.value;
494
+ const lmstudioOk = lmstudioResult.status === 'fulfilled' && lmstudioResult.value;
495
+ const janOk = janResult.status === 'fulfilled' && janResult.value;
441
496
  const openclawOk = openclawResult.status === 'fulfilled' && openclawResult.value;
442
497
  if (ollamaOk) {
443
498
  byokActive = true;
444
499
  localActive = true;
445
500
  printSuccess(`Auto-configured Ollama (${ollamaOk.models} models). Ready — $0 cost!`);
446
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
+ }
447
512
  else if (openclawOk) {
448
513
  byokActive = true;
449
514
  localActive = true;
@@ -474,6 +539,9 @@ async function main() {
474
539
  { env: 'PERPLEXITY_API_KEY', provider: 'perplexity' },
475
540
  { env: 'COHERE_API_KEY', provider: 'cohere' },
476
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' },
477
545
  ];
478
546
  for (const { env, provider } of envKeys) {
479
547
  if (process.env[env])
@@ -499,6 +567,8 @@ async function main() {
499
567
  setPermissionMode('permissive');
500
568
  }
501
569
  }
570
+ // Register built-in agents (hacker, operator, dreamer) so --agent flag works
571
+ registerBuiltinAgents();
502
572
  // Parallel startup: register tools, gather context, check updates, and cloud sync
503
573
  const [, context, , syncMsg] = await Promise.all([
504
574
  registerAllTools({ computerUse: opts.computerUse }),
@@ -528,6 +598,11 @@ async function main() {
528
598
  thinking: opts.thinking || false,
529
599
  thinkingBudget: opts.thinkingBudget ? (parseInt(opts.thinkingBudget, 10) || 10000) : undefined,
530
600
  };
601
+ // Enable self-evaluation if requested
602
+ if (opts.selfEval) {
603
+ const { setSelfEvalEnabled } = await import('./self-eval.js');
604
+ setSelfEvalEnabled(true);
605
+ }
531
606
  // Pipe mode: echo "prompt" | kbot -p OR kbot -p "prompt"
532
607
  if (opts.pipe) {
533
608
  let message = promptArgs.join(' ');
@@ -725,12 +800,13 @@ async function byokFlow() {
725
800
  async function guidedSetup() {
726
801
  console.log(banner(VERSION));
727
802
  console.log();
728
- 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.'));
729
804
  console.log();
730
- 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:'));
731
807
  console.log();
732
- console.log(` ${chalk.bold('1.')} ${chalk.hex('#6B8E6B')('Free & Private')} — Run AI on your computer (no account needed)`);
733
- 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.)`);
734
810
  console.log();
735
811
  const rl = createInterface({ input: process.stdin, output: process.stdout });
736
812
  const choice = await new Promise((resolve) => {
@@ -871,14 +947,48 @@ async function startRepl(agentOpts, context, tier, byokActive = false, localActi
871
947
  printInfo(`${p.name}`);
872
948
  }
873
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
+ }
874
964
  const rl = createInterface({
875
965
  input: process.stdin,
876
966
  output: process.stdout,
877
967
  prompt: kbotPrompt(),
878
968
  });
879
- // First time? Show a gentle hint
969
+ // First time? Show a friendly walkthrough
880
970
  if (sessionCount <= 1) {
881
- 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)]);
882
992
  }
883
993
  console.log();
884
994
  rl.prompt();
@@ -915,12 +1025,28 @@ async function startRepl(agentOpts, context, tier, byokActive = false, localActi
915
1025
  if (!response.streamed) {
916
1026
  printResponse(response.agent, response.content);
917
1027
  }
918
- // Usage footer — subtle, compact like Claude Code
919
- if (response.usage) {
920
- const tokens = response.usage.input_tokens + response.usage.output_tokens;
921
- const cost = response.usage.cost_usd === 0 ? 'free' : `$${response.usage.cost_usd.toFixed(4)}`;
922
- const tools = response.toolCalls > 0 ? ` · ${response.toolCalls} tools` : '';
923
- 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
+ }
924
1050
  }
925
1051
  // Schedule cloud sync push (debounced — batches writes)
926
1052
  schedulePush();
@@ -1057,6 +1183,47 @@ async function handleSlashCommand(input, opts, rl) {
1057
1183
  case 'help':
1058
1184
  printHelp();
1059
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
+ }
1060
1227
  case 'agent':
1061
1228
  if (args[0]) {
1062
1229
  opts.agent = args[0];
@@ -1227,6 +1394,37 @@ async function handleSlashCommand(input, opts, rl) {
1227
1394
  if (opts.thinking)
1228
1395
  printInfo('AI will show reasoning steps before responding.');
1229
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
+ }
1230
1428
  case 'plan': {
1231
1429
  const planTask = args.join(' ');
1232
1430
  if (!planTask) {
@@ -1499,7 +1697,8 @@ async function handleSlashCommand(input, opts, rl) {
1499
1697
  rl.close();
1500
1698
  break;
1501
1699
  default:
1502
- 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');
1503
1702
  }
1504
1703
  }
1505
1704
  // Prevent unhandled rejections from killing the process