@kernel.chat/kbot 3.8.2 → 3.9.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.
package/dist/cli.js CHANGED
@@ -46,6 +46,7 @@ async function main() {
46
46
  .option('-t, --thinking', 'Show AI reasoning steps')
47
47
  .option('--thinking-budget <tokens>', 'Thinking token budget (default: 10000)')
48
48
  .option('--self-eval', 'Enable self-evaluation loop (score and retry low-quality responses)')
49
+ .option('--plan', 'Plan mode — read-only exploration, no changes')
49
50
  .option('--architect', 'Architect mode — plan-review-implement with dual agents')
50
51
  .option('--safe', 'Confirm destructive operations')
51
52
  .option('--strict', 'Confirm ALL operations')
@@ -525,6 +526,161 @@ async function main() {
525
526
  const speed = system.recommendModelSpeed();
526
527
  process.stderr.write(`\n ${AMETHYST('Cost regulation')}: ${speed === 'fast' ? chalk.yellow('conserving (fast model)') : chalk.green('normal (default model)')}\n\n`);
527
528
  });
529
+ program
530
+ .command('status')
531
+ .description('Unified Kernel dashboard — version, tools, agents, learning, collective, npm, GitHub, bootstrap')
532
+ .option('--json', 'Output as JSON')
533
+ .action(async (opts) => {
534
+ // Check both subcommand and parent opts (commander absorbs --json at parent level)
535
+ const jsonMode = opts.json || program.opts().json;
536
+ const chalk = (await import('chalk')).default;
537
+ const AMETHYST = chalk.hex('#6B5B95');
538
+ const DIM = chalk.dim;
539
+ const GREEN = chalk.hex('#4ADE80');
540
+ const YELLOW = chalk.hex('#FBBF24');
541
+ const RED = chalk.hex('#F87171');
542
+ const CYAN = chalk.hex('#67E8F9');
543
+ const line = DIM(' ' + '─'.repeat(40));
544
+ // ── 1. Version & latest check ──
545
+ let latestVersion = '';
546
+ let isLatest = false;
547
+ const npmDownloads = {};
548
+ const githubData = {};
549
+ // Fire all network requests in parallel with 3s timeouts
550
+ const [npmVersionRes, npmDlRes, ghRes] = await Promise.allSettled([
551
+ fetch('https://registry.npmjs.org/@kernel.chat/kbot/latest', { signal: AbortSignal.timeout(3000) })
552
+ .then(r => r.json()),
553
+ fetch('https://api.npmjs.org/downloads/point/last-week/@kernel.chat/kbot', { signal: AbortSignal.timeout(3000) })
554
+ .then(r => r.json()),
555
+ fetch('https://api.github.com/repos/isaacsight/kernel', { signal: AbortSignal.timeout(3000) })
556
+ .then(r => r.json()),
557
+ ]);
558
+ if (npmVersionRes.status === 'fulfilled' && npmVersionRes.value.version) {
559
+ latestVersion = npmVersionRes.value.version;
560
+ isLatest = VERSION === latestVersion;
561
+ }
562
+ if (npmDlRes.status === 'fulfilled' && npmDlRes.value.downloads !== undefined) {
563
+ npmDownloads.downloads = npmDlRes.value.downloads;
564
+ }
565
+ if (ghRes.status === 'fulfilled' && ghRes.value.stargazers_count !== undefined) {
566
+ githubData.stars = ghRes.value.stargazers_count;
567
+ githubData.issues = ghRes.value.open_issues_count ?? 0;
568
+ }
569
+ // ── 2. Learning stats ──
570
+ const { getStats } = await import('./learning.js');
571
+ const stats = getStats();
572
+ // ── 3. Collective ──
573
+ const { getOptInState } = await import('./collective.js');
574
+ const collectiveState = getOptInState();
575
+ // ── 4. Tools count ──
576
+ const { getAllTools } = await import('./tools/index.js');
577
+ const toolCount = getAllTools().length || 290; // fallback to known count if tools not yet registered
578
+ // ── 5. Bootstrap (autotelic score) ──
579
+ let bootstrapScore = 0;
580
+ let bootstrapMax = 0;
581
+ let bootstrapGrade = '?';
582
+ let bootstrapTopFix = '';
583
+ let distributionPct = '';
584
+ try {
585
+ const { runBootstrap } = await import('./bootstrap.js');
586
+ const report = await runBootstrap();
587
+ bootstrapScore = report.score;
588
+ bootstrapMax = report.maxScore;
589
+ bootstrapGrade = report.grade;
590
+ bootstrapTopFix = report.topFix;
591
+ const distSection = report.sections.find(s => s.name.toLowerCase().includes('distribution'));
592
+ if (distSection) {
593
+ distributionPct = `${Math.round((distSection.score / distSection.maxScore) * 100)}%`;
594
+ }
595
+ }
596
+ catch { /* bootstrap can fail if not in a project dir */ }
597
+ // ── 6. Cognitive modules — count from known list ──
598
+ const cognitiveModules = [
599
+ 'learning', 'entropy-context', 'autopoiesis', 'free-energy',
600
+ 'predictive-processing', 'reasoning', 'intentionality',
601
+ 'temporal', 'confidence', 'strange-loops', 'integrated-information',
602
+ ];
603
+ const cognitiveCount = cognitiveModules.length; // 11
604
+ // ── JSON output ──
605
+ if (jsonMode) {
606
+ console.log(JSON.stringify({
607
+ version: VERSION,
608
+ latestVersion: latestVersion || null,
609
+ isLatest,
610
+ tools: toolCount,
611
+ agents: 23,
612
+ cognitiveModules: cognitiveCount,
613
+ learning: {
614
+ patterns: stats.patternsCount,
615
+ solutions: stats.solutionsCount,
616
+ tokensSaved: stats.totalTokensSaved,
617
+ efficiency: stats.efficiency,
618
+ },
619
+ collective: {
620
+ enabled: collectiveState.enabled,
621
+ signalsSent: collectiveState.total_signals_sent,
622
+ },
623
+ npm: { weeklyDownloads: npmDownloads.downloads ?? null },
624
+ github: { stars: githubData.stars ?? null, issues: githubData.issues ?? null },
625
+ bootstrap: {
626
+ score: bootstrapScore,
627
+ maxScore: bootstrapMax,
628
+ grade: bootstrapGrade,
629
+ topFix: bootstrapTopFix,
630
+ },
631
+ }, null, 2));
632
+ return;
633
+ }
634
+ // ── Formatted dashboard ──
635
+ const versionTag = latestVersion
636
+ ? (isLatest ? GREEN(' (latest ✓)') : YELLOW(` (update: ${latestVersion})`))
637
+ : DIM(' (offline)');
638
+ const fmtNum = (n) => n.toLocaleString();
639
+ process.stderr.write('\n');
640
+ process.stderr.write(` ${AMETHYST('◉')} ${chalk.bold('Kernel Status')}\n`);
641
+ process.stderr.write(line + '\n');
642
+ process.stderr.write(` ${chalk.bold('Version')} ${VERSION}${versionTag}\n`);
643
+ process.stderr.write(` ${chalk.bold('Tools')} ${fmtNum(toolCount)} ${DIM('|')} ${chalk.bold('Agents')} 23\n`);
644
+ process.stderr.write(` ${chalk.bold('Cognitive')} ${cognitiveCount}/${cognitiveCount} modules active\n`);
645
+ process.stderr.write(line + '\n');
646
+ // Learning
647
+ const tokensSavedStr = stats.totalTokensSaved > 0 ? fmtNum(stats.totalTokensSaved) + ' tokens saved' : 'learning...';
648
+ process.stderr.write(` ${chalk.bold('Learning')} ${fmtNum(stats.patternsCount)} patterns ${DIM('|')} ${fmtNum(stats.solutionsCount)} solutions ${DIM('|')} ${tokensSavedStr}\n`);
649
+ // Collective
650
+ const collectiveEnabled = collectiveState.enabled ? GREEN('enabled') : DIM('disabled');
651
+ const signalsSent = fmtNum(collectiveState.total_signals_sent || 0);
652
+ process.stderr.write(` ${chalk.bold('Collective')} ${collectiveEnabled} ${DIM('|')} ${signalsSent} signals sent\n`);
653
+ process.stderr.write(line + '\n');
654
+ // npm
655
+ if (npmDownloads.downloads !== undefined) {
656
+ process.stderr.write(` ${chalk.bold('npm')} ${CYAN(fmtNum(npmDownloads.downloads))} downloads/week\n`);
657
+ }
658
+ else {
659
+ process.stderr.write(` ${chalk.bold('npm')} ${DIM('unavailable')}\n`);
660
+ }
661
+ // GitHub
662
+ if (githubData.stars !== undefined) {
663
+ const starStr = `${fmtNum(githubData.stars)} star${githubData.stars === 1 ? '' : 's'}`;
664
+ const issueStr = `${fmtNum(githubData.issues)} issue${githubData.issues === 1 ? '' : 's'}`;
665
+ process.stderr.write(` ${chalk.bold('GitHub')} ${starStr} ${DIM('|')} ${issueStr}\n`);
666
+ }
667
+ else {
668
+ process.stderr.write(` ${chalk.bold('GitHub')} ${DIM('unavailable')}\n`);
669
+ }
670
+ // Bootstrap
671
+ if (bootstrapMax > 0) {
672
+ const pct = Math.round((bootstrapScore / bootstrapMax) * 100);
673
+ const gradeColor = pct >= 80 ? GREEN : pct >= 60 ? YELLOW : RED;
674
+ const distStr = distributionPct ? ` ${DIM('— distribution at')} ${distributionPct}` : '';
675
+ process.stderr.write(` ${chalk.bold('Bootstrap')} ${bootstrapScore}/${bootstrapMax} (${gradeColor(bootstrapGrade)})${distStr}\n`);
676
+ }
677
+ process.stderr.write(line + '\n');
678
+ // Next action (top fix from bootstrap)
679
+ if (bootstrapTopFix) {
680
+ process.stderr.write(` ${chalk.bold('Next:')} ${bootstrapTopFix}\n`);
681
+ }
682
+ process.stderr.write('\n');
683
+ });
528
684
  program
