For AI agents: the complete documentation index is available at https://scriptedalchemy.github.io/agent-bundle/zh/llms.txt, the full documentation bundle is available at https://scriptedalchemy.github.io/agent-bundle/zh/llms-full.txt, and this page is available as Markdown at https://scriptedalchemy.github.io/agent-bundle/zh/guide/start/project-structure.md.
  • 简体中文
  • 项目结构

    一个 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

    各个根目录的含义

    路径表面如何退出
    src/skills/<name>/SKILL.md一个 Skill。目录中其余内容都作为它的资源随行。完全不需要任何声明即可随产物发布。删除该目录,或收窄 skills 配置中的 glob。
    src/commands/*.md扁平的宿主命令文档。frontmatter 按宿主逐一判定:Claude Code 记录了 descriptionargument-hintallowed-toolsmodeldisable-model-invocation,而 Cursor 固定的命令表面是无 frontmatter 的 Markdown。显式指向某个无法表达其所用字段的宿主的命令是 AB4927;隐式选中的宿主收到去掉该字段的正文,validate 给出警告 AB4928inspectomittedFeatures 列出同样的省略。删除该文件。
    src/rules/*.mdc扁平的宿主规则文档,由 Cursor 发射,保留 descriptionglobsalwaysApply。同样的按宿主判定适用:显式 target 为 AB4907,隐式 target 为警告 AB4908删除该文件。
    src/mcp/<server-id>.ts某个已声明、但未指定 entrycommandurl 的 MCP 服务器的 stdio 入口。显式声明 entry
    src/mcp/<server>/{tools,resources,prompts}/*生成式 MCP 服务器路由。路径提供身份;每个模块提供静态 config、schema,以及一个 async 默认 Server Component。routes.servers.<server> 设为 customcommandremote
    src/mcp/<server>/apps/*浏览器 MCP App 入口,编译为自包含 HTML 并注册到生成的服务器上。必须提供静态 config.resourceUri使用自定义服务器,或给文件名加 _ 前缀。
    src/scripts/<name>.ts一个普通脚本,在每个所选 target 中编译为 scripts/<name>.mjs。嵌套模块是硬错误(AB4808)。给某一段路径加 _ 前缀,或用显式 scripts 条目认领该文件。
    src/scripts/<name>.tsx渲染式脚本:async 默认组件接收 argvsignal,并按 CLI 输出契约通过 Agent 渲染器渲染。改名为 .ts、给某一段路径加 _ 前缀,或认领该文件。
    src/cli.ts一个以 plugin.name 命名的包 bin。bin: false
    src/cli/**/*.{ts,tsx}路由式 CLI 命令,编译进一张做过冲突检查的命令图与一个可执行文件。嵌套即身份:src/cli/library/audit.ts<bin> library audit 运行。它取代 src/cli.ts 约定。bin: falseroutes.cli: 'conventional',或给某一段路径加 _ 前缀。
    src/index.ts库输出,带声明文件。lib: false
    src/layout.{ts,tsx}共享文档布局:默认导出一个接收 { children, route, signal } 的组件,在每个渲染式路由——生成式 MCP 工具、资源与提示、渲染式路由 CLI 命令、投影的 MCP 命令与渲染式脚本——外层渲染 Agent.Result。事件路由与浏览器 App 永不被包裹。重命名为 _layout.tsx
    src/mcp/<server>/layout.{ts,tsx}按服务器的布局,嵌套在根布局之内,包裹该生成式服务器的路由。重命名为 _layout.tsx,或把 routes.servers.<server> 设为非生成模式。
    src/state.ts项目状态:默认导出 defineState。生成的 MCP、路由式 CLI 与渲染式脚本的请求作用域都会挂载它。state: false,或改名为 _state.ts
    src/providers/<name>.{ts,tsx}一个请求上下文 provider,挂载在请求句柄的 providers.<camelCaseName> 上。给文件名加 _ 前缀。
    assets/静态资源,按字节复制到每个 target 产物的 assets/ 目录。改为声明顶层 assets 列表。

    路由与包入口约定精确匹配 .ts.tsx 文件;state 约定则专指 src/state.ts。被发现的条目在 规范化模型中带有 provenance.kind: 'conventional',因此 agent-bundle inspect 能告诉你某个文件 是被约定识别的,还是被配置认领的。

    配置与约定

    配置保存任何单个文件都无法拥有的内容——项目标识、target 选择与策略:

    import { 
    const defineConfig: (config: AgentBundleConfig | ConfigFactory) => AgentBundleConfig | ConfigFactory
    defineConfig
    } from 'agent-bundle/config';
    export default
    function defineConfig(config: AgentBundleConfig | ConfigFactory): AgentBundleConfig | ConfigFactory
    defineConfig
    ({
    AgentBundleConfig.plugin: AgentBundlePluginConfig
    plugin
    : {
    AgentBundlePluginConfig.description?: string | undefined
    description
    : 'Evidence-backed project tools.',
    AgentBundlePluginConfig.name: string
    name
    : 'my-plugin' },
    AgentBundleConfig.targets?: string[] | undefined
    targets
    : ['portable', 'codex', 'claude'],
    });

    只有当你需要约定无法表达的东西时才添加显式声明——不同的路径、target 限制,或退出约定:

    import { 
    const defineConfig: (config: AgentBundleConfig | ConfigFactory) => AgentBundleConfig | ConfigFactory
    defineConfig
    } from 'agent-bundle/config';
    export default
    function defineConfig(config: AgentBundleConfig | ConfigFactory): AgentBundleConfig | ConfigFactory
    defineConfig
    ({
    AgentBundleConfig.plugin: AgentBundlePluginConfig
    plugin
    : {
    AgentBundlePluginConfig.description?: string | undefined
    description
    : 'Evidence-backed project tools.',
    AgentBundlePluginConfig.name: string
    name
    : 'my-plugin' },
    AgentBundleConfig.scripts?: Readonly<Record<string, AgentBundleScriptInput>> | undefined
    scripts
    : {
    // Restricted to one target, so it cannot ride the convention. 'detect-risk': {
    AgentBundleScriptEntry.entry: string
    entry
    : './src/scripts/detect-risk.ts',
    AgentBundleScriptEntry.targets?: readonly string[] | undefined
    targets
    : ['portable'] },
    },
    AgentBundleConfig.targets?: string[] | undefined
    targets
    : ['portable', 'codex', 'claude'],
    });

    当项目呈现出前约定时代的写法时,源码校验会报告信息级提示,而不是错误:AB4730 对应一个自行连接 传输层的 stdio 入口(改为默认导出工厂函数即可升级到框架生命周期外壳),AB4731 / AB4732 / AB4733 对应 src/cli.tssrc/index.tssrc/mcp/<server-id>.ts 存在、但被显式配置遮蔽的情形。 bin: falselib: 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

    单宿主布局由 claudecodexcursorportable 这几个 target 提供。 agent-bundle.manifest.json 位于产物根目录、与各 target 目录并列,记录了每个产出文件及其 SHA-256,因此产物校验是内容寻址的,而不是猜测。

    output.distPath 只移动产物根目录;它从不改变每个 target 内部由框架拥有的布局。优先级是 CLI --output,然后 output.distPath,最后是默认值——对同时输出包构建的 agent-bundle buildartifact,对不带 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 { 
    const defineConfig: (config: AgentBundleConfig | ConfigFactory) => AgentBundleConfig | ConfigFactory
    defineConfig
    } from 'agent-bundle/config';
    export default
    function defineConfig(config: AgentBundleConfig | ConfigFactory): AgentBundleConfig | ConfigFactory
    defineConfig
    ({
    AgentBundleConfig.output?: AgentBundleOutputConfig | undefined
    output
    : {
    AgentBundleOutputConfig.distPath?: string | undefined

    The artifact output directory of agent-bundle build, relative to the project root. Defaults to dist. The per-invocation CLI --output flag still wins, but remains subject to the same project-root containment check; absolute and external output paths are unsupported.

    distPath
    : 'artifact' },
    AgentBundleConfig.plugin: AgentBundlePluginConfig
    plugin
    : {
    AgentBundlePluginConfig.description?: string | undefined
    description
    : 'A CLI plus a plugin.',
    AgentBundlePluginConfig.name: string
    name
    : 'my-plugin' },
    AgentBundleConfig.targets?: string[] | undefined
    targets
    : ['portable', 'claude'],
    });

    下一步