This file provides repository-specific instructions for coding agents working on Mioku.
Mioku extends mioki.
miokihandles bot connections, plugin execution, and event dispatch.- Mioku adds plugin metadata discovery, service discovery/loading, help auto-registration, and AI skill auto-loading.
Treat the checked-out source as the source of truth when docs and code differ.
bun install
bun run start
bun run dev
bun run buildUse bun run build as the default validation step after edits in src/, plugins/, or src/services/.
app.ts: local entrypointsrc/index.ts: Mioku startup orchestrationsrc/core/: framework infrastructuresrc/services/*: built-in servicesplugins/*: user-facing pluginsconfig/: runtime configdata/: persistent runtime datatemp/: temporary artifacts such as screenshotsdist/: TypeScript build output
The real startup flow is:
app.tsstarts Mioku.src/index.tsdiscovers plugins fromplugins/*/package.json.src/index.tsdiscovers services fromsrc/services/*/package.json.- Mioku checks for missing services declared by plugins.
miokistarts.plugins/bootruns first because it haspriority: -Infinity.serviceManager.loadAllServices(ctx)initializes all discovered services.registerPluginArtifacts(ctx)auto-registers:- help manifests from
package.json -> mioku.help - plugin AI skills from
skills.tsorskills.js
- help manifests from
- Normal plugins continue running under
mioki.
Important consequence:
- normal plugins should not manually self-register help
- normal plugins should not manually self-register AI skills
For normal plugins, responsibilities are split across files.
Use it for declarative metadata:
- npm package name
- version
- description
mainmioku.servicesmioku.help
Example:
{
"name": "mioku-plugin-example",
"version": "1.0.0",
"description": "Example plugin",
"main": "index.ts",
"mioku": {
"services": ["config", "ai"],
"help": {
"title": "示例插件",
"description": "示例说明",
"commands": [
{
"cmd": "/example",
"desc": "执行示例命令",
"role": "member"
}
]
}
}
}Rules:
mioku.servicesdeclares required servicesmioku.helpis the only supported place for plugin help metadata- do not invent
mioku.skill
Use it for runtime behavior only:
- service lookup via
ctx.services - config registration
- event handlers
- scheduled jobs
- runtime initialization
- cleanup
Use the real repository pattern:
import { definePlugin } from "mioki";
import type { ConfigService } from "../../src/services/config/tpyes";
export default definePlugin({
name: "example",
version: "1.0.0",
description: "Example plugin",
async setup(ctx) {
const configService = ctx.services?.config as ConfigService | undefined;
if (configService) {
await configService.registerConfig("example", "base", {
enabled: true,
});
}
ctx.handle("message", async (event) => {
const text = ctx.text(event).trim();
if (text !== "/example") {
return;
}
await event.reply("ok");
});
return () => {
ctx.logger.info("example unloaded");
};
},
});Do not put these on the plugin object:
helpskill
Use it for global plugin AI tools.
- default-export
AISkill[] - keep handlers deterministic and small
- use repository types from
src/core/types.tsorsrc/index.ts - do not close over setup-local state
Example:
import type { AISkill } from "../../src";
const skills: AISkill[] = [
{
name: "example",
description: "Example plugin tools",
tools: [
{
name: "ping",
description: "Return a simple status message",
parameters: {
type: "object",
properties: {},
required: [],
},
handler: async () => {
return { ok: true };
},
},
],
},
];
export default skills;Do not call aiService.registerSkill(...) from normal plugins. Mioku loads plugin skills automatically through src/core/plugin-artifact-registry.ts.
Use runtime.ts only when skills.ts needs access to state created during setup().
Why:
skills.tsis imported by the framework outside pluginsetup()- it cannot safely depend on setup-local closures
Pattern:
export interface ExampleRuntimeState {
ctx?: any;
cache?: Map<string, string>;
}
const runtimeState: ExampleRuntimeState = {};
export function setExampleRuntimeState(next: ExampleRuntimeState) {
Object.assign(runtimeState, next);
return runtimeState;
}
export function getExampleRuntimeState() {
return runtimeState;
}
export function resetExampleRuntimeState() {
for (const key of Object.keys(runtimeState) as Array<
keyof ExampleRuntimeState
>) {
delete runtimeState[key];
}
}Use shared.ts or utils.ts for pure reusable logic. Keep mutable state in runtime.ts.
Help is auto-loaded from package.json -> mioku.help.
Command item role is:
type CommandRole = "master" | "admin" | "owner" | "member";Do not call helpService.registerHelp(...) from normal plugins unless you are explicitly changing Mioku framework behavior.
Services live under src/services/<name>/.
Each service exports a MiokuService:
export interface MiokuService {
name: string;
version: string;
description?: string;
init(): Promise<void>;
api: Record<string, any>;
dispose?(): Promise<void>;
}Typical shape:
const demoService: MiokuService = {
name: "demo",
version: "1.0.0",
description: "Demo service",
api: {} as DemoServiceAPI,
async init() {
this.api = {
ping() {
return "pong";
},
};
},
};
export default demoService;How services are consumed:
- Plugin declares dependency in
package.json -> mioku.services - Plugin reads API from
ctx.services?.<name>
Example:
import type { ScreenshotService } from "../../src/services/screenshot/types";
const screenshotService = ctx.services?.screenshot as
| ScreenshotService
| undefined;Current non-Minecraft services in this repository:
aiconfighelpscreenshotwebui
Ignore plugins/minecraft and src/services/minecraft unless the task explicitly targets them.
Primary capabilities:
- create named AI instances
- set/get default AI instance
- register/query skills
- register/get chat runtime
- run
generateText(...),generateMultimodal(...),generateWithTools(...), andcomplete(...)
Notes:
- plugin skills are auto-loaded from
skills.ts chat-runtimeis registered by thechatplugin- executable tool loops go through
complete({ executableTools })
Primary capabilities:
registerConfig(...)updateConfig(...)getConfig(...)getPluginConfigs(...)onConfigChange(...)
Notes:
- config files live under
config/<plugin>/<name>.json - existing config is merged with defaults on registration
- the current type file is actually named
src/services/config/tpyes.ts
Primary capabilities:
registerHelp(...)getHelp(...)getAllHelp()unregisterHelp(...)
Framework code uses it for automatic help registration. Normal plugins usually should not.
Primary capabilities:
screenshot(html, options?)screenshotFromUrl(url, options?)cleanupTemp(olderThanMs?)
Notes:
- temp screenshots live under
temp/screenshots - HTML screenshots inject Tailwind via CDN
- browser discovery differs by platform
Primary plugin-facing integration is usually config.md, not direct API calls.
Public API exposed today:
getSettings()
The WebUI service also parses plugin config.md files. Field keys must use:
<configName>.<jsonPath>
Supported config-page field types currently include:
texttextareanumberswitchselectmulti-selectsecretjson
When a plugin may run with multiple bot connections:
- avoid assuming
ctx.botis always the correct sender bot - prefer
ctx.pickBot(ctx.self_id)for outbound actions tied to the current event
Useful context members:
ctx.handle(...)ctx.text(event)ctx.match(...)ctx.segmentctx.loggerctx.cron(...)ctx.pickBot(...)ctx.isOwner(event)ctx.isAdmin(event)
- do not manually call
aiService.registerSkill(...)inside normal plugins - do not manually call
helpService.registerHelp(...)inside normal plugins - do not add
helporskillto the plugin object - do not make
skills.tsdepend on setup-local variables - do not put mutable runtime state into
shared.ts - do not treat legacy Minecraft code as the default service/plugin pattern
- do not “fix”
src/services/config/tpyes.tsimports accidentally unless the task includes that rename
After meaningful changes:
- run
bun run build - if behavior changed, test the affected startup path, command flow, or service interaction locally when practical
If you change repository conventions, update both AGENTS.md and CLAUDE.md in the same change.