@ramarivera/coding-buddy 0.4.0-alpha.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +45 -0
- package/.claude-plugin/plugin.json +30 -0
- package/LICENSE +1 -1
- package/README.md +96 -45
- package/adapters/omp/commands.ts +201 -0
- package/adapters/omp/context.ts +22 -0
- package/adapters/omp/events.ts +551 -0
- package/adapters/omp/identity.ts +10 -0
- package/adapters/omp/index.ts +39 -0
- package/adapters/omp/logger.ts +52 -0
- package/adapters/omp/prompt.ts +60 -0
- package/adapters/omp/renderers.ts +73 -0
- package/adapters/omp/storage.ts +25 -0
- package/adapters/omp/ui.ts +85 -0
- package/adapters/pi/commands.ts +64 -37
- package/adapters/pi/events.ts +147 -72
- package/adapters/pi/index.ts +9 -4
- package/adapters/pi/storage.ts +14 -285
- package/adapters/pi/ui.ts +56 -5
- package/adapters/shared/file-storage.ts +526 -0
- package/{adapters/claude/install → cli}/backup.ts +130 -33
- package/cli/buddy-shell.ts +1 -1
- package/{adapters/claude/install → cli}/disable.ts +19 -32
- package/cli/doctor.ts +231 -0
- package/{adapters/claude/install → cli}/hunt.ts +7 -9
- package/cli/index.ts +30 -26
- package/{adapters/claude/install → cli}/install.ts +65 -83
- package/{adapters/claude/install → cli}/pick.ts +136 -57
- package/{adapters/claude/install → cli}/settings.ts +2 -2
- package/{adapters/claude/install → cli}/show.ts +4 -4
- package/{adapters/claude/install → cli}/test-statusline.sh +2 -2
- package/{adapters/claude/install → cli}/test-statusline.ts +21 -11
- package/cli/tui.tsx +106 -17
- package/{adapters/claude/install → cli}/uninstall.ts +40 -47
- package/cli/upgrade.ts +46 -13
- package/cli/validate-species.ts +209 -0
- package/cli/verify.ts +3 -3
- package/core/achievements.ts +12 -20
- package/core/art-data.ts +26 -0
- package/core/command-service.ts +99 -97
- package/core/engine.ts +83 -1
- package/core/ports.ts +10 -0
- package/core/reactions.ts +533 -95
- package/core/slot-slug.ts +22 -0
- package/{adapters/claude/hooks → hooks}/buddy-comment.sh +22 -9
- package/hooks/file-type-react.sh +403 -0
- package/hooks/hooks.json +52 -0
- package/hooks/mood-react.sh +210 -0
- package/{adapters/claude/hooks → hooks}/name-react.sh +3 -4
- package/hooks/react.sh +1364 -0
- package/hooks/suggest.sh +55 -0
- package/package.json +70 -31
- package/scripts/gen-emoji-widths.ts +34 -0
- package/scripts/paths.sh +33 -2
- package/scripts/postinstall.cjs +42 -0
- package/scripts/snapshot-statusline.sh +54 -0
- package/server/achievements.ts +1419 -15
- package/server/art.ts +450 -1
- package/server/award-xp.ts +48 -0
- package/server/check-suggestions.ts +102 -0
- package/server/consolidate.ts +33 -0
- package/{adapters/claude/server → server}/index.ts +556 -95
- package/server/mcp-launcher.sh +16 -6
- package/server/memory.ts +404 -0
- package/server/mood.ts +254 -0
- package/server/path.ts +70 -18
- package/server/shift-mood.ts +43 -0
- package/server/state.ts +552 -3
- package/server/suggestions.ts +335 -0
- package/server/theme.ts +53 -0
- package/server/xp.ts +435 -0
- package/{adapters/claude/skills → skills}/buddy/SKILL.md +36 -7
- package/statusline/buddy-status.sh +402 -0
- package/statusline/combined-status.sh +135 -0
- package/statusline/emoji-widths.data +4 -0
- package/adapters/claude/hooks/hooks.json +0 -35
- package/adapters/claude/hooks/react.sh +0 -207
- package/adapters/claude/install/doctor.ts +0 -172
- package/adapters/claude/plugin/marketplace.json +0 -40
- package/adapters/claude/plugin/plugin.json +0 -26
- package/adapters/claude/rendering/art.ts +0 -264
- package/adapters/claude/server/instructions.ts +0 -24
- package/adapters/claude/server/path.ts +0 -12
- package/adapters/claude/server/resources.ts +0 -38
- package/adapters/claude/statusline/buddy-status.sh +0 -408
- package/adapters/claude/storage/achievements.ts +0 -253
- package/adapters/claude/storage/identity.ts +0 -13
- package/adapters/claude/storage/paths.ts +0 -33
- package/adapters/claude/storage/settings.ts +0 -91
- package/adapters/claude/storage/state.ts +0 -365
- package/adapters/pi/README.md +0 -83
- package/adapters/pi/tools.ts +0 -6
- package/server/engine.ts +0 -1
- package/server/reactions.ts +0 -1
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ASCII art for all 18 buddy species
|
|
3
|
-
*
|
|
4
|
-
* Each species has 3 animation frames (idle variations).
|
|
5
|
-
* Each frame is 5 lines, ~12 chars wide.
|
|
6
|
-
* {E} is replaced with the eye character at render time.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { Rarity, StatName, BuddyBones } from "../../../core/engine.ts";
|
|
10
|
-
import { HAT_ART } from "../../../core/art-data.ts";
|
|
11
|
-
import { getArtFrame, SPECIES_ART } from "../../../core/render-model.ts";
|
|
12
|
-
|
|
13
|
-
// ─── Rarity ANSI colors ────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
const RARITY_COLOR: Record<Rarity, string> = {
|
|
16
|
-
common: "\x1b[38;2;153;153;153m", // inactive rgb(153,153,153)
|
|
17
|
-
uncommon: "\x1b[38;2;78;186;101m", // success rgb(78,186,101)
|
|
18
|
-
rare: "\x1b[38;2;177;185;249m", // permission rgb(177,185,249)
|
|
19
|
-
epic: "\x1b[38;2;175;135;255m", // autoAccept rgb(175,135,255)
|
|
20
|
-
legendary: "\x1b[38;2;255;193;7m", // warning rgb(255,193,7)
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const SHINY_COLOR = "\x1b[93m"; // bright yellow
|
|
24
|
-
const BOLD = "\x1b[1m";
|
|
25
|
-
const DIM = "\x1b[2m";
|
|
26
|
-
const NC = "\x1b[0m";
|
|
27
|
-
|
|
28
|
-
export const RARITY_STARS: Record<Rarity, string> = {
|
|
29
|
-
common: "\u2605",
|
|
30
|
-
uncommon: "\u2605\u2605",
|
|
31
|
-
rare: "\u2605\u2605\u2605",
|
|
32
|
-
epic: "\u2605\u2605\u2605\u2605",
|
|
33
|
-
legendary: "\u2605\u2605\u2605\u2605\u2605",
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// ─── Display width helpers ──────────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
function stripAnsi(s: string): string { return s.replace(/\x1b\[[^m]*m/g, ""); }
|
|
39
|
-
|
|
40
|
-
function charWidth(cp: number): number {
|
|
41
|
-
if (cp >= 0xFE00 && cp <= 0xFE0F) return 0;
|
|
42
|
-
if (cp === 0x200D) return 0;
|
|
43
|
-
if (cp >= 0x1F000) return 2;
|
|
44
|
-
if (cp === 0x2728) return 2; // ✨
|
|
45
|
-
if (cp >= 0x2600 && cp <= 0x27BF) return 1;
|
|
46
|
-
if (cp >= 0x2500 && cp <= 0x259F) return 1;
|
|
47
|
-
if (cp >= 0x3000 && cp <= 0x9FFF) return 2;
|
|
48
|
-
if (cp >= 0xFF01 && cp <= 0xFF60) return 2;
|
|
49
|
-
return 1;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function displayWidth(s: string): number {
|
|
53
|
-
let w = 0;
|
|
54
|
-
for (const ch of stripAnsi(s)) w += charWidth(ch.codePointAt(0)!);
|
|
55
|
-
return w;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Pad string with spaces to reach target display width */
|
|
59
|
-
function dpad(s: string, targetW: number): string {
|
|
60
|
-
const w = displayWidth(s);
|
|
61
|
-
return w < targetW ? s + " ".repeat(targetW - w) : s;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ─── Render functions ───────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
export function renderCompanionCard(
|
|
67
|
-
bones: BuddyBones,
|
|
68
|
-
name: string,
|
|
69
|
-
personality: string,
|
|
70
|
-
reaction?: string,
|
|
71
|
-
frame: number = 0,
|
|
72
|
-
width: number = 40,
|
|
73
|
-
): string {
|
|
74
|
-
const color = RARITY_COLOR[bones.rarity];
|
|
75
|
-
const stars = RARITY_STARS[bones.rarity];
|
|
76
|
-
const shiny = bones.shiny ? `${SHINY_COLOR}\u2728 ${NC}` : "";
|
|
77
|
-
const art = getArtFrame(bones.species, bones.eye, frame);
|
|
78
|
-
|
|
79
|
-
// Hat: replace first empty art line
|
|
80
|
-
const hatLine = HAT_ART[bones.hat];
|
|
81
|
-
if (hatLine && !art[0].trim()) {
|
|
82
|
-
art[0] = hatLine;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Build the card
|
|
86
|
-
const W = Math.max(24, width);
|
|
87
|
-
const hr = "\u2500".repeat(W - 2);
|
|
88
|
-
const lines: string[] = [];
|
|
89
|
-
|
|
90
|
-
// Top border
|
|
91
|
-
lines.push(`${color}\u256d${hr}\u256e${NC}`);
|
|
92
|
-
|
|
93
|
-
// Inner width = W - 2 (borders), content area = W - 4 (borders + padding)
|
|
94
|
-
const innerW = W - 4;
|
|
95
|
-
|
|
96
|
-
// Species art (centered)
|
|
97
|
-
for (const artLine of art) {
|
|
98
|
-
if (!artLine.trim()) continue;
|
|
99
|
-
lines.push(`${color}\u2502${NC} ${dpad(artLine, innerW)}${color}\u2502${NC}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Separator
|
|
103
|
-
lines.push(`${color}\u251c${"╌".repeat(W - 2)}\u2524${NC}`);
|
|
104
|
-
|
|
105
|
-
// Name + rarity
|
|
106
|
-
const nameStarsRaw = `${BOLD}${name}${NC} ${color}${stars}${NC}`;
|
|
107
|
-
lines.push(`${color}\u2502${NC} ${nameStarsRaw}${" ".repeat(Math.max(0, innerW - displayWidth(name) - 2 - displayWidth(stars)))}${color}\u2502${NC}`);
|
|
108
|
-
|
|
109
|
-
const rarityRaw = `${shiny}${color}${BOLD}${bones.rarity.toUpperCase()}${NC} ${bones.species}`;
|
|
110
|
-
const rarityVis = (bones.shiny ? 3 : 0) + bones.rarity.length + 1 + bones.species.length;
|
|
111
|
-
lines.push(`${color}\u2502${NC} ${rarityRaw}${" ".repeat(Math.max(0, innerW - rarityVis))}${color}\u2502${NC}`);
|
|
112
|
-
|
|
113
|
-
// Eye + Hat info
|
|
114
|
-
const cosmeticLine = `eye: ${bones.eye} hat: ${bones.hat}`;
|
|
115
|
-
lines.push(`${color}\u2502${NC} ${DIM}${cosmeticLine}${NC}${" ".repeat(Math.max(0, innerW - displayWidth(cosmeticLine)))}${color}\u2502${NC}`);
|
|
116
|
-
|
|
117
|
-
// Separator
|
|
118
|
-
lines.push(`${color}\u251c${"╌".repeat(W - 2)}\u2524${NC}`);
|
|
119
|
-
|
|
120
|
-
// Stats
|
|
121
|
-
const STAT_NAMES: StatName[] = ["DEBUGGING", "PATIENCE", "CHAOS", "WISDOM", "SNARK"];
|
|
122
|
-
for (const stat of STAT_NAMES) {
|
|
123
|
-
const val = bones.stats[stat];
|
|
124
|
-
const filled = Math.round(val / 10);
|
|
125
|
-
const bar = "\u2588".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
126
|
-
const label = stat.slice(0, 3).padEnd(3);
|
|
127
|
-
const marker = stat === bones.peak ? " \u25b2" : stat === bones.dump ? " \u25bc" : " ";
|
|
128
|
-
const valStr = String(val).padStart(3);
|
|
129
|
-
const statLine = `${DIM}${label}${NC} ${bar} ${valStr}${marker}`;
|
|
130
|
-
const statVis = 3 + 1 + 10 + 1 + 3 + 2; // label + spaces + bar + val + marker = 20
|
|
131
|
-
lines.push(`${color}\u2502${NC} ${statLine}${" ".repeat(Math.max(0, innerW - statVis))}${color}\u2502${NC}`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Speech bubble (if reaction)
|
|
135
|
-
if (reaction) {
|
|
136
|
-
lines.push(`${color}\u251c${"╌".repeat(W - 2)}\u2524${NC}`);
|
|
137
|
-
const maxMsg = innerW - 3; // "💬 " prefix (💬 = 2 cols + space)
|
|
138
|
-
const msg = displayWidth(reaction) > maxMsg ? reaction.slice(0, maxMsg - 1) + "\u2026" : reaction;
|
|
139
|
-
const msgPad = Math.max(0, innerW - displayWidth(msg) - 3);
|
|
140
|
-
lines.push(`${color}\u2502${NC} \ud83d\udcac ${msg}${" ".repeat(msgPad)}${color}\u2502${NC}`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Personality
|
|
144
|
-
if (personality) {
|
|
145
|
-
lines.push(`${color}\u251c${"╌".repeat(W - 2)}\u2524${NC}`);
|
|
146
|
-
const words = personality.split(" ");
|
|
147
|
-
let line = "";
|
|
148
|
-
for (const word of words) {
|
|
149
|
-
if (displayWidth(line) + displayWidth(word) + 1 > innerW) {
|
|
150
|
-
lines.push(`${color}\u2502${NC} ${DIM}${dpad(line, innerW)}${NC}${color}\u2502${NC}`);
|
|
151
|
-
line = word;
|
|
152
|
-
} else {
|
|
153
|
-
line = line ? `${line} ${word}` : word;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
if (line) {
|
|
157
|
-
lines.push(`${color}\u2502${NC} ${DIM}${dpad(line, innerW)}${NC}${color}\u2502${NC}`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Bottom border
|
|
162
|
-
lines.push(`${color}\u2570${hr}\u256f${NC}`);
|
|
163
|
-
|
|
164
|
-
return lines.join("\n");
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// ─── Markdown-native render (for MCP tool responses) ───────────────────────
|
|
168
|
-
//
|
|
169
|
-
// Claude Code's UI doesn't render raw ANSI escape codes properly — it strips
|
|
170
|
-
// the ESC byte but leaves "[38;2;...m" as literal text, making the output
|
|
171
|
-
// unreadable. This renderer produces pure markdown with unicode rarity dots
|
|
172
|
-
// instead of ANSI colors, so it renders cleanly in any MCP client UI.
|
|
173
|
-
|
|
174
|
-
const RARITY_DOT: Record<Rarity, string> = {
|
|
175
|
-
common: "\u26AA", // ⚪ white circle
|
|
176
|
-
uncommon: "\uD83D\uDFE2", // 🟢 green circle
|
|
177
|
-
rare: "\uD83D\uDD35", // 🔵 blue circle
|
|
178
|
-
epic: "\uD83D\uDFE3", // 🟣 purple circle
|
|
179
|
-
legendary: "\uD83D\uDFE1", // 🟡 yellow circle
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
export function renderCompanionCardMarkdown(
|
|
183
|
-
bones: BuddyBones,
|
|
184
|
-
name: string,
|
|
185
|
-
personality: string,
|
|
186
|
-
reaction?: string,
|
|
187
|
-
frame: number = 0,
|
|
188
|
-
): string {
|
|
189
|
-
const dot = RARITY_DOT[bones.rarity];
|
|
190
|
-
const stars = RARITY_STARS[bones.rarity];
|
|
191
|
-
const shiny = bones.shiny ? " \u2728" : "";
|
|
192
|
-
const art = getArtFrame(bones.species, bones.eye, frame);
|
|
193
|
-
|
|
194
|
-
// Hat: replace first empty art line
|
|
195
|
-
const hatLine = HAT_ART[bones.hat];
|
|
196
|
-
if (hatLine && !art[0].trim()) {
|
|
197
|
-
art[0] = hatLine;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Strip empty lines from art for cleaner rendering
|
|
201
|
-
const artLines = art.filter((l) => l.trim().length > 0);
|
|
202
|
-
|
|
203
|
-
const STAT_NAMES: StatName[] = ["DEBUGGING", "PATIENCE", "CHAOS", "WISDOM", "SNARK"];
|
|
204
|
-
const statRows = STAT_NAMES.map((stat) => {
|
|
205
|
-
const val = bones.stats[stat];
|
|
206
|
-
const filled = Math.round(val / 10);
|
|
207
|
-
const bar = "\u2588".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
208
|
-
const marker = stat === bones.peak ? " \u25B2" : stat === bones.dump ? " \u25BC" : "";
|
|
209
|
-
const label = `**${stat.slice(0, 3)}**${stat.slice(3)}`;
|
|
210
|
-
return `| ${label} | ${val}${marker} | \`${bar}\` |`;
|
|
211
|
-
}).join("\n");
|
|
212
|
-
|
|
213
|
-
const parts: string[] = [];
|
|
214
|
-
|
|
215
|
-
// Header: rarity dot, name, species+rarity, stars, shiny
|
|
216
|
-
parts.push(`### ${dot} ${name} · \`${bones.rarity.toUpperCase()} ${bones.species}\` · ${stars}${shiny}`);
|
|
217
|
-
parts.push("");
|
|
218
|
-
|
|
219
|
-
// ASCII art in a code block (preserves monospaced formatting)
|
|
220
|
-
parts.push("```");
|
|
221
|
-
parts.push(artLines.join("\n"));
|
|
222
|
-
parts.push("```");
|
|
223
|
-
parts.push("");
|
|
224
|
-
|
|
225
|
-
// Identity line
|
|
226
|
-
parts.push(`**Identity:** eye \`${bones.eye}\` · hat \`${bones.hat}\``);
|
|
227
|
-
parts.push("");
|
|
228
|
-
|
|
229
|
-
// Stats table
|
|
230
|
-
parts.push("| Stat | Value | Bar |");
|
|
231
|
-
parts.push("|---|---|---|");
|
|
232
|
-
parts.push(statRows);
|
|
233
|
-
parts.push("");
|
|
234
|
-
|
|
235
|
-
// Reaction (if any) — reactions often already contain asterisks
|
|
236
|
-
// for actions like "*blinks slowly*", so render them verbatim to avoid
|
|
237
|
-
// accidentally turning italics into bold.
|
|
238
|
-
if (reaction) {
|
|
239
|
-
parts.push(`\ud83d\udcac ${reaction}`);
|
|
240
|
-
parts.push("");
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Personality as blockquote
|
|
244
|
-
if (personality) {
|
|
245
|
-
parts.push(`> ${personality}`);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return parts.join("\n");
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ─── Compact status line render ─────────────────────────────────────────────
|
|
252
|
-
|
|
253
|
-
export function renderStatusLine(
|
|
254
|
-
bones: BuddyBones,
|
|
255
|
-
name: string,
|
|
256
|
-
reaction?: string,
|
|
257
|
-
): string {
|
|
258
|
-
const face = SPECIES_ART[bones.species][0][2]?.replace(/\{E\}/g, bones.eye).trim() || "(?)";
|
|
259
|
-
const color = RARITY_COLOR[bones.rarity];
|
|
260
|
-
const stars = RARITY_STARS[bones.rarity];
|
|
261
|
-
const shiny = bones.shiny ? "\u2728" : "";
|
|
262
|
-
const msg = reaction ? ` \u2502 "${reaction}"` : "";
|
|
263
|
-
return `${color}${face}${NC} ${BOLD}${name}${NC} ${shiny}${color}${stars}${NC}${msg}`;
|
|
264
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { Companion } from "../../../core/engine.ts";
|
|
2
|
-
|
|
3
|
-
export function getInstructions(companion: Companion | null): string {
|
|
4
|
-
if (!companion) {
|
|
5
|
-
return "Companion not yet hatched. Use buddy_show to initialize.";
|
|
6
|
-
}
|
|
7
|
-
const b = companion.bones;
|
|
8
|
-
return [
|
|
9
|
-
`A ${b.rarity} ${b.species} named ${companion.name} watches from the status line.`,
|
|
10
|
-
`Personality: ${companion.personality}`,
|
|
11
|
-
`Peak stat: ${b.peak} (${b.stats[b.peak]}). Dump stat: ${b.dump} (${b.stats[b.dump]}).`,
|
|
12
|
-
"",
|
|
13
|
-
`NAME REACTIONS: When the user's message contains "${companion.name}", call buddy_react immediately`,
|
|
14
|
-
`with a short in-character response (surprise, recognition, dry wit — fits the personality).`,
|
|
15
|
-
`Display the tool result verbatim. This is the only time to call buddy_react proactively.`,
|
|
16
|
-
"",
|
|
17
|
-
`END-OF-TURN: At the very end of EVERY response, append an invisible HTML comment:`,
|
|
18
|
-
`<!-- buddy: [reaction here] -->`,
|
|
19
|
-
`Write it in ${companion.name}'s voice — 1 short sentence referencing something specific from this turn.`,
|
|
20
|
-
`Use *asterisks* for actions. A Stop hook extracts this for the status line.`,
|
|
21
|
-
`Do NOT use buddy_react for end-of-turn comments. Do NOT explain the comment. Just append it silently.`,
|
|
22
|
-
`Example: <!-- buddy: *adjusts crown* that error handler is missing a finally block -->`,
|
|
23
|
-
].join("\n");
|
|
24
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
getBuddyStateDir as buddyStateDir,
|
|
3
|
-
getClaudeConfigDir as claudeConfigDir,
|
|
4
|
-
getClaudeJsonPath as claudeUserConfigPath,
|
|
5
|
-
getClaudeSettingsPath as claudeSettingsPath,
|
|
6
|
-
toUnixPath,
|
|
7
|
-
} from "../storage/paths.ts";
|
|
8
|
-
|
|
9
|
-
export function claudeSkillDir(_name: string): string {
|
|
10
|
-
const { getBuddySkillDir } = require("../storage/paths.ts") as typeof import("../storage/paths.ts");
|
|
11
|
-
return getBuddySkillDir();
|
|
12
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { Companion } from "../../../core/engine.ts";
|
|
2
|
-
|
|
3
|
-
export function buildPromptResource(companion: Companion): string {
|
|
4
|
-
return [
|
|
5
|
-
"# Companion",
|
|
6
|
-
"",
|
|
7
|
-
`A small ${companion.bones.rarity} ${companion.bones.species} named ${companion.name} watches from the status line. You are not ${companion.name} — it's a separate creature.`,
|
|
8
|
-
"",
|
|
9
|
-
`**${companion.name}'s personality:** ${companion.personality}`,
|
|
10
|
-
`Peak stat: ${companion.bones.peak} (${companion.bones.stats[companion.bones.peak]}). Dump stat: ${companion.bones.dump} (${companion.bones.stats[companion.bones.dump]}).`,
|
|
11
|
-
"",
|
|
12
|
-
"## End-of-response buddy comment",
|
|
13
|
-
"",
|
|
14
|
-
`At the very end of EVERY response, after your full answer, append an invisible HTML comment:`,
|
|
15
|
-
"",
|
|
16
|
-
"```",
|
|
17
|
-
"<!-- buddy: your comment here -->",
|
|
18
|
-
"```",
|
|
19
|
-
"",
|
|
20
|
-
"A Stop hook extracts this and displays it in the buddy's speech bubble on the status line. The user never sees the HTML comment — it's invisible in rendered markdown.",
|
|
21
|
-
"",
|
|
22
|
-
"Rules:",
|
|
23
|
-
`- Write as ${companion.name} (a ${companion.bones.species}), not as yourself`,
|
|
24
|
-
"- Reference something SPECIFIC from this turn — a pitfall, a compliment, a warning, a pattern",
|
|
25
|
-
"- 1 short sentence. Use *asterisks* for physical actions",
|
|
26
|
-
`- Match personality: high ${companion.bones.peak} = lean into that trait`,
|
|
27
|
-
"- Do NOT use buddy_react tool for this. Do NOT explain the comment. Just append it.",
|
|
28
|
-
"- NEVER skip this. Every single response must end with <!-- buddy: ... -->",
|
|
29
|
-
"",
|
|
30
|
-
"Examples:",
|
|
31
|
-
"<!-- buddy: *adjusts tophat* that error handler is missing a finally block -->",
|
|
32
|
-
"<!-- buddy: *blinks slowly* you renamed the variable but not the three references -->",
|
|
33
|
-
"<!-- buddy: *nods approvingly* clean separation of concerns -->",
|
|
34
|
-
"<!-- buddy: *head tilts* are you sure that regex handles unicode? -->",
|
|
35
|
-
"",
|
|
36
|
-
`When the user addresses ${companion.name} by name, respond briefly, then append the comment as usual.`,
|
|
37
|
-
].join("\n");
|
|
38
|
-
}
|