skills-experimental/buddy-companion-pattern/SKILL.md
# Buddy Companion Pattern Skill Buddy Companion Pattern - Mulberry32 PRNG + Rarity Roll + hashString + rollCache + CompanionBones vs CompanionSoul + Shiny chance 1%。 ## 功能概述 从Claude Code的buddy/companion.ts提取的伙伴系统模式,用于OpenClaw的趣味性功能。 ## 核心机制 ### Mulberry32 PRNG ```typescript // Mulberry32 — tiny seeded PRNG, good enough for picking ducks function mulberry32(seed: number): () => number { let a = seed >>> 0 return function () { a |= 0 a = (a + 0x6d2b79f5) | 0 let t = Math.imul
npx skillsauth add bianhaifeng789-hue/openclaw-config skills-experimental/buddy-companion-patternInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Buddy Companion Pattern - Mulberry32 PRNG + Rarity Roll + hashString + rollCache + CompanionBones vs CompanionSoul + Shiny chance 1%。
从Claude Code的buddy/companion.ts提取的伙伴系统模式,用于OpenClaw的趣味性功能。
// Mulberry32 — tiny seeded PRNG, good enough for picking ducks
function mulberry32(seed: number): () => number {
let a = seed >>> 0
return function () {
a |= 0
a = (a + 0x6d2b79f5) | 0
let t = Math.imul(a ^ (a >>> 15), 1 | a)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
// Tiny seeded PRNG
// Deterministic from seed
// Good enough for casual use
function hashString(s: string): number {
if (typeof Bun !== 'undefined') {
return Number(BigInt(Bun.hash(s)) & 0xffffffffn)
}
// FNV-1a hash for Node
let h = 2166136261
for (let i = 0; i < s.length; i++) {
h ^= s.charCodeAt(i)
h = Math.imul(h, 16777619)
}
return h >>> 0
}
// Bun: use Bun.hash (fast)
// Node: FNV-1a hash (fallback)
// Platform-aware implementation
const RARITY_WEIGHTS = {
common: 60,
uncommon: 25,
rare: 10,
epic: 4,
legendary: 1,
}
function rollRarity(rng: () => number): Rarity {
const total = Object.values(RARITY_WEIGHTS).reduce((a, b) => a + b, 0)
let roll = rng() * total
for (const rarity of RARITIES) {
roll -= RARITY_WEIGHTS[rarity]
if (roll < 0) return rarity
}
return 'common'
}
// Weighted random roll
// Rarity weights: common 60%, legendary 1%
// Cumulative subtraction
let rollCache: { key: string; value: Roll } | undefined
export function roll(userId: string): Roll {
const key = userId + SALT
if (rollCache?.key === key) return rollCache.value // Cache hit
const value = rollFrom(mulberry32(hashString(key)))
rollCache = { key, value }
return value
}
// Deterministic result from userId
// Cache for hot paths (500ms sprite tick, per-keystroke, per-turn)
// Same userId = same result
// Deterministic parts — derived from hash(userId)
export type CompanionBones = {
rarity: Rarity
species: Species
eye: Eye
hat: Hat
shiny: boolean
stats: Record<StatName, number>
}
// Model-generated soul — stored in config after first hatch
export type CompanionSoul = {
name: string
personality: string
}
// Bones regenerated from hash(userId) so species renames don't break stored companions
// and users can't edit their way to a legendary.
// Bones never persist → stale bones fields get overridden
export function getCompanion(): Companion | undefined {
const stored = getGlobalConfig().companion
if (!stored) return undefined
const { bones } = roll(companionUserId())
return { ...stored, ...bones } // bones last → override stale fields
}
// Bones: deterministic from userId hash
// Soul: model-generated, stored in config
// Separation prevents config editing abuse
function rollFrom(rng: () => number): Roll {
const rarity = rollRarity(rng)
const bones: CompanionBones = {
rarity,
species: pick(rng, SPECIES),
eye: pick(rng, EYES),
hat: rarity === 'common' ? 'none' : pick(rng, HATS),
shiny: rng() < 0.01, // 1% shiny chance
stats: rollStats(rng, rarity),
}
return { bones, inspirationSeed: Math.floor(rng() * 1e9) }
}
// shiny: 1% chance
// hat: common has no hat
// inspirationSeed: for name/personality generation
const RARITY_FLOOR: Record<Rarity, number> = {
common: 5,
uncommon: 15,
rare: 25,
epic: 35,
legendary: 50,
}
function rollStats(rng: () => number, rarity: Rarity): Record<StatName, number> {
const floor = RARITY_FLOOR[rarity]
const peak = pick(rng, STAT_NAMES) // One peak stat
let dump = pick(rng, STAT_NAMES)
while (dump === peak) dump = pick(rng, STAT_NAMES) // One dump stat (different)
const stats = {} as Record<StatName, number>
for (const name of STAT_NAMES) {
if (name === peak) {
stats[name] = Math.min(100, floor + 50 + Math.floor(rng() * 30)) // Peak: floor + 50-80
} else if (name === dump) {
stats[name] = Math.max(1, floor - 10 + Math.floor(rng() * 15)) // Dump: floor - 10-5
} else {
stats[name] = floor + Math.floor(rng() * 40) // Others: floor + 0-40
}
}
return stats
}
// Rarity floor determines minimum
// One peak stat, one dump stat
// Rest scattered
{
"userId": "user_abc",
"rarity": "rare",
"species": "duck",
"shiny": false,
"stats": {
"DEBUGGING": 75,
"PATIENCE": 25,
"CHAOS": 40
}
}
hashString(userId) → seed → mulberry32(seed) → deterministic roll
// userId→hash→seed→PRNG→确定性结果
// 同userId永远相同结果
500ms tick + per-keystroke + per-turn → cache prevents repeated hash
// 高频调用缓存
// 避免重复hash计算
Bones: deterministic, never persist → can't edit config for legendary
Soul: model-generated, stored → persists across sessions
// Bones防作弊
// Soul持久化
RARITY_FLOOR[rarity] → minimum stat value → rarity matters
// 稀有度影响最低属性
// legendary最低50分
buddy/companion.ts, buddy/types.tsbusiness
IAA 日报飞书输出能力。 支持把固定 CSV 模板一键转换成: - 中文运营结论 - 飞书卡片 JSON - 飞书发送载荷 Use when: - 需要把 IAA 日报直接发到飞书 - 需要从 CSV 一键生成运营日报
data-ai
IAA日报分析模型 功能: - 渠道日报自动分析 - 小时级+日级ROI联动判断 - 按地区输出加量/降量/停投建议 - 按产品类型输出阈值 - 自动识别利润区/观察区/止损区 Use when: - 分析每天投放数据 - 生成运营日报结论 - 判断是否加量/降量/停投 - 对比美加澳/日韩表现 Keywords: - 日报模型, 投放日报, 加量, 降量, 停投, ROI日报, 分地区分析
data-ai
IAA固定日报分析模板 功能: - 固定字段模板(可直接贴每天数据) - 自动输出总盘结论 - 自动输出美加澳/日韩结论 - 自动给出加量/降量/停投建议 - 适配文件修复/清理两类产品 Use when: - 需要固定日报格式 - 每天复盘渠道表现 - 给运营团队出统一结论 Keywords: - 固定模板, 日报模板, ROI模板, IAA日报, 运营模板
development
# HyperlinkPool Pattern Skill HyperlinkPool Pattern - HyperlinkPool class + strings array + stringMap + Index 0 no hyperlink + intern(hyperlink) + get(id) + undefined handling + 5-minute reset + OSC8 hyperlink interning。 ## 功能概述 从Claude Code的ink/screen.ts提取的HyperlinkPool模式,用于OpenClaw的OSC8超链接池管理。 ## 核心机制 ### HyperlinkPool Class ```typescript export class HyperlinkPool { private strings: string[] = [''] // Index 0 = no hyperlink private stringMap = new Map<string, number>() // strings