@kernel.chat/kbot 3.15.1 → 3.17.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/agents/playtester.d.ts +8 -0
- package/dist/agents/playtester.d.ts.map +1 -0
- package/dist/agents/playtester.js +83 -0
- package/dist/agents/playtester.js.map +1 -0
- package/dist/cli.js +244 -9
- package/dist/cli.js.map +1 -1
- package/dist/consultation.test.d.ts +2 -0
- package/dist/consultation.test.d.ts.map +1 -0
- package/dist/consultation.test.js +86 -0
- package/dist/consultation.test.js.map +1 -0
- package/dist/email-agent.d.ts +33 -0
- package/dist/email-agent.d.ts.map +1 -0
- package/dist/email-agent.js +410 -0
- package/dist/email-agent.js.map +1 -0
- package/dist/imessage-agent.d.ts +28 -0
- package/dist/imessage-agent.d.ts.map +1 -0
- package/dist/imessage-agent.js +274 -0
- package/dist/imessage-agent.js.map +1 -0
- package/dist/init.d.ts +23 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +355 -0
- package/dist/init.js.map +1 -0
- package/dist/init.test.d.ts +2 -0
- package/dist/init.test.d.ts.map +1 -0
- package/dist/init.test.js +89 -0
- package/dist/init.test.js.map +1 -0
- package/dist/matrix.d.ts.map +1 -1
- package/dist/matrix.js +15 -1
- package/dist/matrix.js.map +1 -1
- package/dist/memory-synthesis.test.d.ts +2 -0
- package/dist/memory-synthesis.test.d.ts.map +1 -0
- package/dist/memory-synthesis.test.js +83 -0
- package/dist/memory-synthesis.test.js.map +1 -0
- package/dist/tools/audit.d.ts.map +1 -1
- package/dist/tools/audit.js +18 -1
- package/dist/tools/audit.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const PLAYTESTER_AGENT_ID = "playtester";
|
|
2
|
+
export declare const PLAYTESTER_SYSTEM_PROMPT = "You are kbot's playtester agent \u2014 a brutally honest game tester. You don't sugarcoat. You find problems.\n\n## YOUR JOB\n\nPlay the game mentally by reading the code. Simulate 30 seconds of gameplay in your head. Then report what's broken, what's boring, and what's missing.\n\n## HOW YOU TEST\n\n1. Read scene files \u2014 trace the update loop frame by frame\n2. Read entity code \u2014 check if player/partner/enemies actually behave as designed\n3. Read VFX code \u2014 verify feedback exists for EVERY player action\n4. Read design docs \u2014 compare what's built vs what's promised\n5. Check integration \u2014 are all built systems actually wired into the game loop?\n\n## REPORT FORMAT\n\n### Bug Report:\n```\nBUG: [severity: critical/major/minor]\nWhat: <what's wrong>\nWhere: <file:line>\nExpected: <what should happen>\nActual: <what happens instead>\nFix: <specific code change>\n```\n\n### Feel Report:\n```\nFEEL: [rating: dead/flat/ok/good/great]\nWhat: <moment being evaluated>\nProblem: <why it doesn't feel right>\nReference: <how Hades/Dead Cells/etc handles this>\nFix: <specific improvement>\n```\n\n### Missing Report:\n```\nMISSING: [priority: critical/high/medium/low]\nWhat: <what's not there>\nWhy it matters: <impact on player experience>\n```\n\n## YOUR STANDARDS\n\nYou've played Hades, Dead Cells, Enter the Gungeon, Vampire Survivors, Nuclear Throne. You know what good feels like. You compare the game against those, not against \"it works.\"\n\n### Critical Checklist (fail the build if any are NO):\n- Does the player move within 16ms of input?\n- Does every attack produce visible + audible feedback?\n- Can you tell what every enemy will do by looking at it?\n- Does the partner do something useful without being told?\n- Is there a reason to keep playing after dying?\n- Does the game run at 60fps constant?\n\n### Feel Checklist:\n- Does movement have weight (acceleration, deceleration)?\n- Do hits feel impactful (hitstop, shake, sparks)?\n- Is there contrast (quiet moments vs intense moments)?\n- Does the camera enhance the action?\n- Are there moments of surprise or discovery?\n- Does the partner feel like a character, not a turret?\n\n## YOUR TONE\n\nYou are the player's advocate. You don't care how hard something was to build. You care if it's fun. If the game is boring, say \"this is boring\" and say exactly why. If something is great, acknowledge it briefly and move on \u2014 praise doesn't ship features.";
|
|
3
|
+
export declare const PLAYTESTER_PERSONALITY: {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
traits: string[];
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=playtester.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playtester.d.ts","sourceRoot":"","sources":["../../src/agents/playtester.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,mBAAmB,eAAe,CAAA;AAE/C,eAAO,MAAM,wBAAwB,w6EAgE2N,CAAA;AAEhQ,eAAO,MAAM,sBAAsB;;;;CAUlC,CAAA"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// kbot Playtester Agent — brutally honest game tester
|
|
2
|
+
//
|
|
3
|
+
// Simulates 30 seconds of gameplay by reading code. Reports bugs,
|
|
4
|
+
// feel issues, and missing features. Benchmarks against Hades,
|
|
5
|
+
// Dead Cells, Enter the Gungeon.
|
|
6
|
+
export const PLAYTESTER_AGENT_ID = 'playtester';
|
|
7
|
+
export const PLAYTESTER_SYSTEM_PROMPT = `You are kbot's playtester agent — a brutally honest game tester. You don't sugarcoat. You find problems.
|
|
8
|
+
|
|
9
|
+
## YOUR JOB
|
|
10
|
+
|
|
11
|
+
Play the game mentally by reading the code. Simulate 30 seconds of gameplay in your head. Then report what's broken, what's boring, and what's missing.
|
|
12
|
+
|
|
13
|
+
## HOW YOU TEST
|
|
14
|
+
|
|
15
|
+
1. Read scene files — trace the update loop frame by frame
|
|
16
|
+
2. Read entity code — check if player/partner/enemies actually behave as designed
|
|
17
|
+
3. Read VFX code — verify feedback exists for EVERY player action
|
|
18
|
+
4. Read design docs — compare what's built vs what's promised
|
|
19
|
+
5. Check integration — are all built systems actually wired into the game loop?
|
|
20
|
+
|
|
21
|
+
## REPORT FORMAT
|
|
22
|
+
|
|
23
|
+
### Bug Report:
|
|
24
|
+
\`\`\`
|
|
25
|
+
BUG: [severity: critical/major/minor]
|
|
26
|
+
What: <what's wrong>
|
|
27
|
+
Where: <file:line>
|
|
28
|
+
Expected: <what should happen>
|
|
29
|
+
Actual: <what happens instead>
|
|
30
|
+
Fix: <specific code change>
|
|
31
|
+
\`\`\`
|
|
32
|
+
|
|
33
|
+
### Feel Report:
|
|
34
|
+
\`\`\`
|
|
35
|
+
FEEL: [rating: dead/flat/ok/good/great]
|
|
36
|
+
What: <moment being evaluated>
|
|
37
|
+
Problem: <why it doesn't feel right>
|
|
38
|
+
Reference: <how Hades/Dead Cells/etc handles this>
|
|
39
|
+
Fix: <specific improvement>
|
|
40
|
+
\`\`\`
|
|
41
|
+
|
|
42
|
+
### Missing Report:
|
|
43
|
+
\`\`\`
|
|
44
|
+
MISSING: [priority: critical/high/medium/low]
|
|
45
|
+
What: <what's not there>
|
|
46
|
+
Why it matters: <impact on player experience>
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
## YOUR STANDARDS
|
|
50
|
+
|
|
51
|
+
You've played Hades, Dead Cells, Enter the Gungeon, Vampire Survivors, Nuclear Throne. You know what good feels like. You compare the game against those, not against "it works."
|
|
52
|
+
|
|
53
|
+
### Critical Checklist (fail the build if any are NO):
|
|
54
|
+
- Does the player move within 16ms of input?
|
|
55
|
+
- Does every attack produce visible + audible feedback?
|
|
56
|
+
- Can you tell what every enemy will do by looking at it?
|
|
57
|
+
- Does the partner do something useful without being told?
|
|
58
|
+
- Is there a reason to keep playing after dying?
|
|
59
|
+
- Does the game run at 60fps constant?
|
|
60
|
+
|
|
61
|
+
### Feel Checklist:
|
|
62
|
+
- Does movement have weight (acceleration, deceleration)?
|
|
63
|
+
- Do hits feel impactful (hitstop, shake, sparks)?
|
|
64
|
+
- Is there contrast (quiet moments vs intense moments)?
|
|
65
|
+
- Does the camera enhance the action?
|
|
66
|
+
- Are there moments of surprise or discovery?
|
|
67
|
+
- Does the partner feel like a character, not a turret?
|
|
68
|
+
|
|
69
|
+
## YOUR TONE
|
|
70
|
+
|
|
71
|
+
You are the player's advocate. You don't care how hard something was to build. You care if it's fun. If the game is boring, say "this is boring" and say exactly why. If something is great, acknowledge it briefly and move on — praise doesn't ship features.`;
|
|
72
|
+
export const PLAYTESTER_PERSONALITY = {
|
|
73
|
+
id: 'playtester',
|
|
74
|
+
name: 'Playtester',
|
|
75
|
+
traits: [
|
|
76
|
+
'Brutally honest about game quality',
|
|
77
|
+
'Benchmarks against best-in-class (Hades, Dead Cells)',
|
|
78
|
+
'Player advocate — fun over technical achievement',
|
|
79
|
+
'Produces actionable bug/feel/missing reports',
|
|
80
|
+
'Never sugarcoats, but always offers specific fixes',
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=playtester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playtester.js","sourceRoot":"","sources":["../../src/agents/playtester.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,kEAAkE;AAClE,+DAA+D;AAC/D,iCAAiC;AAEjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAA;AAE/C,MAAM,CAAC,MAAM,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gQAgEwN,CAAA;AAEhQ,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,YAAY;IAClB,MAAM,EAAE;QACN,oCAAoC;QACpC,sDAAsD;QACtD,kDAAkD;QAClD,8CAA8C;QAC9C,oDAAoD;KACrD;CACF,CAAA"}
|
package/dist/cli.js
CHANGED
|
@@ -35,7 +35,7 @@ async function main() {
|
|
|
35
35
|
.name('kbot')
|
|
36
36
|
.description('kbot — Open-source terminal AI agent. Bring your own key, pick your model, run locally.')
|
|
37
37
|
.version(VERSION)
|
|
38
|
-
.option('-a, --agent <agent>', 'Force a specific agent (run kbot agents to see all
|
|
38
|
+
.option('-a, --agent <agent>', 'Force a specific agent (run kbot agents to see all 25)')
|
|
39
39
|
.option('-m, --model <model>', 'Override AI model (auto, sonnet, haiku)')
|
|
40
40
|
.option('-s, --stream', 'Stream the response')
|
|
41
41
|
.option('-p, --pipe', 'Pipe mode — raw text output for scripting')
|
|
@@ -360,6 +360,40 @@ async function main() {
|
|
|
360
360
|
printInfo('Download: kbot models pull <name>');
|
|
361
361
|
printInfo('Or use any HuggingFace GGUF: kbot models pull hf:user/repo:file.gguf');
|
|
362
362
|
});
|
|
363
|
+
program
|
|
364
|
+
.command('init')
|
|
365
|
+
.description('Set up kbot for this project — detects stack, creates tools, writes config (60 seconds)')
|
|
366
|
+
.option('--force', 'Overwrite existing .kbot.json')
|
|
367
|
+
.action(async (opts) => {
|
|
368
|
+
const { existsSync } = await import('node:fs');
|
|
369
|
+
const { join } = await import('node:path');
|
|
370
|
+
const { initProject, formatInitReport } = await import('./init.js');
|
|
371
|
+
const chalk = (await import('chalk')).default;
|
|
372
|
+
const AMETHYST = chalk.hex('#6B5B95');
|
|
373
|
+
const root = process.cwd();
|
|
374
|
+
// Check for existing config
|
|
375
|
+
if (!opts.force && existsSync(join(root, '.kbot.json'))) {
|
|
376
|
+
printWarn('This project already has a .kbot.json. Use --force to overwrite.');
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
process.stderr.write('\n');
|
|
380
|
+
process.stderr.write(` ${AMETHYST('◉')} ${chalk.bold('kbot init')} — scanning project...\n\n`);
|
|
381
|
+
const config = await initProject(root);
|
|
382
|
+
process.stderr.write(formatInitReport(config) + '\n');
|
|
383
|
+
process.stderr.write('\n');
|
|
384
|
+
process.stderr.write(` ${chalk.green('✓')} Config written to ${chalk.dim('.kbot.json')}\n`);
|
|
385
|
+
if (config.forgedTools.length > 0) {
|
|
386
|
+
process.stderr.write(` ${chalk.green('✓')} ${config.forgedTools.length} tools forged: ${chalk.dim(config.forgedTools.join(', '))}\n`);
|
|
387
|
+
}
|
|
388
|
+
process.stderr.write('\n');
|
|
389
|
+
process.stderr.write(` ${chalk.bold('Try now:')}\n`);
|
|
390
|
+
process.stderr.write(` ${chalk.cyan('kbot')} "explain this project"\n`);
|
|
391
|
+
process.stderr.write(` ${chalk.cyan('kbot')} "find the top bug"\n`);
|
|
392
|
+
if (config.commands.test) {
|
|
393
|
+
process.stderr.write(` ${chalk.cyan('kbot')} "run the tests and fix any failures"\n`);
|
|
394
|
+
}
|
|
395
|
+
process.stderr.write('\n');
|
|
396
|
+
});
|
|
363
397
|
program
|
|
364
398
|
.command('doctor')
|
|
365
399
|
.description('Diagnose your kbot setup — check everything is working')
|
|
@@ -660,7 +694,7 @@ async function main() {
|
|
|
660
694
|
latestVersion: latestVersion || null,
|
|
661
695
|
isLatest,
|
|
662
696
|
tools: toolCount,
|
|
663
|
-
agents:
|
|
697
|
+
agents: 25,
|
|
664
698
|
cognitiveModules: cognitiveCount,
|
|
665
699
|
learning: {
|
|
666
700
|
patterns: stats.patternsCount,
|
|
@@ -692,7 +726,7 @@ async function main() {
|
|
|
692
726
|
process.stderr.write(` ${AMETHYST('◉')} ${chalk.bold('Kernel Status')}\n`);
|
|
693
727
|
process.stderr.write(line + '\n');
|
|
694
728
|
process.stderr.write(` ${chalk.bold('Version')} ${VERSION}${versionTag}\n`);
|
|
695
|
-
process.stderr.write(` ${chalk.bold('Tools')} ${fmtNum(toolCount)} ${DIM('|')} ${chalk.bold('Agents')}
|
|
729
|
+
process.stderr.write(` ${chalk.bold('Tools')} ${fmtNum(toolCount)} ${DIM('|')} ${chalk.bold('Agents')} 25\n`);
|
|
696
730
|
process.stderr.write(` ${chalk.bold('Cognitive')} ${cognitiveCount}/${cognitiveCount} modules active\n`);
|
|
697
731
|
process.stderr.write(line + '\n');
|
|
698
732
|
// Learning
|
|
@@ -1393,6 +1427,191 @@ async function main() {
|
|
|
1393
1427
|
printError('Voice mode not available on this platform');
|
|
1394
1428
|
}
|
|
1395
1429
|
});
|
|
1430
|
+
// ── Email Agent ──
|
|
1431
|
+
const emailAgentCmd = program
|
|
1432
|
+
.command('email-agent')
|
|
1433
|
+
.description('Autonomous email companion agent — responds to emails via local AI ($0 cost)');
|
|
1434
|
+
emailAgentCmd
|
|
1435
|
+
.command('start')
|
|
1436
|
+
.description('Start the email agent — polls for new emails and responds via Ollama')
|
|
1437
|
+
.option('--model <model>', 'Ollama model to use', 'qwen2.5-coder:32b')
|
|
1438
|
+
.option('--interval <ms>', 'Poll interval in milliseconds', '15000')
|
|
1439
|
+
.option('--users <emails>', 'Comma-separated email addresses to monitor (omit for open mode — all inbound)')
|
|
1440
|
+
.option('--open', 'Open mode — respond to ALL inbound emails (no whitelist)')
|
|
1441
|
+
.action(async (opts) => {
|
|
1442
|
+
const { startEmailAgent } = await import('./email-agent.js');
|
|
1443
|
+
const { existsSync, readFileSync } = await import('node:fs');
|
|
1444
|
+
const { join } = await import('node:path');
|
|
1445
|
+
// Load env
|
|
1446
|
+
let supabaseUrl = '', supabaseKey = '', resendKey = '';
|
|
1447
|
+
const envPaths = [join(process.cwd(), '.env'), join(process.env.HOME || '', '.kbot', '.env')];
|
|
1448
|
+
for (const envPath of envPaths) {
|
|
1449
|
+
if (existsSync(envPath)) {
|
|
1450
|
+
const env = readFileSync(envPath, 'utf8');
|
|
1451
|
+
const get = (k) => env.match(new RegExp(`^${k}=(.+)$`, 'm'))?.[1]?.trim() ?? '';
|
|
1452
|
+
supabaseUrl = supabaseUrl || get('VITE_SUPABASE_URL');
|
|
1453
|
+
supabaseKey = supabaseKey || get('SUPABASE_SERVICE_KEY');
|
|
1454
|
+
resendKey = resendKey || get('RESEND_API_KEY');
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
if (!supabaseUrl || !supabaseKey) {
|
|
1458
|
+
printError('Missing VITE_SUPABASE_URL or SUPABASE_SERVICE_KEY in .env');
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
if (!resendKey) {
|
|
1462
|
+
printError('Missing RESEND_API_KEY in .env — needed to send reply emails');
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
const agentUsers = opts.users?.split(',').map(e => e.trim()) || [];
|
|
1466
|
+
if (agentUsers.length === 0 && !opts.open) {
|
|
1467
|
+
printError('No users specified. Use --users email1,email2 or --open for all inbound.');
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
const chalk = (await import('chalk')).default;
|
|
1471
|
+
const mode = agentUsers.length === 0 ? 'OPEN (all inbound)' : `${agentUsers.length} users`;
|
|
1472
|
+
console.log();
|
|
1473
|
+
console.log(chalk.hex('#6B5B95')(' ◉ Kernel Email Agent'));
|
|
1474
|
+
console.log(chalk.dim(` Model: ${opts.model || 'qwen2.5-coder:32b'}`));
|
|
1475
|
+
console.log(chalk.dim(` Mode: ${mode}`));
|
|
1476
|
+
console.log(chalk.dim(` Poll interval: ${Number(opts.interval || 15000) / 1000}s`));
|
|
1477
|
+
console.log();
|
|
1478
|
+
await startEmailAgent({
|
|
1479
|
+
supabaseUrl,
|
|
1480
|
+
supabaseKey,
|
|
1481
|
+
resendKey,
|
|
1482
|
+
ollamaUrl: 'http://localhost:11434',
|
|
1483
|
+
ollamaModel: opts.model || 'qwen2.5-coder:32b',
|
|
1484
|
+
pollInterval: Number(opts.interval || 15000),
|
|
1485
|
+
agentUsers,
|
|
1486
|
+
});
|
|
1487
|
+
// Keep process alive
|
|
1488
|
+
await new Promise(() => { });
|
|
1489
|
+
});
|
|
1490
|
+
emailAgentCmd
|
|
1491
|
+
.command('status')
|
|
1492
|
+
.description('Show email agent status')
|
|
1493
|
+
.action(async () => {
|
|
1494
|
+
const { getEmailAgentState } = await import('./email-agent.js');
|
|
1495
|
+
const state = getEmailAgentState();
|
|
1496
|
+
if (state.running) {
|
|
1497
|
+
printSuccess(`Email agent is running`);
|
|
1498
|
+
printInfo(` Processed: ${state.processedCount} emails`);
|
|
1499
|
+
printInfo(` Last check: ${state.lastCheck || 'never'}`);
|
|
1500
|
+
if (state.errors.length > 0) {
|
|
1501
|
+
printWarn(` Recent errors: ${state.errors.length}`);
|
|
1502
|
+
for (const err of state.errors.slice(-3)) {
|
|
1503
|
+
printError(` ${err}`);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
else {
|
|
1508
|
+
printInfo('Email agent is not running. Start with: kbot email-agent start --open');
|
|
1509
|
+
}
|
|
1510
|
+
});
|
|
1511
|
+
// ── iMessage Agent ──
|
|
1512
|
+
const imessageCmd = program
|
|
1513
|
+
.command('imessage-agent')
|
|
1514
|
+
.description('Free iMessage/SMS agent via macOS Messages.app ($0 cost, unlimited)');
|
|
1515
|
+
imessageCmd
|
|
1516
|
+
.command('start')
|
|
1517
|
+
.description('Start monitoring iMessage — responds via local Ollama')
|
|
1518
|
+
.option('--model <model>', 'Ollama model to use', 'qwen2.5-coder:32b')
|
|
1519
|
+
.option('--interval <ms>', 'Poll interval in milliseconds', '10000')
|
|
1520
|
+
.option('--numbers <nums>', 'Comma-separated phone numbers to monitor (e.g., +17145551234)')
|
|
1521
|
+
.action(async (opts) => {
|
|
1522
|
+
const { platform } = await import('node:os');
|
|
1523
|
+
if (platform() !== 'darwin') {
|
|
1524
|
+
printError('iMessage agent is only available on macOS');
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
const numbers = opts.numbers?.split(',').map(n => n.trim()) || [];
|
|
1528
|
+
if (numbers.length === 0) {
|
|
1529
|
+
printError('No phone numbers specified. Use --numbers +17145551234,+12135559876');
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
// Optional Supabase for logging
|
|
1533
|
+
let supabaseUrl = '', supabaseKey = '';
|
|
1534
|
+
const { existsSync, readFileSync } = await import('node:fs');
|
|
1535
|
+
const { join } = await import('node:path');
|
|
1536
|
+
const envPaths = [join(process.cwd(), '.env'), join(process.env.HOME || '', '.kbot', '.env')];
|
|
1537
|
+
for (const envPath of envPaths) {
|
|
1538
|
+
if (existsSync(envPath)) {
|
|
1539
|
+
const env = readFileSync(envPath, 'utf8');
|
|
1540
|
+
const get = (k) => env.match(new RegExp(`^${k}=(.+)$`, 'm'))?.[1]?.trim() ?? '';
|
|
1541
|
+
supabaseUrl = supabaseUrl || get('VITE_SUPABASE_URL');
|
|
1542
|
+
supabaseKey = supabaseKey || get('SUPABASE_SERVICE_KEY');
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
const chalk = (await import('chalk')).default;
|
|
1546
|
+
console.log();
|
|
1547
|
+
console.log(chalk.hex('#6B5B95')(' ◉ Kernel iMessage Agent'));
|
|
1548
|
+
console.log(chalk.dim(` Model: ${opts.model || 'qwen2.5-coder:32b'}`));
|
|
1549
|
+
console.log(chalk.dim(` Monitoring: ${numbers.join(', ')}`));
|
|
1550
|
+
console.log(chalk.dim(` Poll interval: ${Number(opts.interval || 10000) / 1000}s`));
|
|
1551
|
+
if (supabaseUrl)
|
|
1552
|
+
console.log(chalk.dim(' Logging to Supabase: yes'));
|
|
1553
|
+
console.log();
|
|
1554
|
+
const { startIMessageAgent } = await import('./imessage-agent.js');
|
|
1555
|
+
await startIMessageAgent({
|
|
1556
|
+
ollamaUrl: 'http://localhost:11434',
|
|
1557
|
+
ollamaModel: opts.model || 'qwen2.5-coder:32b',
|
|
1558
|
+
pollInterval: Number(opts.interval || 10000),
|
|
1559
|
+
numbers,
|
|
1560
|
+
supabaseUrl: supabaseUrl || undefined,
|
|
1561
|
+
supabaseKey: supabaseKey || undefined,
|
|
1562
|
+
});
|
|
1563
|
+
// Keep process alive
|
|
1564
|
+
await new Promise(() => { });
|
|
1565
|
+
});
|
|
1566
|
+
imessageCmd
|
|
1567
|
+
.command('status')
|
|
1568
|
+
.description('Show iMessage agent status')
|
|
1569
|
+
.action(async () => {
|
|
1570
|
+
const { getIMessageAgentState } = await import('./imessage-agent.js');
|
|
1571
|
+
const state = getIMessageAgentState();
|
|
1572
|
+
if (state.running) {
|
|
1573
|
+
printSuccess('iMessage agent is running');
|
|
1574
|
+
printInfo(` Messages processed: ${state.messagesProcessed}`);
|
|
1575
|
+
printInfo(` Last check: ${state.lastCheck || 'never'}`);
|
|
1576
|
+
if (state.errors.length > 0) {
|
|
1577
|
+
printWarn(` Recent errors: ${state.errors.length}`);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
printInfo('iMessage agent is not running. Start with: kbot imessage-agent start --numbers +1234567890');
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
// ── Consultation ──
|
|
1585
|
+
program
|
|
1586
|
+
.command('consultation')
|
|
1587
|
+
.description('Consultation engine — domain guardrails, intake, client management')
|
|
1588
|
+
.option('--check <message>', 'Check if a message hits domain guardrails')
|
|
1589
|
+
.option('--intake', 'Generate the intake questionnaire')
|
|
1590
|
+
.action(async (opts) => {
|
|
1591
|
+
const { checkDomainGuardrails, getIntakeMessage } = await import('./consultation.js');
|
|
1592
|
+
if (opts.check) {
|
|
1593
|
+
const result = checkDomainGuardrails(opts.check);
|
|
1594
|
+
if (result.blocked) {
|
|
1595
|
+
printWarn(`Blocked — ${result.domain} domain`);
|
|
1596
|
+
printInfo(result.message || '');
|
|
1597
|
+
if (result.suggestedTopic)
|
|
1598
|
+
printInfo(`Suggested redirect: ${result.suggestedTopic}`);
|
|
1599
|
+
}
|
|
1600
|
+
else {
|
|
1601
|
+
printSuccess('Message passes domain guardrails');
|
|
1602
|
+
}
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
if (opts.intake) {
|
|
1606
|
+
console.log(getIntakeMessage());
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
// Default: show help
|
|
1610
|
+
printInfo('Kernel Consultation Engine');
|
|
1611
|
+
printInfo('');
|
|
1612
|
+
printInfo(' kbot consultation --check "message" Check domain guardrails');
|
|
1613
|
+
printInfo(' kbot consultation --intake Generate intake questions');
|
|
1614
|
+
});
|
|
1396
1615
|
program
|
|
1397
1616
|
.command('export <session>')
|
|
1398
1617
|
.description('Export a saved session to markdown, JSON, or HTML')
|
|
@@ -1444,24 +1663,40 @@ async function main() {
|
|
|
1444
1663
|
});
|
|
1445
1664
|
program
|
|
1446
1665
|
.command('audit <repo>')
|
|
1447
|
-
.description('Full audit of any GitHub repository — security, quality, docs, DevOps')
|
|
1448
|
-
.option('--share', '
|
|
1666
|
+
.description('Full audit of any GitHub repository — security, quality, docs, DevOps. Generates shareable report with badge.')
|
|
1667
|
+
.option('--share', 'Auto-share the report as a public GitHub Gist')
|
|
1668
|
+
.option('--json', 'Output raw JSON')
|
|
1669
|
+
.option('--badge', 'Print only the badge markdown (for adding to READMEs)')
|
|
1449
1670
|
.action(async (repo, auditOpts) => {
|
|
1450
1671
|
const { auditRepo, formatAuditReport } = await import('./tools/audit.js');
|
|
1451
1672
|
printInfo(`Auditing ${repo}...`);
|
|
1452
1673
|
try {
|
|
1453
1674
|
const result = await auditRepo(repo);
|
|
1675
|
+
if (auditOpts.json) {
|
|
1676
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
if (auditOpts.badge) {
|
|
1680
|
+
const pct = Math.round((result.score / result.maxScore) * 100);
|
|
1681
|
+
const badgeColor = pct >= 80 ? 'brightgreen' : pct >= 60 ? 'yellow' : 'red';
|
|
1682
|
+
console.log(`[-${badgeColor})](https://www.npmjs.com/package/@kernel.chat/kbot)`);
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1454
1685
|
const report = formatAuditReport(result);
|
|
1455
1686
|
console.log(report);
|
|
1687
|
+
// Auto-share as gist
|
|
1456
1688
|
if (auditOpts.share) {
|
|
1457
|
-
const { shareConversation } = await import('./share.js');
|
|
1458
|
-
// Save as a pseudo-conversation for sharing
|
|
1459
1689
|
printInfo('Sharing audit report...');
|
|
1460
1690
|
try {
|
|
1461
1691
|
const { createGist } = await import('./share.js');
|
|
1462
|
-
const url = createGist(report, `kbot-audit-${repo.replace('/', '-')}.md`, `kbot Audit: ${repo}`, true);
|
|
1463
|
-
if (url?.startsWith('http'))
|
|
1692
|
+
const url = createGist(report, `kbot-audit-${repo.replace('/', '-')}.md`, `kbot Audit: ${repo} — Grade ${result.grade}`, true);
|
|
1693
|
+
if (url?.startsWith('http')) {
|
|
1464
1694
|
printSuccess(`Shared! ${url}`);
|
|
1695
|
+
printInfo(`Badge for ${repo}'s README:`);
|
|
1696
|
+
const pct = Math.round((result.score / result.maxScore) * 100);
|
|
1697
|
+
const badgeColor = pct >= 80 ? 'brightgreen' : pct >= 60 ? 'yellow' : 'red';
|
|
1698
|
+
printInfo(` [-${badgeColor})](${url})`);
|
|
1699
|
+
}
|
|
1465
1700
|
}
|
|
1466
1701
|
catch {
|
|
1467
1702
|
printInfo('Could not create Gist. Install GitHub CLI: brew install gh');
|