Skip to content

约定目录

在插件包根目录下放一个 commands/ 文件夹、往里丢一个 .ts 文件,命令就出现了——不用在任何地方注册。这组会被 Feature 发现机制自动扫描的目录就是约定目录:每个目录对应一个 Feature 包(feature provider),目录里的文件按命名规则映射为能力(capability)。发现流程:

几个要点。能力的完整 id 形如 owner\0feature\0localName\0 分隔),localName 由目录内的相对路径决定;同一 owner 下 localName(或同一文件来源)重复会抛 DiscoveryConflictError。目录不存在或没有匹配文件时该 Feature 静默跳过——插件只声明自己用到的目录就行。另外,target: server 的模块在 Node 侧加载执行,target: client(pages)则经构建产物在浏览器加载。

目录一览

目录文件形态递归targetFeature 包featureId默认导出
commands/.ts / .tsx,支持动态参数文件是(子目录拼层级)server@zhin.js/commandzhin.commanddefineCommand(...)
middlewares/.tsserver@zhin.js/middlewarezhin.middlewaredefineMiddleware(...)
components/.ts / .tsxserver@zhin.js/componentzhin.componentdefineComponent(...)
adapters/.tsserver@zhin.js/adapterzhin.adapterdefineAdapter(...)
tools/.tsserver@zhin.js/toolzhin.agent-tooldefineAgentTool(...)
skills/子目录 + SKILL.md一层server@zhin.js/skillzhin.skillMarkdown 文本
agents/*.agent.mdserver@zhin.js/agent-featurezhin.agentMarkdown 文本
mcp/.tsserver@zhin.js/mcp-featurezhin.mcpdefineMcp(...)
pages/.ts / .tsx,含 $nav / $footer 布局槽client@zhin.js/page / @zhin.js/layoutzhin.page / zhin.layout页面构件

命名规则

通用段规则只有一条:目录名、普通文件名去扩展名后,必须匹配 ^[a-z0-9][a-z0-9-]*$,即小写字母/数字开头、可含连字符。不匹配的文件被跳过。各目录的补充规则:

目录localName 推导示例
commands/子目录与文件名用 / 拼接;动态参数文件 [name:type=default].ts(x) 映射为 $name 段,type ∈ string|number|boolean=default 可省commands/lottery-today.tslottery-todaycommands/lottery/[game:string=].tslottery/$game
middlewares/相对路径去扩展名,/ 拼接middlewares/keyword-reply.tskeyword-reply
components/同上components/share-music.tsshare-music
adapters/同上adapters/napcat.tsnapcat
tools/文件名去扩展名(不递归子目录)tools/music-search.tsmusic-search
skills/子目录名即 localName,目录内必须含 SKILL.mdskills/memory-consolidate/SKILL.mdmemory-consolidate
agents/文件名去掉 .agent.md 后缀agents/planner.agent.mdplanner
mcp/文件名去扩展名(不递归)mcp/my-server.tsmy-server
pages/文件名去扩展名;$nav.tsx / $footer.tsx 是布局槽(同 slot 同时有 .ts.tsx 时以 .tsx 为准)pages/orchestration.tsxorchestrationpages/$nav.tsxnav

命令动态参数文件的方括号语法写错(如不支持的类型)会抛 CommandPathSyntaxError,提示 expected [name:string|number|boolean=default].ts(x)

各目录的最小形态

commands/ — defineCommand

ts
// plugins/utils/lottery/commands/lottery-today.ts
import { defineCommand } from '@zhin.js/command';

export default defineCommand<LotteryConfig>({
  description: 'Show today published recommendation report',
  async execute({ owner, use }) {
    const db = resolveLotteryRuntime({ owner, use })?.db ?? getLotteryDb();
    if (!db) return '数据库未就绪';
    // …返回字符串即回复
  },
});

middlewares/ — defineMiddleware

ts
// plugins/utils/group-suite/middlewares/keyword-reply.ts(节选)
import { defineMiddleware } from '@zhin.js/middleware';

export default defineMiddleware<Message, GroupSuiteConfig>({
  target: 'inbound',
  async handle(context, next) {
    const config = resolveGroupSuiteConfig(context.config);
    if (!config.keywordReply) {
      await next();
      return;
    }
    // …命中关键词则回复,否则 await next() 放行
  },
});

adapters/ — defineAdapter

ts
// plugins/adapters/napcat/adapters/napcat.ts(节选)
import { defineAdapter } from '@zhin.js/adapter';

export default defineAdapter<NapCatAdapterConfig>({
  capabilities: ['inbound', 'outbound'],
  create(context) {
    const config = resolveNapCatConfig(context.config);
    const gateway = context.use(messageGatewayToken);
    return new NapCatWsEndpoint({ id: context.id, gateway, config });
  },
});

capabilities 至少含 inbound / outbound 之一;create 返回的 Endpoint 生命周期见 WS/SSE 端点生命周期

tools/ — defineAgentTool

ts
// plugins/utils/music/tools/music-search.ts(节选)
import { defineAgentTool } from '@zhin.js/tool';

export default defineAgentTool<{ keyword: string; source?: MusicSource; limit?: number }>({
  description: '搜索音乐并返回结果列表',
  inputSchema: {
    type: 'object',
    properties: { keyword: { type: 'string', description: '搜索关键词' } },
    required: ['keyword'],
  },
  approval: 'never',
  execute: ({ keyword, source, limit }) => searchMusic(String(keyword), source, limit ?? 5),
});

skills/ 与 agents/ — Markdown

skills/<name>/SKILL.md 带 frontmatter(name / description / tools 白名单等),如 examples/full-bot/skills/memory-consolidate/SKILL.md

markdown
---
name: memory-consolidate
description: 回合末或 master 说「记住」时,将 1–3 条可检索事实写入 memory_entries
tools:
  - memory_upsert
  - memory_search
---

agents/<name>.agent.md 是 Agent 人格/指令文件,如 examples/multi-agent-room/agents/planner.agent.md

pages/ — Console 页面

pages/*.tsx 编译为浏览器产物,挂进 Remote Console;examples/full-bot/pages/orchestration.tsx 是现成例子。$nav.tsx / $footer.tsx@zhin.js/layout 消费,注入导航与页脚。

仓库实例

想找生产级参照时,直接翻这些目录:commandsplugins/utils/lottery/commands/(含动态参数 [game:string=].ts);middlewaresplugins/utils/group-suite/middlewares/plugins/games/*/middlewares/componentsplugins/utils/music/components/share-music.tsadaptersplugins/adapters/napcat/adapters/napcat.tstoolsplugins/utils/music/tools/plugins/utils/group-suite/tools/skillsexamples/full-bot/skills/memory-consolidate/agentsexamples/multi-agent-room/agents/pagesexamples/full-bot/pages/orchestration.tsx