529
685
  .command('immune')
530
686
  .description('Self-audit — kbot\'s immune system scans its own code for bugs, security holes, and bad decisions')
@@ -1143,6 +1299,113 @@ async function main() {
1143
1299
  const md = generateChangelog({ since: changelogOpts.since, format: 'markdown' });
1144
1300
  process.stdout.write(md);
1145
1301
  });
1302
+ // ── Automate subcommands ──
1303
+ const automateCmd = program
1304
+ .command('automate')
1305
+ .description('Event-driven automations — file watchers, schedules, git hooks, webhooks');
1306
+ automateCmd
1307
+ .command('list')
1308
+ .description('List all configured automations')
1309
+ .action(async () => {
1310
+ const { listAutomations, formatAutomationList } = await import('./automations.js');
1311
+ const automations = listAutomations();
1312
+ process.stderr.write(formatAutomationList(automations) + '\n');
1313
+ });
1314
+ automateCmd
1315
+ .command('add')
1316
+ .description('Create a new automation')
1317
+ .requiredOption('--trigger <trigger>', 'Trigger spec (e.g., "file:src/**/*.ts:change", "schedule:every 5m", "git:pre-commit", "webhook:/deploy")')
1318
+ .requiredOption('--agent <agent>', 'Agent to run (e.g., coder, researcher, guardian)')
1319
+ .requiredOption('--prompt <prompt>', 'Prompt to send to the agent')
1320
+ .option('--name <name>', 'Human-readable name for the automation')
1321
+ .option('--tools <tools>', 'Comma-separated list of tools to enable')
1322
+ .action(async (addOpts) => {
1323
+ try {
1324
+ const { createAutomation, parseTriggerString } = await import('./automations.js');
1325
+ const trigger = parseTriggerString(addOpts.trigger);
1326
+ const automation = createAutomation({
1327
+ name: addOpts.name || `${trigger.type}:${addOpts.agent}`,
1328
+ trigger,
1329
+ action: {
1330
+ agent: addOpts.agent,
1331
+ prompt: addOpts.prompt,
1332
+ tools: addOpts.tools ? addOpts.tools.split(',').map((t) => t.trim()) : undefined,
1333
+ },
1334
+ });
1335
+ printSuccess(`Automation created: ${automation.name} (${automation.id})`);
1336
+ printInfo(` Trigger: ${addOpts.trigger}`);
1337
+ printInfo(` Agent: ${addOpts.agent}`);
1338
+ if (trigger.type === 'git') {
1339
+ printInfo(' Git hooks installed.');
1340
+ }
1341
+ if (trigger.type === 'file' || trigger.type === 'schedule') {
1342
+ printInfo(' Run `kbot automate start` to activate the daemon.');
1343
+ }
1344
+ }
1345
+ catch (err) {
1346
+ printError(err instanceof Error ? err.message : String(err));
1347
+ process.exit(1);
1348
+ }
1349
+ });
1350
+ automateCmd
1351
+ .command('remove')
1352
+ .description('Remove an automation by ID')
1353
+ .argument('<id>', 'Automation ID')
1354
+ .action(async (id) => {
1355
+ const { removeAutomation } = await import('./automations.js');
1356
+ const removed = removeAutomation(id);
1357
+ if (removed) {
1358
+ printSuccess(`Automation "${id}" removed.`);
1359
+ }
1360
+ else {
1361
+ printError(`Automation "${id}" not found.`);
1362
+ process.exit(1);
1363
+ }
1364
+ });
1365
+ automateCmd
1366
+ .command('run')
1367
+ .description('Manually trigger an automation')
1368
+ .argument('<id>', 'Automation ID')
1369
+ .action(async (id) => {
1370
+ const { runAutomation, getAutomation } = await import('./automations.js');
1371
+ const automation = getAutomation(id);
1372
+ if (!automation) {
1373
+ printError(`Automation "${id}" not found.`);
1374
+ process.exit(1);
1375
+ }
1376
+ printInfo(`Running automation: ${automation.name}...`);
1377
+ const result = await runAutomation(id);
1378
+ if (result.success) {
1379
+ printSuccess(`Automation completed successfully.`);
1380
+ if (result.output) {
1381
+ process.stderr.write('\n' + result.output + '\n');
1382
+ }
1383
+ }
1384
+ else {
1385
+ printError(`Automation failed: ${result.error}`);
1386
+ process.exit(1);
1387
+ }
1388
+ });
1389
+ automateCmd
1390
+ .command('start')
1391
+ .description('Start the automation daemon (file watchers + schedule timers)')
1392
+ .action(async () => {
1393
+ const { startAutomationDaemon, listAutomations } = await import('./automations.js');
1394
+ const automations = listAutomations();
1395
+ if (automations.length === 0) {
1396
+ printError('No automations configured. Use `kbot automate add` first.');
1397
+ process.exit(1);
1398
+ }
1399
+ printInfo('Starting automation daemon...');
1400
+ const { running } = startAutomationDaemon({
1401
+ log: (msg) => process.stderr.write(msg + '\n'),
1402
+ });
1403
+ if (running) {
1404
+ printSuccess('Daemon running. Press Ctrl+C to stop.');
1405
+ // Keep the process alive
1406
+ await new Promise(() => { });
1407
+ }
1408
+ });
1146
1409
  program
