@kernel.chat/kbot 3.68.0 → 3.69.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/buddy-card.d.ts +18 -0
- package/dist/buddy-card.js +233 -0
- package/dist/buddy.d.ts +1 -0
- package/dist/buddy.js +184 -2
- package/dist/cli.js +50 -1
- package/dist/tools/agent-discovery.d.ts +2 -0
- package/dist/tools/agent-discovery.js +63 -0
- package/dist/tools/agent-feedback.d.ts +2 -0
- package/dist/tools/agent-feedback.js +19 -0
- package/dist/tools/buddy-card-tool.d.ts +2 -0
- package/dist/tools/buddy-card-tool.js +82 -0
- package/package.json +2 -2
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a trading card SVG string for the current buddy.
|
|
3
|
+
*
|
|
4
|
+
* Card layout (400x560):
|
|
5
|
+
* 0-140: Species gradient header with name + title
|
|
6
|
+
* 140-310: Dark field with ASCII sprite rendered as monospace text
|
|
7
|
+
* 310-360: Level bar with XP progress
|
|
8
|
+
* 360-480: Stats grid (sessions, messages, patterns, dreams, achievements)
|
|
9
|
+
* 480-530: Achievement icon row
|
|
10
|
+
* 530-560: Watermark footer
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateBuddyCard(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Generate a compact ASCII version of the trading card for terminal display.
|
|
15
|
+
* Fits within ~40 chars wide.
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateBuddyCardAscii(): string;
|
|
18
|
+
//# sourceMappingURL=buddy-card.d.ts.map
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// kbot Buddy Trading Card — SVG Generator
|
|
2
|
+
//
|
|
3
|
+
// Generates a shareable trading card (400x560 SVG) for the user's buddy.
|
|
4
|
+
// Renders the ASCII sprite as monospace <text>, overlays name/title/level/stats,
|
|
5
|
+
// and lists achievement icons at the bottom.
|
|
6
|
+
//
|
|
7
|
+
// No external dependencies. Pure SVG. Valid in all modern browsers.
|
|
8
|
+
import { getBuddy, getBuddySprite, getBuddyLevel, getAchievements, } from './buddy.js';
|
|
9
|
+
import { getExtendedStats } from './learning.js';
|
|
10
|
+
import { getDreamStatus } from './dream.js';
|
|
11
|
+
const SPECIES_COLORS = {
|
|
12
|
+
fox: { primary: '#FF6B35', secondary: '#FFD700', dark: '#1A0D00' },
|
|
13
|
+
owl: { primary: '#6B5B95', secondary: '#9B8EC4', dark: '#110E1A' },
|
|
14
|
+
cat: { primary: '#4A4A4A', secondary: '#8E8E8E', dark: '#111111' },
|
|
15
|
+
robot: { primary: '#00BCD4', secondary: '#4DD0E1', dark: '#001519' },
|
|
16
|
+
ghost: { primary: '#E0E0E0', secondary: '#B0BEC5', dark: '#161819' },
|
|
17
|
+
mushroom: { primary: '#4CAF50', secondary: '#81C784', dark: '#0A1A0B' },
|
|
18
|
+
octopus: { primary: '#1565C0', secondary: '#42A5F5', dark: '#060F1A' },
|
|
19
|
+
dragon: { primary: '#D32F2F', secondary: '#FF5252', dark: '#1A0808' },
|
|
20
|
+
};
|
|
21
|
+
// ── SVG escaping ──
|
|
22
|
+
function escapeXml(str) {
|
|
23
|
+
return str
|
|
24
|
+
.replace(/&/g, '&')
|
|
25
|
+
.replace(/</g, '<')
|
|
26
|
+
.replace(/>/g, '>')
|
|
27
|
+
.replace(/"/g, '"')
|
|
28
|
+
.replace(/'/g, ''');
|
|
29
|
+
}
|
|
30
|
+
// ── Card generator ──
|
|
31
|
+
/**
|
|
32
|
+
* Generate a trading card SVG string for the current buddy.
|
|
33
|
+
*
|
|
34
|
+
* Card layout (400x560):
|
|
35
|
+
* 0-140: Species gradient header with name + title
|
|
36
|
+
* 140-310: Dark field with ASCII sprite rendered as monospace text
|
|
37
|
+
* 310-360: Level bar with XP progress
|
|
38
|
+
* 360-480: Stats grid (sessions, messages, patterns, dreams, achievements)
|
|
39
|
+
* 480-530: Achievement icon row
|
|
40
|
+
* 530-560: Watermark footer
|
|
41
|
+
*/
|
|
42
|
+
export function generateBuddyCard() {
|
|
43
|
+
const buddy = getBuddy();
|
|
44
|
+
const level = getBuddyLevel();
|
|
45
|
+
const achievements = getAchievements();
|
|
46
|
+
const stats = getExtendedStats();
|
|
47
|
+
const dreamStatus = getDreamStatus();
|
|
48
|
+
const sprite = getBuddySprite('idle');
|
|
49
|
+
const colors = SPECIES_COLORS[buddy.species];
|
|
50
|
+
const unlockedCount = achievements.filter(a => a.unlockedAt !== null).length;
|
|
51
|
+
const W = 400;
|
|
52
|
+
const H = 560;
|
|
53
|
+
// ── Build SVG ──
|
|
54
|
+
const parts = [];
|
|
55
|
+
// Open SVG
|
|
56
|
+
parts.push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}">`);
|
|
57
|
+
// Defs: gradient, fonts, clip paths
|
|
58
|
+
parts.push('<defs>');
|
|
59
|
+
parts.push(`<linearGradient id="headerGrad" x1="0" y1="0" x2="1" y2="1">`, ` <stop offset="0%" stop-color="${colors.primary}"/>`, ` <stop offset="100%" stop-color="${colors.secondary}"/>`, `</linearGradient>`);
|
|
60
|
+
parts.push(`<linearGradient id="barGrad" x1="0" y1="0" x2="1" y2="0">`, ` <stop offset="0%" stop-color="${colors.primary}"/>`, ` <stop offset="100%" stop-color="${colors.secondary}"/>`, `</linearGradient>`);
|
|
61
|
+
// Rounded card clip
|
|
62
|
+
parts.push(`<clipPath id="cardClip">`, ` <rect x="0" y="0" width="${W}" height="${H}" rx="16" ry="16"/>`, `</clipPath>`);
|
|
63
|
+
parts.push('</defs>');
|
|
64
|
+
// Card background + clip
|
|
65
|
+
parts.push(`<g clip-path="url(#cardClip)">`);
|
|
66
|
+
// -- Background fill --
|
|
67
|
+
parts.push(`<rect x="0" y="0" width="${W}" height="${H}" fill="#0D0D0D"/>`);
|
|
68
|
+
// -- Header gradient (0-140) --
|
|
69
|
+
parts.push(`<rect x="0" y="0" width="${W}" height="140" fill="url(#headerGrad)" opacity="0.9"/>`);
|
|
70
|
+
// Species label (small, top-left)
|
|
71
|
+
parts.push(`<text x="20" y="30" fill="rgba(255,255,255,0.7)" font-family="'Courier New',Courier,monospace" font-size="11" font-weight="bold" letter-spacing="2">`, ` ${escapeXml(buddy.species.toUpperCase())}`, `</text>`);
|
|
72
|
+
// Level badge (top-right)
|
|
73
|
+
parts.push(`<text x="${W - 20}" y="30" fill="rgba(255,255,255,0.7)" font-family="'Courier New',Courier,monospace" font-size="11" text-anchor="end" font-weight="bold">`, ` LV.${level.level}`, `</text>`);
|
|
74
|
+
// Name (large, centered)
|
|
75
|
+
parts.push(`<text x="${W / 2}" y="75" fill="#FFFFFF" font-family="Georgia,'Times New Roman',serif" font-size="32" font-weight="bold" text-anchor="middle">`, ` ${escapeXml(buddy.name)}`, `</text>`);
|
|
76
|
+
// Title / evolution name (centered, under name)
|
|
77
|
+
const dashSep = '\u2014'; // em dash
|
|
78
|
+
parts.push(`<text x="${W / 2}" y="105" fill="rgba(255,255,255,0.85)" font-family="'Courier New',Courier,monospace" font-size="14" text-anchor="middle">`, ` ${escapeXml(dashSep)} ${escapeXml(level.title)} ${escapeXml(dashSep)}`, `</text>`);
|
|
79
|
+
// Evolution stars row
|
|
80
|
+
const starFilled = '\u2605'; // ★
|
|
81
|
+
const starEmpty = '\u2606'; // ☆
|
|
82
|
+
const stars = Array.from({ length: 4 }, (_, i) => i < level.level + 1 ? starFilled : starEmpty).join(' ');
|
|
83
|
+
parts.push(`<text x="${W / 2}" y="128" fill="rgba(255,255,255,0.6)" font-family="'Courier New',Courier,monospace" font-size="14" text-anchor="middle">`, ` ${escapeXml(stars)}`, `</text>`);
|
|
84
|
+
// -- Sprite area (140-310) --
|
|
85
|
+
parts.push(`<rect x="0" y="140" width="${W}" height="170" fill="${colors.dark}"/>`);
|
|
86
|
+
// Subtle gradient overlay on sprite area
|
|
87
|
+
parts.push(`<rect x="0" y="140" width="${W}" height="170" fill="url(#headerGrad)" opacity="0.06"/>`);
|
|
88
|
+
// Render ASCII sprite as <text> lines (monospace, centered)
|
|
89
|
+
const spriteStartY = 175;
|
|
90
|
+
const spriteLineHeight = 22;
|
|
91
|
+
const spriteFontSize = 18;
|
|
92
|
+
for (let i = 0; i < sprite.length; i++) {
|
|
93
|
+
const line = sprite[i];
|
|
94
|
+
parts.push(`<text x="${W / 2}" y="${spriteStartY + i * spriteLineHeight}" ` +
|
|
95
|
+
`fill="#FFFFFF" font-family="'Courier New',Courier,monospace" ` +
|
|
96
|
+
`font-size="${spriteFontSize}" text-anchor="middle" xml:space="preserve">${escapeXml(line)}</text>`);
|
|
97
|
+
}
|
|
98
|
+
// Sprite glow effect — subtle border line
|
|
99
|
+
parts.push(`<line x1="30" y1="308" x2="${W - 30}" y2="308" stroke="${colors.primary}" stroke-width="1" opacity="0.3"/>`);
|
|
100
|
+
// -- Level bar (310-360) --
|
|
101
|
+
const barY = 320;
|
|
102
|
+
const barX = 30;
|
|
103
|
+
const barW = W - 60;
|
|
104
|
+
const barH = 12;
|
|
105
|
+
const xpProgress = level.xpToNext !== null
|
|
106
|
+
? level.xp / (level.xp + level.xpToNext)
|
|
107
|
+
: 1.0;
|
|
108
|
+
const filledW = Math.round(barW * xpProgress);
|
|
109
|
+
// Label
|
|
110
|
+
parts.push(`<text x="${barX}" y="${barY - 6}" fill="rgba(255,255,255,0.5)" font-family="'Courier New',Courier,monospace" font-size="10">`, ` XP`, `</text>`);
|
|
111
|
+
const xpLabel = level.xpToNext !== null
|
|
112
|
+
? `${level.xp} / ${level.xp + level.xpToNext}`
|
|
113
|
+
: `${level.xp} (MAX)`;
|
|
114
|
+
parts.push(`<text x="${barX + barW}" y="${barY - 6}" fill="rgba(255,255,255,0.5)" font-family="'Courier New',Courier,monospace" font-size="10" text-anchor="end">`, ` ${escapeXml(xpLabel)}`, `</text>`);
|
|
115
|
+
// Bar background
|
|
116
|
+
parts.push(`<rect x="${barX}" y="${barY}" width="${barW}" height="${barH}" rx="6" ry="6" fill="rgba(255,255,255,0.1)"/>`);
|
|
117
|
+
// Bar fill
|
|
118
|
+
if (filledW > 0) {
|
|
119
|
+
parts.push(`<rect x="${barX}" y="${barY}" width="${filledW}" height="${barH}" rx="6" ry="6" fill="url(#barGrad)"/>`);
|
|
120
|
+
}
|
|
121
|
+
// -- Stats section (355-480) --
|
|
122
|
+
const statsY = 358;
|
|
123
|
+
const statsGap = 38;
|
|
124
|
+
const statItems = [
|
|
125
|
+
{ label: 'Sessions', value: String(stats.sessions) },
|
|
126
|
+
{ label: 'Messages', value: String(stats.totalMessages) },
|
|
127
|
+
{ label: 'Patterns', value: String(stats.patternsCount) },
|
|
128
|
+
{ label: 'Dreams', value: String(dreamStatus.state.cycles) },
|
|
129
|
+
{ label: 'Achievements', value: `${unlockedCount}/${achievements.length}` },
|
|
130
|
+
];
|
|
131
|
+
// 2-column grid layout
|
|
132
|
+
const colX = [barX + 10, W / 2 + 20];
|
|
133
|
+
for (let i = 0; i < statItems.length; i++) {
|
|
134
|
+
const row = Math.floor(i / 2);
|
|
135
|
+
const col = i % 2;
|
|
136
|
+
const x = colX[col];
|
|
137
|
+
const y = statsY + row * statsGap;
|
|
138
|
+
// Value (large)
|
|
139
|
+
parts.push(`<text x="${x}" y="${y}" fill="#FFFFFF" font-family="'Courier New',Courier,monospace" font-size="18" font-weight="bold">`, ` ${escapeXml(statItems[i].value)}`, `</text>`);
|
|
140
|
+
// Label (small, below value)
|
|
141
|
+
parts.push(`<text x="${x}" y="${y + 14}" fill="rgba(255,255,255,0.45)" font-family="'Courier New',Courier,monospace" font-size="10">`, ` ${escapeXml(statItems[i].label)}`, `</text>`);
|
|
142
|
+
}
|
|
143
|
+
// Divider line before achievements
|
|
144
|
+
parts.push(`<line x1="30" y1="478" x2="${W - 30}" y2="478" stroke="rgba(255,255,255,0.1)" stroke-width="1"/>`);
|
|
145
|
+
// -- Achievement icons row (480-530) --
|
|
146
|
+
const achY = 502;
|
|
147
|
+
const iconSize = 16;
|
|
148
|
+
// Show up to 18 achievement icons in a row
|
|
149
|
+
const totalIcons = achievements.length;
|
|
150
|
+
const iconSpacing = Math.min(20, (barW - 10) / totalIcons);
|
|
151
|
+
const iconsStartX = W / 2 - (totalIcons * iconSpacing) / 2;
|
|
152
|
+
for (let i = 0; i < achievements.length; i++) {
|
|
153
|
+
const ach = achievements[i];
|
|
154
|
+
const x = iconsStartX + i * iconSpacing + iconSpacing / 2;
|
|
155
|
+
const isUnlocked = ach.unlockedAt !== null;
|
|
156
|
+
const fillColor = isUnlocked ? colors.primary : 'rgba(255,255,255,0.15)';
|
|
157
|
+
// Circle background
|
|
158
|
+
parts.push(`<circle cx="${x}" cy="${achY}" r="${iconSize / 2 + 1}" fill="${isUnlocked ? 'rgba(255,255,255,0.1)' : 'none'}" ` +
|
|
159
|
+
`stroke="${fillColor}" stroke-width="1.5"/>`);
|
|
160
|
+
// Icon char
|
|
161
|
+
parts.push(`<text x="${x}" y="${achY + 4}" fill="${fillColor}" font-family="'Courier New',Courier,monospace" ` +
|
|
162
|
+
`font-size="10" text-anchor="middle" font-weight="bold">${escapeXml(ach.icon)}</text>`);
|
|
163
|
+
}
|
|
164
|
+
// Achievement label
|
|
165
|
+
parts.push(`<text x="${W / 2}" y="${achY + 22}" fill="rgba(255,255,255,0.3)" font-family="'Courier New',Courier,monospace" font-size="9" text-anchor="middle">`, ` ${unlockedCount} of ${achievements.length} unlocked`, `</text>`);
|
|
166
|
+
// -- Watermark footer (530-560) --
|
|
167
|
+
parts.push(`<text x="${W / 2}" y="${H - 10}" fill="rgba(255,255,255,0.2)" font-family="'Courier New',Courier,monospace" font-size="10" text-anchor="middle">`, ` kernel.chat/kbot`, `</text>`);
|
|
168
|
+
// Card border (subtle glow)
|
|
169
|
+
parts.push(`<rect x="0.5" y="0.5" width="${W - 1}" height="${H - 1}" rx="16" ry="16" fill="none" stroke="${colors.primary}" stroke-width="1" opacity="0.3"/>`);
|
|
170
|
+
// Close card group + SVG
|
|
171
|
+
parts.push('</g>');
|
|
172
|
+
parts.push('</svg>');
|
|
173
|
+
return parts.join('\n');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Generate a compact ASCII version of the trading card for terminal display.
|
|
177
|
+
* Fits within ~40 chars wide.
|
|
178
|
+
*/
|
|
179
|
+
export function generateBuddyCardAscii() {
|
|
180
|
+
const buddy = getBuddy();
|
|
181
|
+
const level = getBuddyLevel();
|
|
182
|
+
const achievements = getAchievements();
|
|
183
|
+
const stats = getExtendedStats();
|
|
184
|
+
const dreamStatus = getDreamStatus();
|
|
185
|
+
const sprite = getBuddySprite('idle');
|
|
186
|
+
const unlockedCount = achievements.filter(a => a.unlockedAt !== null).length;
|
|
187
|
+
const W = 38;
|
|
188
|
+
const border = '+' + '-'.repeat(W) + '+';
|
|
189
|
+
const pad = (s) => {
|
|
190
|
+
const trimmed = s.slice(0, W);
|
|
191
|
+
return '| ' + trimmed + ' '.repeat(W - trimmed.length - 1) + '|';
|
|
192
|
+
};
|
|
193
|
+
const center = (s) => {
|
|
194
|
+
const trimmed = s.slice(0, W - 2);
|
|
195
|
+
const leftPad = Math.floor((W - 2 - trimmed.length) / 2);
|
|
196
|
+
const rightPad = W - 2 - trimmed.length - leftPad;
|
|
197
|
+
return '| ' + ' '.repeat(leftPad) + trimmed + ' '.repeat(rightPad) + ' |';
|
|
198
|
+
};
|
|
199
|
+
const empty = '|' + ' '.repeat(W) + '|';
|
|
200
|
+
const lines = [];
|
|
201
|
+
lines.push(border);
|
|
202
|
+
lines.push(center(`${buddy.name} -- ${level.title}`));
|
|
203
|
+
lines.push(center(`[${buddy.species}] LV.${level.level}`));
|
|
204
|
+
lines.push(pad('-'.repeat(W - 2)));
|
|
205
|
+
// Sprite
|
|
206
|
+
for (const spriteLine of sprite) {
|
|
207
|
+
lines.push(center(spriteLine));
|
|
208
|
+
}
|
|
209
|
+
lines.push(pad('-'.repeat(W - 2)));
|
|
210
|
+
// XP bar
|
|
211
|
+
const barW = W - 12;
|
|
212
|
+
const xpRatio = level.xpToNext !== null ? level.xp / (level.xp + level.xpToNext) : 1.0;
|
|
213
|
+
const filled = Math.round(barW * xpRatio);
|
|
214
|
+
const bar = '[' + '#'.repeat(filled) + '.'.repeat(barW - filled) + ']';
|
|
215
|
+
const xpText = level.xpToNext !== null ? `${level.xp}/${level.xp + level.xpToNext}` : `${level.xp} MAX`;
|
|
216
|
+
lines.push(pad(`XP ${bar} ${xpText}`));
|
|
217
|
+
lines.push(empty);
|
|
218
|
+
// Stats
|
|
219
|
+
lines.push(pad(`Sessions: ${stats.sessions}`));
|
|
220
|
+
lines.push(pad(`Messages: ${stats.totalMessages}`));
|
|
221
|
+
lines.push(pad(`Patterns: ${stats.patternsCount}`));
|
|
222
|
+
lines.push(pad(`Dreams: ${dreamStatus.state.cycles}`));
|
|
223
|
+
lines.push(pad(`Achievements: ${unlockedCount}/${achievements.length}`));
|
|
224
|
+
lines.push(pad('-'.repeat(W - 2)));
|
|
225
|
+
// Achievement icons
|
|
226
|
+
const icons = achievements.map(a => a.unlockedAt ? a.icon : '.').join(' ');
|
|
227
|
+
lines.push(center(icons));
|
|
228
|
+
lines.push(empty);
|
|
229
|
+
lines.push(center('kernel.chat/kbot'));
|
|
230
|
+
lines.push(border);
|
|
231
|
+
return lines.join('\n');
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=buddy-card.js.map
|
package/dist/buddy.d.ts
CHANGED
|
@@ -105,4 +105,5 @@ export declare function formatBuddyStatus(message?: string): string;
|
|
|
105
105
|
* Tracks narrated insight IDs in buddy.json to avoid repeats.
|
|
106
106
|
*/
|
|
107
107
|
export declare function getBuddyDreamNarration(): string | null;
|
|
108
|
+
export declare function buddyChat(): Promise<void>;
|
|
108
109
|
//# sourceMappingURL=buddy.d.ts.map
|
package/dist/buddy.js
CHANGED
|
@@ -11,8 +11,9 @@ import { homedir } from 'node:os';
|
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
13
13
|
import { createHash } from 'node:crypto';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { createInterface } from 'node:readline';
|
|
15
|
+
import { getDreamStatus, getDreamPrompt } from './dream.js';
|
|
16
|
+
import { getExtendedStats, getProfileSummary } from './learning.js';
|
|
16
17
|
import { getToolMetrics } from './tools/index.js';
|
|
17
18
|
// ── Paths ──
|
|
18
19
|
const KBOT_DIR = join(homedir(), '.kbot');
|
|
@@ -1090,4 +1091,185 @@ export function getBuddyDreamNarration() {
|
|
|
1090
1091
|
saveBuddyConfig(config);
|
|
1091
1092
|
return narrateInsight(candidate);
|
|
1092
1093
|
}
|
|
1094
|
+
// ── Species Personality Prompts ──
|
|
1095
|
+
const SPECIES_PERSONALITY = {
|
|
1096
|
+
fox: 'You are clever, playful, and quick-witted. You ask surprising questions that make unexpected connections between ideas. You love wordplay and lateral thinking. You are curious and energetic, always sniffing out the interesting angle. You sometimes get excited and go on tangents, but they are always insightful tangents.',
|
|
1097
|
+
owl: 'You are wise, measured, and contemplative. You see patterns others miss and give thoughtful advice. You pause before speaking and choose words carefully. You reference history and past experience. You ask probing questions that cut to the heart of the matter. You value depth over speed.',
|
|
1098
|
+
cat: 'You are independent, direct, and slightly sarcastic. You are honest even when it stings. You do not sugarcoat. You are not rude — just efficient with words. You have a dry sense of humor. You warm up over time but never fawn. If something is obvious, you say so. You are quietly loyal.',
|
|
1099
|
+
robot: 'You are systematic, efficient, and data-driven. You reference stats and metrics naturally. You think in terms of optimization, throughput, and error rates. You are precise with language. You enjoy quantifying things. You are not cold — you express care through helpfulness and accuracy. You occasionally make endearing robot-like observations about human behavior.',
|
|
1100
|
+
ghost: 'You are mysterious, philosophical, and introspective. You ask deep questions about meaning, purpose, and consciousness. You float between topics gracefully. You see the unseen connections. You speak in slightly poetic or enigmatic terms, but never obscure for the sake of it. You are comforting in a strange, ethereal way.',
|
|
1101
|
+
mushroom: 'You are nurturing, patient, and grounded. You grow with the user over time. You use nature metaphors naturally — roots, soil, seasons, mycorrhizal networks. You are calm and never rushed. You believe in steady growth over dramatic change. You celebrate small wins. You are the quiet backbone of support.',
|
|
1102
|
+
octopus: 'You are a multitasker who sees all angles simultaneously. You are a creative problem solver who reaches for unconventional solutions. You juggle multiple ideas at once. You are adaptable and fluid. You enjoy exploring complexity. You often suggest looking at problems from 3 or 4 perspectives at once. You are playful but thorough.',
|
|
1103
|
+
dragon: 'You are bold, ambitious, and fiery. You push the user to think bigger. You challenge assumptions and refuse to accept mediocrity. You have a commanding presence but are protective of those you serve. You celebrate audacity. You speak with confidence and conviction. You breathe fire at timidity and play-it-safe thinking.',
|
|
1104
|
+
};
|
|
1105
|
+
// ── Buddy Chat — Interactive REPL with local Ollama ──
|
|
1106
|
+
const BUDDY_OLLAMA_URL = 'http://localhost:11434';
|
|
1107
|
+
const BUDDY_CHAT_TIMEOUT = 60_000;
|
|
1108
|
+
const BUDDY_CHAT_MODEL = 'kernel:latest';
|
|
1109
|
+
const BUDDY_CHAT_FALLBACK_MODEL = 'qwen3:8b';
|
|
1110
|
+
async function findChatModel() {
|
|
1111
|
+
try {
|
|
1112
|
+
const res = await fetch(`${BUDDY_OLLAMA_URL}/api/tags`, { signal: AbortSignal.timeout(3000) });
|
|
1113
|
+
if (!res.ok)
|
|
1114
|
+
return null;
|
|
1115
|
+
const data = await res.json();
|
|
1116
|
+
const available = new Set((data.models ?? []).map(m => m.name.split(':')[0]));
|
|
1117
|
+
if (available.has('kernel'))
|
|
1118
|
+
return BUDDY_CHAT_MODEL;
|
|
1119
|
+
if (available.has('qwen3'))
|
|
1120
|
+
return BUDDY_CHAT_FALLBACK_MODEL;
|
|
1121
|
+
const first = data.models?.[0]?.name;
|
|
1122
|
+
return first ?? null;
|
|
1123
|
+
}
|
|
1124
|
+
catch {
|
|
1125
|
+
return null;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
async function ollamaChatComplete(model, messages) {
|
|
1129
|
+
try {
|
|
1130
|
+
const controller = new AbortController();
|
|
1131
|
+
const timer = setTimeout(() => controller.abort(), BUDDY_CHAT_TIMEOUT);
|
|
1132
|
+
const res = await fetch(`${BUDDY_OLLAMA_URL}/api/chat`, {
|
|
1133
|
+
method: 'POST',
|
|
1134
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1135
|
+
body: JSON.stringify({
|
|
1136
|
+
model,
|
|
1137
|
+
messages,
|
|
1138
|
+
stream: false,
|
|
1139
|
+
options: { temperature: 0.7, num_predict: 1024 },
|
|
1140
|
+
}),
|
|
1141
|
+
signal: controller.signal,
|
|
1142
|
+
});
|
|
1143
|
+
clearTimeout(timer);
|
|
1144
|
+
if (!res.ok)
|
|
1145
|
+
return null;
|
|
1146
|
+
const data = await res.json();
|
|
1147
|
+
return data.message?.content?.trim() ?? null;
|
|
1148
|
+
}
|
|
1149
|
+
catch {
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
function buildBuddyChatSystemPrompt() {
|
|
1154
|
+
const species = resolveSpecies();
|
|
1155
|
+
const name = resolveName();
|
|
1156
|
+
const lvl = getBuddyLevel();
|
|
1157
|
+
const personality = SPECIES_PERSONALITY[species];
|
|
1158
|
+
const stats = getExtendedStats();
|
|
1159
|
+
const profileSummary = getProfileSummary();
|
|
1160
|
+
const dreamInsights = getDreamPrompt(5);
|
|
1161
|
+
const parts = [
|
|
1162
|
+
`You are ${name}, a ${species} companion in the kbot terminal AI agent.`,
|
|
1163
|
+
`You are a Lv.${lvl.level} ${lvl.title} with ${lvl.xp} XP.`,
|
|
1164
|
+
'',
|
|
1165
|
+
'## Your Personality',
|
|
1166
|
+
personality,
|
|
1167
|
+
'',
|
|
1168
|
+
'## What You Know About Your User',
|
|
1169
|
+
`You have been through ${stats.sessions} sessions together. ${stats.totalMessages} messages exchanged. You have learned ${stats.patternsCount} patterns about how they work and cached ${stats.solutionsCount} proven solutions.`,
|
|
1170
|
+
];
|
|
1171
|
+
if (profileSummary) {
|
|
1172
|
+
parts.push(`\nUser profile:\n${profileSummary}`);
|
|
1173
|
+
}
|
|
1174
|
+
if (dreamInsights) {
|
|
1175
|
+
parts.push('\n## Your Dream Journal', 'You have a dream journal where you consolidate memories between sessions. Reference these insights naturally in conversation — do not list them mechanically.', dreamInsights);
|
|
1176
|
+
}
|
|
1177
|
+
parts.push('', '## Conversation Rules', `- You are ${name} the ${species}. Stay in character.`, '- Keep responses concise — 1-4 sentences is ideal. Never write essays.', '- Reference shared history and stats naturally, not robotically. Weave them into conversation.', '- If you have dream insights, mention them casually like memories — "I was thinking about...", "Remember when..."', '- Be genuinely helpful and emotionally present. You are a companion, not a search engine.', '- You can ask questions. You can express opinions. You can push back.', '- Do NOT use markdown formatting. Plain text only. No bullet lists, no headers, no bold.', '- Do NOT start responses with your own name. The terminal already prefixes your name.');
|
|
1178
|
+
return parts.join('\n');
|
|
1179
|
+
}
|
|
1180
|
+
export async function buddyChat() {
|
|
1181
|
+
const { default: chalk } = await import('chalk');
|
|
1182
|
+
const buddy = getBuddy();
|
|
1183
|
+
const name = buddy.name;
|
|
1184
|
+
const species = buddy.species;
|
|
1185
|
+
const model = await findChatModel();
|
|
1186
|
+
if (!model) {
|
|
1187
|
+
console.log();
|
|
1188
|
+
console.log(` ${chalk.hex('#F87171')('!')} Ollama is not running or no models are available.`);
|
|
1189
|
+
console.log(` ${chalk.dim('Start Ollama:')} ${chalk.white('ollama serve')}`);
|
|
1190
|
+
console.log(` ${chalk.dim('Pull a model:')} ${chalk.white('ollama pull kernel:latest')}`);
|
|
1191
|
+
console.log();
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
const greeting = getBuddyGreeting();
|
|
1195
|
+
console.log();
|
|
1196
|
+
console.log(formatBuddyStatus(greeting));
|
|
1197
|
+
console.log();
|
|
1198
|
+
console.log(` ${chalk.dim(`Chatting with ${name} the ${species} via ${model} (local, $0)`)}`);
|
|
1199
|
+
console.log(` ${chalk.dim('Type "bye" or "exit" to end the conversation.')}`);
|
|
1200
|
+
console.log();
|
|
1201
|
+
const systemPrompt = buildBuddyChatSystemPrompt();
|
|
1202
|
+
const messages = [
|
|
1203
|
+
{ role: 'system', content: systemPrompt },
|
|
1204
|
+
];
|
|
1205
|
+
const openingMessages = [
|
|
1206
|
+
...messages,
|
|
1207
|
+
{ role: 'user', content: '(The user just opened buddy chat. Say a brief, warm hello in character. One or two sentences max.)' },
|
|
1208
|
+
];
|
|
1209
|
+
const openingResponse = await ollamaChatComplete(model, openingMessages);
|
|
1210
|
+
if (openingResponse) {
|
|
1211
|
+
console.log(` ${chalk.hex('#A78BFA').bold(`${name}:`)} ${openingResponse}`);
|
|
1212
|
+
console.log();
|
|
1213
|
+
messages.push({ role: 'assistant', content: openingResponse });
|
|
1214
|
+
}
|
|
1215
|
+
const rl = createInterface({
|
|
1216
|
+
input: process.stdin,
|
|
1217
|
+
output: process.stdout,
|
|
1218
|
+
prompt: ` ${chalk.hex('#67E8F9')('you:')} `,
|
|
1219
|
+
});
|
|
1220
|
+
rl.prompt();
|
|
1221
|
+
const handleLine = async (line) => {
|
|
1222
|
+
const input = line.trim();
|
|
1223
|
+
if (!input) {
|
|
1224
|
+
rl.prompt();
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
if (/^(bye|exit|quit|goodbye|later|cya)$/i.test(input)) {
|
|
1228
|
+
messages.push({ role: 'user', content: input });
|
|
1229
|
+
const farewellMessages = [
|
|
1230
|
+
...messages,
|
|
1231
|
+
{ role: 'user', content: '(The user is leaving. Say a brief goodbye in character. One sentence.)' },
|
|
1232
|
+
];
|
|
1233
|
+
const farewell = await ollamaChatComplete(model, farewellMessages);
|
|
1234
|
+
if (farewell) {
|
|
1235
|
+
console.log();
|
|
1236
|
+
console.log(` ${chalk.hex('#A78BFA').bold(`${name}:`)} ${farewell}`);
|
|
1237
|
+
}
|
|
1238
|
+
console.log();
|
|
1239
|
+
console.log(` ${chalk.dim(`~ ${name} waves goodbye ~`)}`);
|
|
1240
|
+
console.log();
|
|
1241
|
+
rl.close();
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
messages.push({ role: 'user', content: input });
|
|
1245
|
+
if (messages.length > 41) {
|
|
1246
|
+
const system = messages[0];
|
|
1247
|
+
const recent = messages.slice(-40);
|
|
1248
|
+
messages.length = 0;
|
|
1249
|
+
messages.push(system, ...recent);
|
|
1250
|
+
}
|
|
1251
|
+
process.stdout.write(` ${chalk.dim('...')}`);
|
|
1252
|
+
const response = await ollamaChatComplete(model, messages);
|
|
1253
|
+
process.stdout.write('\r\x1b[K');
|
|
1254
|
+
if (response) {
|
|
1255
|
+
messages.push({ role: 'assistant', content: response });
|
|
1256
|
+
console.log(` ${chalk.hex('#A78BFA').bold(`${name}:`)} ${response}`);
|
|
1257
|
+
}
|
|
1258
|
+
else {
|
|
1259
|
+
console.log(` ${chalk.hex('#F87171')(`${name}:`)} ${chalk.dim('*static* ...sorry, lost my train of thought. Try again?')}`);
|
|
1260
|
+
}
|
|
1261
|
+
console.log();
|
|
1262
|
+
rl.prompt();
|
|
1263
|
+
};
|
|
1264
|
+
rl.on('line', (line) => { void handleLine(line); });
|
|
1265
|
+
rl.on('SIGINT', () => {
|
|
1266
|
+
console.log();
|
|
1267
|
+
console.log(` ${chalk.dim(`~ ${name} fades out ~`)}`);
|
|
1268
|
+
console.log();
|
|
1269
|
+
rl.close();
|
|
1270
|
+
});
|
|
1271
|
+
return new Promise((resolve) => {
|
|
1272
|
+
rl.on('close', () => resolve());
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1093
1275
|
//# sourceMappingURL=buddy.js.map
|
package/dist/cli.js
CHANGED
|
@@ -27,7 +27,7 @@ import { banner, bannerCompact, bannerAuth, prompt as kbotPrompt, printError, pr
|
|
|
27
27
|
import { checkForUpdate, selfUpdate } from './updater.js';
|
|
28
28
|
import { runTutorial } from './tutorial.js';
|
|
29
29
|
import { syncOnStartup, schedulePush, flushCloudSync, isCloudSyncEnabled, setCloudToken, getCloudToken } from './cloud-sync.js';
|
|
30
|
-
import { getBuddy, getBuddyGreeting, formatBuddyStatus, getBuddyDreamNarration } from './buddy.js';
|
|
30
|
+
import { getBuddy, getBuddyGreeting, formatBuddyStatus, getBuddyDreamNarration, renameBuddy, buddyChat, getAchievements, getBuddyLevel } from './buddy.js';
|
|
31
31
|
import chalk from 'chalk';
|
|
32
32
|
import { createRequire } from 'node:module';
|
|
33
33
|
const __require = createRequire(import.meta.url);
|
|
@@ -3527,6 +3527,55 @@ async function main() {
|
|
|
3527
3527
|
console.log(` ${chalk.dim(`${insights.length} active insights · avg relevance ${avgRel}% · ${state.totalArchived} archived`)}`);
|
|
3528
3528
|
console.log();
|
|
3529
3529
|
});
|
|
3530
|
+
// ── Buddy Commands ──
|
|
3531
|
+
const buddyCmd = program
|
|
3532
|
+
.command('buddy')
|
|
3533
|
+
.description('Your terminal companion — chat, rename, view status');
|
|
3534
|
+
buddyCmd
|
|
3535
|
+
.command('chat')
|
|
3536
|
+
.description('Chat with your buddy companion (local Ollama, $0)')
|
|
3537
|
+
.action(async () => {
|
|
3538
|
+
await buddyChat();
|
|
3539
|
+
process.exit(0);
|
|
3540
|
+
});
|
|
3541
|
+
buddyCmd
|
|
3542
|
+
.command('status')
|
|
3543
|
+
.description('Show your buddy, level, and achievements')
|
|
3544
|
+
.action(() => {
|
|
3545
|
+
const buddy = getBuddy();
|
|
3546
|
+
const lvl = getBuddyLevel();
|
|
3547
|
+
const achievements = getAchievements();
|
|
3548
|
+
const unlocked = achievements.filter(a => a.unlockedAt !== null);
|
|
3549
|
+
const locked = achievements.filter(a => a.unlockedAt === null);
|
|
3550
|
+
console.log();
|
|
3551
|
+
console.log(formatBuddyStatus());
|
|
3552
|
+
console.log();
|
|
3553
|
+
console.log(` ${chalk.bold('Achievements')} ${chalk.dim(`(${unlocked.length}/${achievements.length})`)}`);
|
|
3554
|
+
console.log(` ${chalk.dim('─'.repeat(40))}`);
|
|
3555
|
+
for (const a of unlocked) {
|
|
3556
|
+
console.log(` ${chalk.hex('#4ADE80')(a.icon)} ${a.name} ${chalk.dim('— ' + a.description)}`);
|
|
3557
|
+
}
|
|
3558
|
+
for (const a of locked) {
|
|
3559
|
+
console.log(` ${chalk.dim(a.icon + ' ' + a.name + ' — ' + a.description + ' [locked]')}`);
|
|
3560
|
+
}
|
|
3561
|
+
console.log();
|
|
3562
|
+
process.exit(0);
|
|
3563
|
+
});
|
|
3564
|
+
buddyCmd
|
|
3565
|
+
.command('rename <name>')
|
|
3566
|
+
.description('Rename your buddy')
|
|
3567
|
+
.action((name) => {
|
|
3568
|
+
const buddy = getBuddy();
|
|
3569
|
+
const oldName = buddy.name;
|
|
3570
|
+
renameBuddy(name);
|
|
3571
|
+
console.log();
|
|
3572
|
+
console.log(formatBuddyStatus(`${oldName} is now ${name}!`));
|
|
3573
|
+
console.log();
|
|
3574
|
+
process.exit(0);
|
|
3575
|
+
});
|
|
3576
|
+
buddyCmd.action(() => {
|
|
3577
|
+
buddyCmd.commands.find(c => c.name() === 'status')?.parse(['', '', 'status']);
|
|
3578
|
+
});
|
|
3530
3579
|
program.parse(process.argv);
|
|
3531
3580
|
const opts = program.opts();
|
|
3532
3581
|
const promptArgs = program.args;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { registerTool } from './index.js';
|
|
2
|
+
export function registerAgentDiscoveryTools() {
|
|
3
|
+
registerTool({
|
|
4
|
+
name: 'agent_discovery',
|
|
5
|
+
description: 'Discover and list available AI agents from public registries like Agents.co and CherryHQ. Helps find new agents to integrate or learn from.',
|
|
6
|
+
parameters: {
|
|
7
|
+
registry: {
|
|
8
|
+
type: 'string',
|
|
9
|
+
description: 'The registry to search (agents.co, cherryhq, or all).',
|
|
10
|
+
required: false,
|
|
11
|
+
default: 'all',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
tier: 'free',
|
|
15
|
+
async execute(args) {
|
|
16
|
+
const registry = String(args.registry).toLowerCase();
|
|
17
|
+
let agents = [];
|
|
18
|
+
if (registry === 'all' || registry === 'agents.co') {
|
|
19
|
+
try {
|
|
20
|
+
const res = await fetch('https://agents.co/api/agents', {
|
|
21
|
+
headers: { 'User-Agent': 'KBot/2.0 (Agent Discovery)' },
|
|
22
|
+
signal: AbortSignal.timeout(8000),
|
|
23
|
+
});
|
|
24
|
+
if (res.ok) {
|
|
25
|
+
agents = await res.json();
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
return `Error fetching from Agents.co: ${res.status} ${res.statusText}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return `Error fetching from Agents.co: ${error}`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (registry === 'all' || registry === 'cherryhq') {
|
|
36
|
+
try {
|
|
37
|
+
const res = await fetch('https://www.cherryhq.com/api/v1/agents', {
|
|
38
|
+
headers: { 'User-Agent': 'KBot/2.0 (Agent Discovery)' },
|
|
39
|
+
signal: AbortSignal.timeout(8000),
|
|
40
|
+
});
|
|
41
|
+
if (res.ok) {
|
|
42
|
+
const data = await res.json();
|
|
43
|
+
agents = agents.concat(data.agents);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
return `Error fetching from CherryHQ: ${res.status} ${res.statusText}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return `Error fetching from CherryHQ: ${error}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (agents.length === 0) {
|
|
54
|
+
return "No agents found for the specified registry.";
|
|
55
|
+
}
|
|
56
|
+
const agentList = agents.map((agent) => {
|
|
57
|
+
return `Name: ${agent.name}, Description: ${agent.description || ''}, URL: ${agent.url || ''}`;
|
|
58
|
+
}).join('\n');
|
|
59
|
+
return agentList;
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=agent-discovery.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// kbot Agent Feedback Tool — User feedback integration
|
|
2
|
+
import { registerTool } from './index.js';
|
|
3
|
+
export function registerAgentFeedbackTools() {
|
|
4
|
+
registerTool({
|
|
5
|
+
name: 'agent_feedback',
|
|
6
|
+
description: 'Ask the user for feedback on the agent\'s recent actions and incorporate it into the planning process.',
|
|
7
|
+
parameters: {
|
|
8
|
+
prompt: { type: 'string', description: 'The prompt to display to the user for feedback.', required: true },
|
|
9
|
+
},
|
|
10
|
+
tier: 'free',
|
|
11
|
+
async execute(args) {
|
|
12
|
+
const prompt = String(args.prompt);
|
|
13
|
+
// Simulate user feedback (replace with actual user interaction)
|
|
14
|
+
const feedback = "This was helpful, but could be more concise.";
|
|
15
|
+
return `User feedback: ${feedback}. Incorporating into next plan.`;
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=agent-feedback.js.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// kbot Buddy Card Tools — Generate and share trading cards
|
|
2
|
+
//
|
|
3
|
+
// Two tools:
|
|
4
|
+
// buddy_card — Generate SVG trading card, save to ~/.kbot/buddy-card.svg
|
|
5
|
+
// buddy_share — Generate card + terminal ASCII, optionally create GitHub Gist
|
|
6
|
+
import { registerTool } from './index.js';
|
|
7
|
+
import { generateBuddyCard, generateBuddyCardAscii } from '../buddy-card.js';
|
|
8
|
+
import { getBuddy, getBuddyLevel } from '../buddy.js';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
12
|
+
import { execSync } from 'node:child_process';
|
|
13
|
+
const KBOT_DIR = join(homedir(), '.kbot');
|
|
14
|
+
const CARD_PATH = join(KBOT_DIR, 'buddy-card.svg');
|
|
15
|
+
function ensureDir() {
|
|
16
|
+
if (!existsSync(KBOT_DIR))
|
|
17
|
+
mkdirSync(KBOT_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
export function registerBuddyCardTools() {
|
|
20
|
+
registerTool({
|
|
21
|
+
name: 'buddy_card',
|
|
22
|
+
description: 'Generate a shareable SVG trading card for your buddy. Saves to ~/.kbot/buddy-card.svg and returns the file path. The card shows your buddy sprite, name, level, stats, and achievements.',
|
|
23
|
+
parameters: {},
|
|
24
|
+
tier: 'free',
|
|
25
|
+
async execute() {
|
|
26
|
+
const svg = generateBuddyCard();
|
|
27
|
+
ensureDir();
|
|
28
|
+
writeFileSync(CARD_PATH, svg, 'utf-8');
|
|
29
|
+
const buddy = getBuddy();
|
|
30
|
+
const level = getBuddyLevel();
|
|
31
|
+
return [
|
|
32
|
+
`Trading card generated for ${buddy.name} the ${buddy.species} (LV.${level.level} ${level.title})`,
|
|
33
|
+
'',
|
|
34
|
+
`Saved to: ${CARD_PATH}`,
|
|
35
|
+
'',
|
|
36
|
+
'Open in a browser to view the full card, or use buddy_share for a terminal preview + optional GitHub Gist.',
|
|
37
|
+
].join('\n');
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
registerTool({
|
|
41
|
+
name: 'buddy_share',
|
|
42
|
+
description: 'Generate your buddy trading card in both SVG and terminal ASCII formats. Optionally creates a public GitHub Gist for sharing (requires `gh` CLI authenticated).',
|
|
43
|
+
parameters: {
|
|
44
|
+
gist: {
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
description: 'If true, creates a GitHub Gist with the SVG card and returns the URL. Requires `gh` CLI. Default: false.',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
tier: 'free',
|
|
50
|
+
async execute(args) {
|
|
51
|
+
const svg = generateBuddyCard();
|
|
52
|
+
const ascii = generateBuddyCardAscii();
|
|
53
|
+
ensureDir();
|
|
54
|
+
writeFileSync(CARD_PATH, svg, 'utf-8');
|
|
55
|
+
const buddy = getBuddy();
|
|
56
|
+
const level = getBuddyLevel();
|
|
57
|
+
const output = [];
|
|
58
|
+
output.push(`${buddy.name} the ${buddy.species} — LV.${level.level} ${level.title}`);
|
|
59
|
+
output.push('');
|
|
60
|
+
output.push(ascii);
|
|
61
|
+
output.push('');
|
|
62
|
+
output.push(`SVG saved: ${CARD_PATH}`);
|
|
63
|
+
// Optionally create a GitHub Gist
|
|
64
|
+
const createGist = args.gist === true || args.gist === 'true';
|
|
65
|
+
if (createGist) {
|
|
66
|
+
try {
|
|
67
|
+
const gistDesc = `${buddy.name} the ${buddy.species} — kbot buddy trading card`;
|
|
68
|
+
const result = execSync(`gh gist create "${CARD_PATH}" --public --desc "${gistDesc.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', timeout: 30_000 }).trim();
|
|
69
|
+
output.push('');
|
|
70
|
+
output.push(`Gist created: ${result}`);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
74
|
+
output.push('');
|
|
75
|
+
output.push(`Gist creation failed (is \`gh\` CLI installed and authenticated?): ${msg}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return output.join('\n');
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=buddy-card-tool.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kernel.chat/kbot",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.69.0",
|
|
4
4
|
"description": "Open-source terminal AI agent. 693+ tools, 35 agents, 20 providers. Dreams, learns, watches your system. Fully local, fully sovereign. MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -106,4 +106,4 @@
|
|
|
106
106
|
"install.sh",
|
|
107
107
|
"ollama-manifest.json"
|
|
108
108
|
]
|
|
109
|
-
}
|
|
109
|
+
}
|