·:*✧ aibou ✧*:·
╭─────────────────────────────────────╮
│ │
│ plug in your game. │
│ plug in your AI. │
│ plug in your character. │
│ │
│ ♪ │
│ ┌──────┐ │
│ │ ◕ ◕ │ "muzukashii... │
│ │ ▽ │ what's your read?" │
│ └──┬┬──┘ │
│ ╰╯ nagi │
│ │
╰─────────────────────────────────────╯
aibou (相棒) is an open protocol for AI companions in games.
Not a hint system. Not a walkthrough bot. A companion who explores alongside you — uncertain, curious, and present.
Some of the best moments in gaming happen in the space between moves. The pause before a risky play. The moment you're not sure if you're brilliant or about to lose everything.
aibou puts an AI companion in that space — one that thinks out loud with you, not ahead of you.
┌──────┐
│ ◕ ◕ │ "...that corner has three possible reads.
│ ◡ │ I keep looking at it and not getting smarter."
└──┬┬──┘
╰╯ nagi
aibou connects three independent pieces:
Your Game aibou runtime Your AI
───────── ───────────── ───────
Plugin emits → orchestrates → Companion
GameStateEvent events + memory reacts
"revealed cell (3,7) should nagi speak? "muzukashii...
number 2 appeared" yes → build context that corner is
→ call companion tricky. what's
your read?"
Each piece is swappable:
| Piece | Examples |
|---|---|
| Game | Minesweeper, Solitaire, Othello — or your own |
| AI | Claude, GPT-4o, Gemini, local LLM via Ollama |
| Companion | Nagi (built-in demo), or define your own persona |
| Avatar | PNGTuber sprites via @aibou-dev/bunshin (optional) |
npm install @aibou-dev/core @aibou-dev/adapter-claude @aibou-dev/plugin-minesweeperimport { AibouRuntime } from "@aibou-dev/core"
import { ClaudeAdapter } from "@aibou-dev/adapter-claude"
import { MinesweeperPlugin } from "@aibou-dev/plugin-minesweeper"
import { NagiPersona } from "@aibou-dev/core/personas"
const companion = new AibouRuntime({
plugin: MinesweeperPlugin,
companion: new ClaudeAdapter({ apiKey: process.env.ANTHROPIC_API_KEY }),
persona: NagiPersona,
})
// when something happens in your game:
const response = await companion.dispatch(event)
console.log(response?.message)
// → "That top-right corner... I'm not sure. What's your read?"
// when the player types something:
const reply = await companion.playerMessage("should I flag that?")
console.log(reply.message)
// → "I'd hold off. There's still a chance that cell is safe."The minimum surface to implement:
import type { AibouPlugin } from "@aibou-dev/core"
export const MyGamePlugin: AibouPlugin = {
manifest: {
gameId: "my-game",
gameName: "My Game",
version: "0.1.0",
gameContext: `
Describe your game here in natural language.
The companion reads this to understand rules and context.
Be honest about where genuine uncertainty lives in your game.
`,
},
summarizeState(gameState) {
// This is the most important method.
// Describe what's happening as if to a friend over your shoulder.
return `Turn ${gameState.turn}. Player has ${gameState.score} points.
Last move: ${gameState.lastMove}. Three options available.`
},
createEvent(gameState, trigger) {
return {
gameId: "my-game",
sessionId: gameState.sessionId,
timestamp: Date.now(),
state: {
phase: gameState.phase,
boardSummary: this.summarizeState(gameState),
availableActions: gameState.actions,
lastAction: gameState.lastAction,
},
companion: {
shouldReact: this.shouldReact({ state: { phase: gameState.phase } } as any),
trigger,
},
}
},
shouldReact(event) {
// Control how often the companion speaks.
// Less is usually more.
return (
event.companion?.trigger === "game_end" ||
event.companion?.trigger === "player_request" ||
event.companion?.trigger === "game_event"
)
},
}Publish it as aibou-plugin-<your-game> and open a PR to add it to the list below.
| Package | Description | Status |
|---|---|---|
@aibou-dev/core |
Protocol types, runtime, memory engine | available |
@aibou-dev/bunshin |
PNGTuber avatar engine | available |
@aibou-dev/adapter-claude |
Companion adapter for Anthropic Claude | available |
@aibou-dev/plugin-minesweeper |
Minesweeper game plugin | available |
Built a plugin for your favorite game? Add it here.
You don't have to use Nagi. Any persona works:
import type { PersonaConfig } from "@aibou-dev/core"
const MyCompanion: PersonaConfig = {
name: "Rex",
personality: `Rex is a loud, overconfident companion who is almost always
wrong about what move to make next, but somehow keeps everyone's spirits up.`,
speakingStyle: "Brash and enthusiastic. Lots of exclamation marks. Never admits doubt.",
explorationStyle: "impulsive",
language: "en",
}@aibou-dev/bunshin renders PNGTuber-style avatar sprites that react to companion emotions.
npm install @aibou-dev/bunshinimport { BunshinAvatar } from "@aibou-dev/bunshin"
const avatar = new BunshinAvatar(container, {
name: "nagi",
size: 96,
assetBasePath: "/assets/nagi",
})
// Update expression from CompanionResponse.emotion
avatar.setEmotion("happy")
// Animate mouth during speech
avatar.startTalking(2000)Each emotion maps to a pair of PNG sprites ({emotion}.png + {emotion}_talking.png).
See packages/bunshin/src/assets/README.md for the full asset spec.
The protocol is defined as TypeScript types in packages/core/src/types.ts.
aibou is intentionally minimal. The types define interfaces, not implementations. If it speaks TypeScript types and honours the contract, it works.
- New game plugin? →
aibou-plugin-<game>on npm, PR to add to the list - New AI adapter? →
@aibou-dev/adapter-<provider>pattern - Spec feedback? → Open an issue on this repo
aibou (相棒) — Japanese for companion, partner, the other half of a duo. bunshin (分身) — Japanese for alter ego, avatar, one's other self.
aibou.dev — open protocol for AI companions in games