一键导入
yaebal-author-plugin
Use when writing a custom yaebal plugin — the Plugin<In, Out> contract, derive vs decorate, and typed plugin dependencies.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when writing a custom yaebal plugin — the Plugin<In, Out> contract, derive vs decorate, and typed plugin dependencies.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | yaebal-author-plugin |
| description | Use when writing a custom yaebal plugin — the Plugin<In, Out> contract, derive vs decorate, and typed plugin dependencies. |
a plugin is a function that receives a composer and returns it with an enriched context type:
export type Plugin<In extends Context = Context, Out extends object = Record<never, never>> = <
C extends In,
>(composer: Composer<C>) => Composer<C & Out>;
In is what the plugin requires on the context, Out is what it adds. users wire it with
bot.install(myPlugin(options)).
import type { Context, Plugin } from "@yaebal/core";
/** options are always an exported interface. */
export interface GreeterOptions {
greeting?: string;
}
/** the context fields this plugin adds — exported so users can type helpers around it. */
export interface GreeterControl {
greet(): Promise<unknown>;
}
export function greeter(options: GreeterOptions = {}): Plugin<Context, GreeterControl> {
const greeting = options.greeting ?? "hi"; // static setup runs once, here
return (composer) =>
composer.derive((ctx) => ({
greet: () => ctx.send(`${greeting}, ${ctx.from?.first_name ?? "there"}!`),
}));
}
composer.derive(fn) — fn runs on every update (may be async). use it for anything
computed from ctx: the current user, per-request state, api wrappers bound to the update.composer.decorate(value) — attached once, zero per-update cost. use it for services,
clients, config, constants. never do per-request work in decorate.next()) goes through
composer.use(async (ctx, next) => { ...; await next(); }) inside the plugin.In parametera plugin that needs another plugin's context declares it in In. install order is then
type-checked; never rely on implicit ordering or runtime probing:
import type { Context, Plugin } from "@yaebal/core";
interface Deps {
session: { visits: number };
}
export function visitBadge(): Plugin<Context & Deps, { badge: string }> {
return (composer) =>
composer.derive((ctx) => ({ badge: `visit #${ctx.session.visits}` }));
}
bot.install(visitBadge()); // ❌ compile error: no session
bot.install(session({ initial: () => ({ visits: 0 }) })).install(visitBadge()); // ✅
install() throws at
runtime if you return a different composer (its middleware would silently detach).Out) interface from the package entry.Plugin<Context, Record<never, never>>
(e.g. a pure guard/drop plugin) — Out is never any.StorageAdapter from @yaebal/sklad as an option instead of
binding to a specific database.Use when building multi-step dialogs in a yaebal bot — choosing between scenes, conversation, and prompt, and wiring their persistence.
Use when writing or modifying a Telegram bot built on the yaebal framework — bot setup, handlers, filter queries, commands, and context typing.
Use when adding LLM features to a yaebal bot with @yaebal/ai — ctx.ai streaming replies, model adapters, conversation memory, and rate limits.
Use when a yaebal bot fails to compile, start, or respond — install-order type errors, ESM specifier errors, Telegram 400/409/429 responses.
Use when running a yaebal bot in production — long polling with @yaebal/runner vs webhooks with @yaebal/web, graceful shutdown, and error handling.
Use when adding inline/reply keyboards or handling button presses in a yaebal bot — @yaebal/keyboard builders and @yaebal/callback-data typed payloads.