1147
1410
  .command('completions')
1148
1411
  .description('Generate shell tab-completion script (bash, zsh, fish)')
@@ -1163,7 +1426,7 @@ async function main() {
1163
1426
  if (opts.quiet)
1164
1427
  setQuiet(true);
1165
1428
  // If a sub-command was run, we're done
1166
- if (['byok', 'auth', 'ide', 'local', 'ollama', 'kbot-local', 'pull', 'doctor', 'serve', 'agents', 'watch', 'voice', 'export', 'plugins', 'changelog', 'completions'].includes(program.args[0]))
1429
+ if (['byok', 'auth', 'ide', 'local', 'ollama', 'kbot-local', 'pull', 'doctor', 'serve', 'agents', 'watch', 'voice', 'export', 'plugins', 'changelog', 'completions', 'automate', 'status'].includes(program.args[0]))
1167
1430
  return;
1168
1431
  // Check for API key (BYOK or local provider)
1169
1432
  let byokActive = isByokEnabled();
@@ -1357,6 +1620,7 @@ async function main() {
1357
1620
  tier,
1358
1621
  thinking: opts.thinking || false,
1359
1622
  thinkingBudget: opts.thinkingBudget ? (parseInt(opts.thinkingBudget, 10) || 10000) : undefined,
1623
+ plan: opts.plan || false,
1360
1624
  };
1361
1625
  // Enable self-evaluation if requested
1362
1626
  if (opts.selfEval) {