@kernel.chat/kbot 3.65.0 → 3.67.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/README.md +35 -6
- package/dist/agent.js +12 -0
- package/dist/cli.js +127 -5
- package/dist/collective-dreams.d.ts +36 -0
- package/dist/collective-dreams.js +182 -0
- package/dist/dream.d.ts +2 -1
- package/dist/dream.js +107 -3
- package/dist/prompt-evolution.d.ts +9 -0
- package/dist/prompt-evolution.js +28 -0
- package/dist/tools/audit.d.ts +2 -1
- package/dist/tools/audit.js +127 -4
- package/dist/tools/behavior-tools.d.ts +2 -0
- package/dist/tools/behavior-tools.js +63 -0
- package/dist/tools/collective-dream-tools.d.ts +2 -0
- package/dist/tools/collective-dream-tools.js +109 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/watchdog.d.ts +32 -0
- package/dist/tools/watchdog.js +356 -0
- package/dist/user-behavior.d.ts +65 -0
- package/dist/user-behavior.js +301 -0
- package/package.json +1 -1
package/dist/dream.js
CHANGED
|
@@ -21,6 +21,9 @@ import { getHistory } from './memory.js';
|
|
|
21
21
|
import { loadMemory } from './memory.js';
|
|
22
22
|
import { getTopPatterns, getTopSolutions, getProfileSummary, updateProfile, recordPattern, learnFact, } from './learning.js';
|
|
23
23
|
import { getMemoryScannerStats } from './memory-scanner.js';
|
|
24
|
+
import { getLearningReport as getMusicLearningReport, getRecentHistory as getMusicRecentHistory, getPreferences as getMusicPreferences, } from './music-learning.js';
|
|
25
|
+
import { getBehaviorForDream } from './user-behavior.js';
|
|
26
|
+
import { registerAmendment } from './prompt-evolution.js';
|
|
24
27
|
// ── Constants ──
|
|
25
28
|
const DREAM_DIR = join(homedir(), '.kbot', 'memory', 'dreams');
|
|
26
29
|
const JOURNAL_FILE = join(DREAM_DIR, 'journal.json');
|
|
@@ -171,6 +174,20 @@ function buildConsolidationPrompt(sessionHistory, existingInsights, existingMemo
|
|
|
171
174
|
.map(d => `- [${d.kind}] ${d.content.slice(0, 150)} (confidence: ${Math.round(d.confidence * 100)}%)`)
|
|
172
175
|
.join('\n')
|
|
173
176
|
: '(no recent detections)';
|
|
177
|
+
// ── Tier 6: Music learning — sound, pattern, mix memories ──
|
|
178
|
+
const musicPrefs = getMusicPreferences();
|
|
179
|
+
const hasMusicData = musicPrefs.totalBeats > 0 || musicPrefs.totalSessions > 0;
|
|
180
|
+
let musicText = '(no music production data yet)';
|
|
181
|
+
if (hasMusicData) {
|
|
182
|
+
const report = getMusicLearningReport();
|
|
183
|
+
const recentEvents = getMusicRecentHistory(10);
|
|
184
|
+
const recentText = recentEvents.length > 0
|
|
185
|
+
? recentEvents.map(e => `- [${e.action}] ${e.genre} ${e.key} ${e.bpm}bpm — ${e.detail || 'no detail'} (${e.feedback})`).join('\n')
|
|
186
|
+
: '';
|
|
187
|
+
musicText = report + (recentText ? `\n\n**Recent production events:**\n${recentText}` : '');
|
|
188
|
+
}
|
|
189
|
+
// ── Tier 7: User computer behavior — desktop observation ──
|
|
190
|
+
const behaviorText = getBehaviorForDream(48) || '(no behavior data yet)';
|
|
174
191
|
return `You are a memory consolidation system. Analyze this conversation session and ALL accumulated knowledge tiers to extract durable cross-tier insights.
|
|
175
192
|
|
|
176
193
|
EXISTING DREAM INSIGHTS (Tier 4 — Dream Journal):
|
|
@@ -191,6 +208,12 @@ ${profileText}
|
|
|
191
208
|
RECENT MEMORY SCANNER DETECTIONS (Tier 5 — Passive Detection):
|
|
192
209
|
${scannerText}
|
|
193
210
|
|
|
211
|
+
MUSIC PRODUCTION LEARNING (Tier 6 — Musical Memory):
|
|
212
|
+
${musicText}
|
|
213
|
+
|
|
214
|
+
USER COMPUTER BEHAVIOR (Tier 7 — Desktop Observation):
|
|
215
|
+
${behaviorText}
|
|
216
|
+
|
|
194
217
|
SESSION TO CONSOLIDATE:
|
|
195
218
|
${historyText}
|
|
196
219
|
|
|
@@ -201,6 +224,21 @@ Extract 1-5 insights by synthesizing across ALL tiers. Each insight should be:
|
|
|
201
224
|
- Cross-tier when possible (e.g., a pattern + preference = workflow insight)
|
|
202
225
|
- About the USER, their preferences, patterns, workflows, or project context
|
|
203
226
|
|
|
227
|
+
For music/production sessions, pay special attention to:
|
|
228
|
+
- Which sounds, instruments, and presets scored well and why
|
|
229
|
+
- BPM + key + genre combinations that the user gravitates toward
|
|
230
|
+
- Mix decisions that worked (volume balance, send levels, panning)
|
|
231
|
+
- Production patterns (e.g., "808 sub bass in F1 works well at 142 BPM for trap")
|
|
232
|
+
- Cross-domain insights (e.g., coding workflow preferences that mirror production habits)
|
|
233
|
+
Use category "music" for production-specific insights.
|
|
234
|
+
|
|
235
|
+
For desktop behavior data, look for:
|
|
236
|
+
- Workflow patterns (which apps are always open together, e.g., IDE + terminal + browser)
|
|
237
|
+
- Productivity habits (active hours, app switching frequency)
|
|
238
|
+
- Context-switching tendencies (many apps vs focused few)
|
|
239
|
+
- Tool preferences (which creative/dev tools dominate)
|
|
240
|
+
- Cross-domain insights (e.g., "user switches to music production apps in evening hours")
|
|
241
|
+
|
|
204
242
|
Pay special attention to:
|
|
205
243
|
- Patterns that confirm or contradict existing insights
|
|
206
244
|
- Scanner corrections that reveal unrecognized preferences
|
|
@@ -211,7 +249,7 @@ Format each insight as a JSON array of objects:
|
|
|
211
249
|
[
|
|
212
250
|
{
|
|
213
251
|
"content": "the insight text",
|
|
214
|
-
"category": "pattern|preference|skill|project|relationship",
|
|
252
|
+
"category": "pattern|preference|skill|project|relationship|music",
|
|
215
253
|
"keywords": ["keyword1", "keyword2"]
|
|
216
254
|
}
|
|
217
255
|
]
|
|
@@ -256,6 +294,7 @@ export function applyDreamInsights(insights) {
|
|
|
256
294
|
preferencesApplied: 0,
|
|
257
295
|
patternsHinted: 0,
|
|
258
296
|
factsLearned: 0,
|
|
297
|
+
promptAmendments: 0,
|
|
259
298
|
};
|
|
260
299
|
for (const insight of insights) {
|
|
261
300
|
switch (insight.category) {
|
|
@@ -276,6 +315,18 @@ export function applyDreamInsights(insights) {
|
|
|
276
315
|
if (techTerms.length > 0) {
|
|
277
316
|
updateProfile({ techTerms });
|
|
278
317
|
}
|
|
318
|
+
// High-relevance preference insights → prompt evolution amendments.
|
|
319
|
+
// This wires dream consolidation into the GEPA prompt evolution system
|
|
320
|
+
// so kbot's specialist prompts self-improve based on learned behavior.
|
|
321
|
+
if (insight.relevance > 0.8) {
|
|
322
|
+
const amendment = insightToAmendment(insight.content);
|
|
323
|
+
if (amendment) {
|
|
324
|
+
// Target the 'kernel' agent (general) — the preference applies broadly.
|
|
325
|
+
// Tag with the dream insight ID for traceability / rollback.
|
|
326
|
+
registerAmendment('kernel', amendment, `dream preference: ${insight.content.slice(0, 80)}`, insight.id);
|
|
327
|
+
result.promptAmendments++;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
279
330
|
result.preferencesApplied++;
|
|
280
331
|
break;
|
|
281
332
|
}
|
|
@@ -312,10 +363,60 @@ export function applyDreamInsights(insights) {
|
|
|
312
363
|
result.factsLearned++;
|
|
313
364
|
break;
|
|
314
365
|
}
|
|
366
|
+
case 'music': {
|
|
367
|
+
// Music production insights → context facts.
|
|
368
|
+
// These capture durable knowledge like "808 sub in F1 at 142 BPM works for trap"
|
|
369
|
+
// and feed back into the learning system for future prompt enrichment.
|
|
370
|
+
learnFact(insight.content, 'context', 'observed');
|
|
371
|
+
result.factsLearned++;
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
315
374
|
}
|
|
316
375
|
}
|
|
317
376
|
return result;
|
|
318
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* Convert a preference insight into a concrete prompt amendment.
|
|
380
|
+
* Maps common preference patterns to actionable instructions for the agent.
|
|
381
|
+
* Returns null if no actionable amendment can be derived.
|
|
382
|
+
*/
|
|
383
|
+
function insightToAmendment(insightContent) {
|
|
384
|
+
const lower = insightContent.toLowerCase();
|
|
385
|
+
// Speed / action-oriented preferences
|
|
386
|
+
if (/\b(?:speed|fast|quick|ship|action|just do it|don't ask)\b/.test(lower)) {
|
|
387
|
+
return 'Be action-oriented. Ship first, polish later. Don\'t ask for permission on non-destructive operations.';
|
|
388
|
+
}
|
|
389
|
+
// Conciseness preferences
|
|
390
|
+
if (/\b(?:concise|brief|short|terse|no fluff|straight to the point)\b/.test(lower)) {
|
|
391
|
+
return 'Keep responses concise. Lead with the answer or action. Skip preambles and restating the question.';
|
|
392
|
+
}
|
|
393
|
+
// Detail / thoroughness preferences
|
|
394
|
+
if (/\b(?:detailed|thorough|explain|verbose|show work|step.by.step)\b/.test(lower)) {
|
|
395
|
+
return 'Be thorough in explanations. Show reasoning steps. Include context and alternatives when relevant.';
|
|
396
|
+
}
|
|
397
|
+
// Code-first preferences
|
|
398
|
+
if (/\b(?:code.first|show.code|less.talk|implementation|no.theory)\b/.test(lower)) {
|
|
399
|
+
return 'Lead with code. Show the implementation first, then explain only if the user asks.';
|
|
400
|
+
}
|
|
401
|
+
// Terminal / CLI preferences
|
|
402
|
+
if (/\b(?:terminal|cli|command.line|shell|no.gui|no.web)\b/.test(lower)) {
|
|
403
|
+
return 'Prefer terminal-based solutions. Use CLI tools over web interfaces. Everything should be scriptable.';
|
|
404
|
+
}
|
|
405
|
+
// Safety / careful preferences
|
|
406
|
+
if (/\b(?:careful|safe|confirm|check.first|verify|double.check)\b/.test(lower)) {
|
|
407
|
+
return 'Verify before acting. Confirm destructive operations. Read files before editing. Run builds after changes.';
|
|
408
|
+
}
|
|
409
|
+
// Exploration / creative preferences
|
|
410
|
+
if (/\b(?:creative|explore|try.new|experiment|novel|unconventional)\b/.test(lower)) {
|
|
411
|
+
return 'Explore creative solutions. Consider unconventional approaches. Suggest alternatives the user might not have considered.';
|
|
412
|
+
}
|
|
413
|
+
// Fallback: use the insight content directly as a general behavioral nudge
|
|
414
|
+
// Only if the insight is short enough to be a useful prompt instruction
|
|
415
|
+
if (insightContent.length <= 200) {
|
|
416
|
+
return `User preference: ${insightContent}`;
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
319
420
|
/** Extract tech-related terms from insight text */
|
|
320
421
|
function extractTechTermsFromInsight(text) {
|
|
321
422
|
const techTerms = new Set([
|
|
@@ -416,7 +517,9 @@ export async function dream(sessionId = 'default') {
|
|
|
416
517
|
const newlyCreatedInsights = [];
|
|
417
518
|
if (rawInsights) {
|
|
418
519
|
try {
|
|
419
|
-
|
|
520
|
+
// Strip markdown code fences if Ollama wraps the JSON
|
|
521
|
+
const cleaned = rawInsights.replace(/^```(?:json)?\s*\n?/i, '').replace(/\n?```\s*$/i, '').trim();
|
|
522
|
+
const parsed = JSON.parse(cleaned);
|
|
420
523
|
if (Array.isArray(parsed)) {
|
|
421
524
|
const now = new Date().toISOString();
|
|
422
525
|
for (const p of parsed.slice(0, 5)) {
|
|
@@ -452,7 +555,8 @@ export async function dream(sessionId = 'default') {
|
|
|
452
555
|
const rawReinforce = await ollamaGenerate(reinforcePrompt);
|
|
453
556
|
if (rawReinforce) {
|
|
454
557
|
try {
|
|
455
|
-
const
|
|
558
|
+
const cleanedR = rawReinforce.replace(/^```(?:json)?\s*\n?/i, '').replace(/\n?```\s*$/i, '').trim();
|
|
559
|
+
const indices = JSON.parse(cleanedR);
|
|
456
560
|
if (Array.isArray(indices)) {
|
|
457
561
|
const now = new Date().toISOString();
|
|
458
562
|
for (const idx of indices) {
|
|
@@ -85,6 +85,15 @@ export declare function getEvolutionStats(): {
|
|
|
85
85
|
* Useful for debugging or when a major prompt rewrite happens.
|
|
86
86
|
*/
|
|
87
87
|
export declare function resetEvolution(agent?: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* Register an external prompt amendment (e.g. from the dream engine).
|
|
90
|
+
* This allows other subsystems to inject mutations into the evolution state
|
|
91
|
+
* without going through the trace-based evolvePrompt() pipeline.
|
|
92
|
+
*
|
|
93
|
+
* The mutation is tagged with an optional sourceId for traceability
|
|
94
|
+
* (e.g. the dream insight ID that produced it).
|
|
95
|
+
*/
|
|
96
|
+
export declare function registerAmendment(agent: string, amendment: string, reason: string, sourceId?: string): PromptMutation;
|
|
88
97
|
/**
|
|
89
98
|
* Flush pending state to disk. Call on process exit.
|
|
90
99
|
*/
|
package/dist/prompt-evolution.js
CHANGED
|
@@ -375,6 +375,34 @@ export function resetEvolution(agent) {
|
|
|
375
375
|
}
|
|
376
376
|
persist();
|
|
377
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* Register an external prompt amendment (e.g. from the dream engine).
|
|
380
|
+
* This allows other subsystems to inject mutations into the evolution state
|
|
381
|
+
* without going through the trace-based evolvePrompt() pipeline.
|
|
382
|
+
*
|
|
383
|
+
* The mutation is tagged with an optional sourceId for traceability
|
|
384
|
+
* (e.g. the dream insight ID that produced it).
|
|
385
|
+
*/
|
|
386
|
+
export function registerAmendment(agent, amendment, reason, sourceId) {
|
|
387
|
+
const state = getState();
|
|
388
|
+
const taggedReason = sourceId ? `${reason} [source: ${sourceId}]` : reason;
|
|
389
|
+
const mutation = {
|
|
390
|
+
agent,
|
|
391
|
+
original: getPromptAmendment(agent),
|
|
392
|
+
mutated: `\n\n[Dream-Evolved — ${new Date().toISOString().slice(0, 10)}]\n${amendment}`,
|
|
393
|
+
reason: taggedReason,
|
|
394
|
+
appliedAt: new Date().toISOString(),
|
|
395
|
+
scoreBefore: 0,
|
|
396
|
+
scoreAfter: 0,
|
|
397
|
+
};
|
|
398
|
+
state.mutations.push(mutation);
|
|
399
|
+
if (state.mutations.length > MAX_MUTATIONS) {
|
|
400
|
+
state.mutations = state.mutations.slice(-MAX_MUTATIONS);
|
|
401
|
+
}
|
|
402
|
+
state.generation++;
|
|
403
|
+
persist();
|
|
404
|
+
return mutation;
|
|
405
|
+
}
|
|
378
406
|
/**
|
|
379
407
|
* Flush pending state to disk. Call on process exit.
|
|
380
408
|
*/
|
package/dist/tools/audit.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ interface AuditSection {
|
|
|
15
15
|
}
|
|
16
16
|
declare function auditRepo(repo: string): Promise<AuditResult>;
|
|
17
17
|
declare function formatAuditReport(result: AuditResult): string;
|
|
18
|
+
declare function formatAuditTerminal(result: AuditResult): string;
|
|
18
19
|
export declare function registerAuditTools(): void;
|
|
19
|
-
export { auditRepo, formatAuditReport };
|
|
20
|
+
export { auditRepo, formatAuditReport, formatAuditTerminal };
|
|
20
21
|
//# sourceMappingURL=audit.d.ts.map
|
package/dist/tools/audit.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Security, code quality, documentation, dependency health — all in one report.
|
|
4
4
|
// Designed to be shared. Every audit links back to kbot.
|
|
5
|
+
import chalk from 'chalk';
|
|
5
6
|
import { registerTool } from './index.js';
|
|
6
7
|
const GITHUB_API = 'https://api.github.com';
|
|
7
8
|
const HEADERS = {
|
|
8
|
-
'User-Agent': 'KBot/
|
|
9
|
+
'User-Agent': 'KBot/3.65 (Audit)',
|
|
9
10
|
'Accept': 'application/vnd.github.v3+json',
|
|
10
11
|
};
|
|
11
12
|
async function githubFetch(path) {
|
|
@@ -17,7 +18,7 @@ async function githubFetch(path) {
|
|
|
17
18
|
async function rawFetch(repo, path, branch = 'main') {
|
|
18
19
|
for (const b of [branch, 'master']) {
|
|
19
20
|
try {
|
|
20
|
-
const res = await fetch(`https://raw.githubusercontent.com/${repo}/${b}/${path}`, { headers: { 'User-Agent': 'KBot/
|
|
21
|
+
const res = await fetch(`https://raw.githubusercontent.com/${repo}/${b}/${path}`, { headers: { 'User-Agent': 'KBot/3.65' } });
|
|
21
22
|
if (res.ok)
|
|
22
23
|
return res.text();
|
|
23
24
|
}
|
|
@@ -414,7 +415,7 @@ function formatAuditReport(result) {
|
|
|
414
415
|
// Badge
|
|
415
416
|
const badgeColor = pct >= 80 ? 'brightgreen' : pct >= 60 ? 'yellow' : 'red';
|
|
416
417
|
const badgeUrl = `https://img.shields.io/badge/kbot_audit-${result.grade}_(${pct}%25)-${badgeColor}`;
|
|
417
|
-
lines.push('---', '', '### Add this badge to your README', '', '```markdown', `[](https://www.npmjs.com/package/@kernel.chat/kbot)`, '```', '', `*Audited by [kbot](https://www.npmjs.com/package/@kernel.chat/kbot) —
|
|
418
|
+
lines.push('---', '', '### Add this badge to your README', '', '```markdown', `[](https://www.npmjs.com/package/@kernel.chat/kbot)`, '```', '', `*Audited by [kbot](https://www.npmjs.com/package/@kernel.chat/kbot) — 35 specialist agents, 686+ tools, 20 AI providers*`, `*Install: \`npm install -g @kernel.chat/kbot\` | Audit any repo: \`kbot audit owner/repo\`*`);
|
|
418
419
|
return lines.join('\n');
|
|
419
420
|
}
|
|
420
421
|
/** Generate a compact one-line summary for social sharing */
|
|
@@ -431,6 +432,128 @@ function formatAuditSummary(result) {
|
|
|
431
432
|
summary += ' — Clean bill of health';
|
|
432
433
|
return summary;
|
|
433
434
|
}
|
|
435
|
+
// ── Terminal-styled audit report (chalk) ──────────────────────────────────────
|
|
436
|
+
const VIOLET = '#A78BFA';
|
|
437
|
+
const GREEN = '#4ADE80';
|
|
438
|
+
const YELLOW = '#FBBF24';
|
|
439
|
+
const RED = '#F87171';
|
|
440
|
+
const DIM = '#6B7280';
|
|
441
|
+
const WHITE = '#F9FAFB';
|
|
442
|
+
function gradeColor(grade) {
|
|
443
|
+
if (grade === 'A')
|
|
444
|
+
return GREEN;
|
|
445
|
+
if (grade === 'B')
|
|
446
|
+
return GREEN;
|
|
447
|
+
if (grade === 'C')
|
|
448
|
+
return YELLOW;
|
|
449
|
+
if (grade === 'D')
|
|
450
|
+
return YELLOW;
|
|
451
|
+
return RED;
|
|
452
|
+
}
|
|
453
|
+
function statusColor(status) {
|
|
454
|
+
if (status === 'pass')
|
|
455
|
+
return GREEN;
|
|
456
|
+
if (status === 'warn')
|
|
457
|
+
return YELLOW;
|
|
458
|
+
return RED;
|
|
459
|
+
}
|
|
460
|
+
function scoreBar(score, max, width = 20) {
|
|
461
|
+
const pct = max > 0 ? score / max : 0;
|
|
462
|
+
const filled = Math.round(pct * width);
|
|
463
|
+
const empty = width - filled;
|
|
464
|
+
const color = pct >= 0.8 ? GREEN : pct >= 0.6 ? YELLOW : RED;
|
|
465
|
+
return chalk.hex(color)('\u2588'.repeat(filled)) + chalk.hex(DIM)('\u2591'.repeat(empty));
|
|
466
|
+
}
|
|
467
|
+
function boxLine(content, width) {
|
|
468
|
+
// Strip ANSI for length calculation
|
|
469
|
+
const stripped = content.replace(/\x1b\[[0-9;]*m/g, '');
|
|
470
|
+
const pad = Math.max(0, width - stripped.length - 4);
|
|
471
|
+
return chalk.hex(DIM)('\u2502') + ' ' + content + ' '.repeat(pad) + ' ' + chalk.hex(DIM)('\u2502');
|
|
472
|
+
}
|
|
473
|
+
function findingIcon(finding, _sectionStatus) {
|
|
474
|
+
// Heuristic: positive findings get check, negative get appropriate icon
|
|
475
|
+
const negative = /^(no |missing |could not |stale |\.env NOT|not |high dep|default branch is NOT)/i;
|
|
476
|
+
const warning = /^(readme is short|updated \d+d ago)/i;
|
|
477
|
+
if (negative.test(finding))
|
|
478
|
+
return chalk.hex(RED)('\u2717');
|
|
479
|
+
if (warning.test(finding))
|
|
480
|
+
return chalk.hex(YELLOW)('\u26A0');
|
|
481
|
+
return chalk.hex(GREEN)('\u2713');
|
|
482
|
+
}
|
|
483
|
+
function formatAuditTerminal(result) {
|
|
484
|
+
const pct = Math.round((result.score / result.maxScore) * 100);
|
|
485
|
+
const gc = gradeColor(result.grade);
|
|
486
|
+
const lines = [];
|
|
487
|
+
const W = 60; // inner width
|
|
488
|
+
// ── Header box ──
|
|
489
|
+
const title = `AUDIT REPORT: ${result.repo}`;
|
|
490
|
+
const titlePad = Math.max(0, W - title.length - 2);
|
|
491
|
+
const leftPad = Math.floor(titlePad / 2);
|
|
492
|
+
const rightPad = titlePad - leftPad;
|
|
493
|
+
lines.push('');
|
|
494
|
+
lines.push(chalk.hex(VIOLET)('\u256D') +
|
|
495
|
+
chalk.hex(VIOLET)('\u2500'.repeat(leftPad + 1)) +
|
|
496
|
+
chalk.hex(WHITE).bold(` ${title} `) +
|
|
497
|
+
chalk.hex(VIOLET)('\u2500'.repeat(rightPad + 1)) +
|
|
498
|
+
chalk.hex(VIOLET)('\u256E'));
|
|
499
|
+
// Grade line inside box
|
|
500
|
+
const gradeLine = ` Grade ${chalk.hex(gc).bold(result.grade)} ${chalk.hex(DIM)('\u2502')} ${result.score}/${result.maxScore} (${pct}%)`;
|
|
501
|
+
lines.push(boxLine(gradeLine, W + 4));
|
|
502
|
+
// Score bar inside box
|
|
503
|
+
const bar = scoreBar(result.score, result.maxScore, 30);
|
|
504
|
+
const barLine = ` ${bar} ${chalk.hex(gc).bold(`${pct}%`)}`;
|
|
505
|
+
lines.push(boxLine(barLine, W + 4));
|
|
506
|
+
// Summary inside box
|
|
507
|
+
lines.push(boxLine('', W + 4));
|
|
508
|
+
lines.push(boxLine(` ${chalk.hex(WHITE)(result.summary)}`, W + 4));
|
|
509
|
+
// Close header box
|
|
510
|
+
lines.push(chalk.hex(VIOLET)('\u2570') +
|
|
511
|
+
chalk.hex(VIOLET)('\u2500'.repeat(W + 2)) +
|
|
512
|
+
chalk.hex(VIOLET)('\u256F'));
|
|
513
|
+
lines.push('');
|
|
514
|
+
// ── Sections ──
|
|
515
|
+
for (const section of result.sections) {
|
|
516
|
+
const sPct = section.maxScore > 0 ? Math.round((section.score / section.maxScore) * 100) : 0;
|
|
517
|
+
const sc = statusColor(section.status);
|
|
518
|
+
const statusDot = section.status === 'pass'
|
|
519
|
+
? chalk.hex(GREEN)('\u25CF')
|
|
520
|
+
: section.status === 'warn'
|
|
521
|
+
? chalk.hex(YELLOW)('\u25CF')
|
|
522
|
+
: chalk.hex(RED)('\u25CF');
|
|
523
|
+
// Section header
|
|
524
|
+
lines.push(` ${statusDot} ${chalk.hex(WHITE).bold(section.name)} ` +
|
|
525
|
+
chalk.hex(DIM)('\u2500'.repeat(Math.max(1, 40 - section.name.length))) +
|
|
526
|
+
` ${scoreBar(section.score, section.maxScore, 12)} ` +
|
|
527
|
+
chalk.hex(sc).bold(`${sPct}%`));
|
|
528
|
+
// Findings
|
|
529
|
+
for (const f of section.findings) {
|
|
530
|
+
const icon = findingIcon(f, section.status);
|
|
531
|
+
lines.push(` ${icon} ${chalk.hex(DIM)(f)}`);
|
|
532
|
+
}
|
|
533
|
+
lines.push('');
|
|
534
|
+
}
|
|
535
|
+
// ── Footer ──
|
|
536
|
+
const sep = chalk.hex(VIOLET)('\u2500'.repeat(W + 2));
|
|
537
|
+
lines.push(sep);
|
|
538
|
+
lines.push('');
|
|
539
|
+
// Badge markdown
|
|
540
|
+
const badgeColor = pct >= 80 ? 'brightgreen' : pct >= 60 ? 'yellow' : 'red';
|
|
541
|
+
const badgeUrl = `https://img.shields.io/badge/kbot_audit-${result.grade}_(${pct}%25)-${badgeColor}`;
|
|
542
|
+
lines.push(chalk.hex(VIOLET).bold(' Add this badge to your README:'));
|
|
543
|
+
lines.push('');
|
|
544
|
+
lines.push(chalk.hex(DIM)(` [](https://www.npmjs.com/package/@kernel.chat/kbot)`));
|
|
545
|
+
lines.push('');
|
|
546
|
+
// Install CTA
|
|
547
|
+
lines.push(chalk.hex(DIM)(' Audited by ') +
|
|
548
|
+
chalk.hex(VIOLET).bold('kbot') +
|
|
549
|
+
chalk.hex(DIM)(' \u2014 35 specialist agents, 686+ tools, 20 AI providers'));
|
|
550
|
+
lines.push(chalk.hex(DIM)(' Install: ') +
|
|
551
|
+
chalk.hex(WHITE)('npm install -g @kernel.chat/kbot') +
|
|
552
|
+
chalk.hex(DIM)(' | Audit any repo: ') +
|
|
553
|
+
chalk.hex(WHITE)('kbot audit owner/repo'));
|
|
554
|
+
lines.push('');
|
|
555
|
+
return lines.join('\n');
|
|
556
|
+
}
|
|
434
557
|
export function registerAuditTools() {
|
|
435
558
|
registerTool({
|
|
436
559
|
name: 'repo_audit',
|
|
@@ -453,5 +576,5 @@ export function registerAuditTools() {
|
|
|
453
576
|
});
|
|
454
577
|
}
|
|
455
578
|
// Export for CLI subcommand
|
|
456
|
-
export { auditRepo, formatAuditReport };
|
|
579
|
+
export { auditRepo, formatAuditReport, formatAuditTerminal };
|
|
457
580
|
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// kbot Behavior Tools — Agent-accessible desktop behavior observation
|
|
2
|
+
//
|
|
3
|
+
// Exposes the user behavior system to kbot's tool system so agents can:
|
|
4
|
+
// - Capture a behavior snapshot (what apps are open now)
|
|
5
|
+
// - View behavior patterns from recent history
|
|
6
|
+
//
|
|
7
|
+
// macOS only. Privacy-conscious — app names and window titles only.
|
|
8
|
+
// No window contents, no screenshots, no keylogging.
|
|
9
|
+
import { registerTool } from './index.js';
|
|
10
|
+
import { captureUserBehavior, getBehaviorSummary } from '../user-behavior.js';
|
|
11
|
+
export function registerBehaviorTools() {
|
|
12
|
+
// ── behavior_snapshot ──
|
|
13
|
+
// Capture current desktop state
|
|
14
|
+
registerTool({
|
|
15
|
+
name: 'behavior_snapshot',
|
|
16
|
+
description: 'Capture a snapshot of the user\'s current desktop state: which apps are visible, which app is active (frontmost), the active window title, screen count, and whether Ollama is running. macOS only. Privacy-conscious — captures app names and window titles only, never window contents.',
|
|
17
|
+
parameters: {},
|
|
18
|
+
tier: 'free',
|
|
19
|
+
timeout: 10_000, // 10s — osascript can be slow
|
|
20
|
+
execute: async () => {
|
|
21
|
+
const snapshot = captureUserBehavior();
|
|
22
|
+
if (!snapshot) {
|
|
23
|
+
return 'Behavior capture unavailable (macOS only, or osascript failed).';
|
|
24
|
+
}
|
|
25
|
+
const lines = [
|
|
26
|
+
'Behavior Snapshot',
|
|
27
|
+
`Time: ${snapshot.timestamp} (${snapshot.hour}:00, ${['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][snapshot.dayOfWeek]})`,
|
|
28
|
+
`Active app: ${snapshot.activeApp || '(none)'}`,
|
|
29
|
+
`Active window: ${snapshot.activeWindowTitle || '(none)'}`,
|
|
30
|
+
`Screens: ${snapshot.screenCount}`,
|
|
31
|
+
`Ollama running: ${snapshot.ollamaRunning ? 'yes' : 'no'}`,
|
|
32
|
+
'',
|
|
33
|
+
`Visible apps (${snapshot.visibleApps.length}):`,
|
|
34
|
+
...snapshot.visibleApps.map(a => ` - ${a}`),
|
|
35
|
+
];
|
|
36
|
+
return lines.join('\n');
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
// ── behavior_summary ──
|
|
40
|
+
// Show behavior patterns from recent history
|
|
41
|
+
registerTool({
|
|
42
|
+
name: 'behavior_summary',
|
|
43
|
+
description: 'Show user behavior patterns from recent history: most-used apps, active hours, app switching patterns, app combinations frequently seen together. Reads from stored snapshots. Use this to understand the user\'s workflow habits and desktop patterns.',
|
|
44
|
+
parameters: {
|
|
45
|
+
hours: {
|
|
46
|
+
type: 'number',
|
|
47
|
+
description: 'How many hours of history to analyze (default: 24, max: 168)',
|
|
48
|
+
required: false,
|
|
49
|
+
default: 24,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
tier: 'free',
|
|
53
|
+
execute: async (args) => {
|
|
54
|
+
const hours = Math.min(args.hours || 24, 168);
|
|
55
|
+
const summary = getBehaviorSummary(hours);
|
|
56
|
+
if (!summary) {
|
|
57
|
+
return 'No behavior data available yet. Snapshots are captured at the start of each kbot session. Use behavior_snapshot to capture one now.';
|
|
58
|
+
}
|
|
59
|
+
return summary.text;
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
} // end registerBehaviorTools
|
|
63
|
+
//# sourceMappingURL=behavior-tools.js.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// kbot Collective Dream Tools — Share and receive dream wisdom
|
|
2
|
+
//
|
|
3
|
+
// Agent-accessible tools for the collective dream sharing system:
|
|
4
|
+
// - collective_dream_status: How many local insights are shareable, opt-in state
|
|
5
|
+
// - collective_dream_optin: Opt in/out of anonymous dream sharing
|
|
6
|
+
import { registerTool } from './index.js';
|
|
7
|
+
import { loadConfig, saveConfig } from '../auth.js';
|
|
8
|
+
import { getDreamStatus } from '../dream.js';
|
|
9
|
+
import { prepareCollectiveDreams } from '../collective-dreams.js';
|
|
10
|
+
// ── Config helpers ──
|
|
11
|
+
const OPTIN_KEY = 'collective_dreams_enabled';
|
|
12
|
+
function isDreamSharingEnabled() {
|
|
13
|
+
const config = loadConfig();
|
|
14
|
+
if (!config)
|
|
15
|
+
return false;
|
|
16
|
+
return config[OPTIN_KEY] === true;
|
|
17
|
+
}
|
|
18
|
+
function setDreamSharingEnabled(enabled) {
|
|
19
|
+
const config = loadConfig() || { default_model: 'auto', default_agent: 'auto' };
|
|
20
|
+
config[OPTIN_KEY] = enabled;
|
|
21
|
+
saveConfig(config);
|
|
22
|
+
}
|
|
23
|
+
// ── Tool Registration ──
|
|
24
|
+
export function registerCollectiveDreamTools() {
|
|
25
|
+
registerTool({
|
|
26
|
+
name: 'collective_dream_status',
|
|
27
|
+
description: 'Show collective dream sharing status — how many local insights qualify for sharing, opt-in state, and what gets anonymized. Use this to check before opting in.',
|
|
28
|
+
parameters: {},
|
|
29
|
+
tier: 'free',
|
|
30
|
+
async execute() {
|
|
31
|
+
const enabled = isDreamSharingEnabled();
|
|
32
|
+
const { insights } = getDreamStatus();
|
|
33
|
+
// Check how many would qualify for sharing (relevance > 0.7)
|
|
34
|
+
const shareable = prepareCollectiveDreams(insights);
|
|
35
|
+
const highRelevance = insights.filter(i => i.relevance > 0.7).length;
|
|
36
|
+
const lines = [
|
|
37
|
+
'Collective Dream Sharing',
|
|
38
|
+
'========================',
|
|
39
|
+
`Status: ${enabled ? 'OPTED IN' : 'not opted in'}`,
|
|
40
|
+
'',
|
|
41
|
+
`Local insights: ${insights.length}`,
|
|
42
|
+
`High-relevance (>0.7): ${highRelevance}`,
|
|
43
|
+
`Shareable after anonymization: ${shareable.length}`,
|
|
44
|
+
'',
|
|
45
|
+
'What gets shared (anonymized):',
|
|
46
|
+
' - Dream category (pattern, preference, skill, etc.)',
|
|
47
|
+
' - Keywords (PII stripped)',
|
|
48
|
+
' - Generalized content (names, paths, keys removed)',
|
|
49
|
+
'',
|
|
50
|
+
'What NEVER gets shared:',
|
|
51
|
+
' - Raw insight text, file paths, project names',
|
|
52
|
+
' - API keys, source code, conversation content',
|
|
53
|
+
' - Your identity or any personal information',
|
|
54
|
+
];
|
|
55
|
+
if (shareable.length > 0) {
|
|
56
|
+
lines.push('', 'Preview of anonymized insights:');
|
|
57
|
+
for (const s of shareable.slice(0, 3)) {
|
|
58
|
+
lines.push(` [${s.category}] ${s.generalizedContent.slice(0, 120)}`);
|
|
59
|
+
if (s.keywords.length > 0) {
|
|
60
|
+
lines.push(` keywords: ${s.keywords.join(', ')}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (shareable.length > 3) {
|
|
64
|
+
lines.push(` ... and ${shareable.length - 3} more`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!enabled) {
|
|
68
|
+
lines.push('', 'To opt in: use the collective_dream_optin tool with enabled=true');
|
|
69
|
+
}
|
|
70
|
+
return lines.join('\n');
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
registerTool({
|
|
74
|
+
name: 'collective_dream_optin',
|
|
75
|
+
description: 'Opt in or out of anonymous collective dream sharing. When enabled, high-relevance insights are anonymized and prepared for upload to the collective pool. Other kbot users benefit from aggregated patterns. Persists to ~/.kbot/config.json.',
|
|
76
|
+
parameters: {
|
|
77
|
+
enabled: {
|
|
78
|
+
type: 'boolean',
|
|
79
|
+
description: 'true to opt in, false to opt out',
|
|
80
|
+
required: true,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
tier: 'free',
|
|
84
|
+
async execute(args) {
|
|
85
|
+
const enabled = args.enabled === true || args.enabled === 'true';
|
|
86
|
+
setDreamSharingEnabled(enabled);
|
|
87
|
+
if (enabled) {
|
|
88
|
+
const { insights } = getDreamStatus();
|
|
89
|
+
const shareable = prepareCollectiveDreams(insights);
|
|
90
|
+
return [
|
|
91
|
+
'Collective dream sharing: ENABLED',
|
|
92
|
+
'',
|
|
93
|
+
`${shareable.length} insights ready to share (anonymized).`,
|
|
94
|
+
'Your dreams will be stripped of all personal info before sharing.',
|
|
95
|
+
'You can opt out at any time.',
|
|
96
|
+
].join('\n');
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
return [
|
|
100
|
+
'Collective dream sharing: DISABLED',
|
|
101
|
+
'',
|
|
102
|
+
'No insights will be shared. Your dream journal remains fully private.',
|
|
103
|
+
'Existing collective data (if any) is not affected.',
|
|
104
|
+
].join('\n');
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
} // end registerCollectiveDreamTools
|
|
109
|
+
//# sourceMappingURL=collective-dream-tools.js.map
|
package/dist/tools/index.js
CHANGED
|
@@ -299,9 +299,12 @@ const LAZY_MODULE_IMPORTS = [
|
|
|
299
299
|
{ path: './dj-set-builder.js', registerFn: 'registerDjSetBuilderTools' },
|
|
300
300
|
{ path: './serum2-preset.js', registerFn: 'registerSerum2PresetTools' },
|
|
301
301
|
{ path: './dream-tools.js', registerFn: 'registerDreamTools' },
|
|
302
|
+
{ path: './collective-dream-tools.js', registerFn: 'registerCollectiveDreamTools' },
|
|
302
303
|
{ path: './memory-scanner-tools.js', registerFn: 'registerMemoryScannerTools' },
|
|
303
304
|
{ path: './buddy-tools.js', registerFn: 'registerBuddyTools' },
|
|
304
305
|
{ path: './voice-input-tools.js', registerFn: 'registerVoiceInputTools' },
|
|
306
|
+
{ path: './watchdog.js', registerFn: 'registerWatchdogTools' },
|
|
307
|
+
{ path: './behavior-tools.js', registerFn: 'registerBehaviorTools' },
|
|
305
308
|
];
|
|
306
309
|
/** Track whether lazy tools have been registered */
|
|
307
310
|
let lazyToolsRegistered = false;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ServiceInfo {
|
|
2
|
+
label: string;
|
|
3
|
+
shortName: string;
|
|
4
|
+
pid: number | null;
|
|
5
|
+
status: 'running' | 'dead' | 'not-loaded';
|
|
6
|
+
cpu: string;
|
|
7
|
+
mem: string;
|
|
8
|
+
uptime: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SystemHealth {
|
|
11
|
+
loadAvg: string;
|
|
12
|
+
memFree: string;
|
|
13
|
+
memTotal: string;
|
|
14
|
+
memUsed: string;
|
|
15
|
+
diskFree: string;
|
|
16
|
+
diskUsed: string;
|
|
17
|
+
diskTotal: string;
|
|
18
|
+
ollamaStatus: string;
|
|
19
|
+
ollamaModels: string[];
|
|
20
|
+
kbotMemorySize: string;
|
|
21
|
+
dreamCycles: number;
|
|
22
|
+
dreamInsights: number;
|
|
23
|
+
services: ServiceInfo[];
|
|
24
|
+
}
|
|
25
|
+
export declare function getServiceStatus(): ServiceInfo[];
|
|
26
|
+
export declare function restartService(name: string): {
|
|
27
|
+
success: boolean;
|
|
28
|
+
message: string;
|
|
29
|
+
};
|
|
30
|
+
export declare function getSystemHealth(): SystemHealth;
|
|
31
|
+
export declare function registerWatchdogTools(): void;
|
|
32
|
+
//# sourceMappingURL=watchdog.d.ts.map
|