项目结构
一个 agent-bundle 项目就是普通的 Node 包,只是在根目录多了一个文件,并采用约定的 src/ 目录树。
这里没有任何东西是强制的:配置沉默时由约定补齐,而当两者描述同一件事时,配置总是胜出。
目录布局
my-plugin/
├── agent-bundle.config.ts # 项目标识、targets 与策略
├── package.json # 权威的发布版本号与包标识
├── assets/ # 按字节复制到每个产物中的静态文件
└── src/
├── skills/<name>/SKILL.md # 每个目录一个 Skill,并带有自己的资源
├── commands/*.md # 宿主斜杠命令文档
├── rules/*.mdc # 宿主规则文档
├── hooks/*.ts # 由配置引用的生命周期钩子处理器
├── mcp/<server-id>.ts # 手写的 stdio MCP 服务器入口
├── mcp/<server>/ # 或生成式服务器,每个路由一个模块
│ ├── tools/*.tsx
│ ├── resources/*.tsx
│ ├── prompts/*.tsx
│ ├── apps/*.tsx # 编译为自包含 HTML 的浏览器 MCP App
│ └── layout.tsx # 可选的按服务器布局,包裹该服务器的路由
├── scripts/<name>.ts # 产物脚本(.tsx 通过 Agent 渲染器渲染)
├── cli.ts # 单个包 bin
├── cli/**/*.ts # 或路由式 CLI,嵌套即命令路径
├── index.ts # 库入口
├── layout.tsx # 可选的共享布局,包裹每个渲染式路由
├── state.ts # 项目状态定义
└── providers/<name>.ts # 请求上下文 provider
各个根目录的含义
路由与包入口约定精确匹配 .ts 与 .tsx 文件;state 约定则专指 src/state.ts。被发现的条目在
规范化模型中带有 provenance.kind: 'conventional',因此 agent-bundle inspect 能告诉你某个文件
是被约定识别的,还是被配置认领的。
配置与约定
配置保存任何单个文件都无法拥有的内容——项目标识、target 选择与策略:
import { defineConfig } from 'agent-bundle/config';
export default defineConfig({
plugin: { description: 'Evidence-backed project tools.', name: 'my-plugin' },
targets: ['portable', 'codex', 'claude'],
});
只有当你需要约定无法表达的东西时才添加显式声明——不同的路径、target 限制,或退出约定:
import { defineConfig } from 'agent-bundle/config';
export default defineConfig({
plugin: { description: 'Evidence-backed project tools.', name: 'my-plugin' },
scripts: {
// Restricted to one target, so it cannot ride the convention.
'detect-risk': { entry: './src/scripts/detect-risk.ts', targets: ['portable'] },
},
targets: ['portable', 'codex', 'claude'],
});
当项目呈现出前约定时代的写法时,源码校验会报告信息级提示,而不是错误:AB4730 对应一个自行连接
传输层的 stdio 入口(改为默认导出工厂函数即可升级到框架生命周期外壳),AB4731 / AB4732 /
AB4733 对应 src/cli.ts、src/index.ts 或 src/mcp/<server-id>.ts 存在、但被显式配置遮蔽的情形。
bin: false 与 lib: false 这两个退出方式则完全静默。
输出落在哪里
agent-bundle build 会写出两类彼此独立的东西。
宿主产物
在产物根目录下,每个所选 target 一个目录。命令行把该根目录默认为 artifact/,因此它永远不会与下文的包构建
冲突;output.distPath 或 --output 可以移动它:
artifact/
├── agent-bundle.manifest.json # 每个产出文件及其 SHA-256
└── plugin/ # targets: ['plugin'] — 一个多宿主捆绑包
├── .claude-plugin/
├── .codex-plugin/
├── .cursor-plugin/
├── bin/<plugin-name>.mjs # 路由式 CLI,存在 src/cli/** 时出现
├── skills/
├── hooks/
├── mcp/
├── scripts/
├── assets/
├── AGENTS.md
└── INSTALL.md
单宿主布局由 claude、codex、cursor 与 portable 这几个 target 提供。
agent-bundle.manifest.json 位于产物根目录、与各 target 目录并列,记录了每个产出文件及其 SHA-256,因此产物校验是内容寻址的,而不是猜测。
output.distPath 只移动产物根目录;它从不改变每个 target 内部由框架拥有的布局。优先级是 CLI
--output,然后 output.distPath,最后是默认值——对同时输出包构建的 agent-bundle build 是
artifact,对不带 packageOutputs 的编程式 build() 是 dist。取值必须是非空、限定在项目根目录内的
相对 POSIX 路径。
npm 包构建
当项目声明了 bin/lib——或通过约定提供了它们——同一次构建还会在 dist/ 下写出可供 node 消费的
包构建:
dist/
├── bin/<name>.js # 自执行 ESM、shebang、可执行位
├── <stem>.js # 库入口
└── **/*.d.ts # 声明文件,当 lib.dts 开启时
dist 是强制忽略的目录:包输出永远不会进入项目源码快照,也不会进入 Skill 与资源发现。两类输出不得重叠:
在带有包入口的项目上把 output.distPath 或 --output 指向 dist 就是 AB4706。默认值已经把二者分开,
显式写出也无妨:
import { defineConfig } from 'agent-bundle/config';
export default defineConfig({
output: { distPath: 'artifact' },
plugin: { description: 'A CLI plus a plugin.', name: 'my-plugin' },
targets: ['portable', 'claude'],
});
下一步