Busabase

发布模板

把你已经做好的 Folder 变成一个可安装的应用——busabase-cli export --template、SKILL.md 草稿、目录索引,以及怎么让自托管服务器指向你自己的画廊。

你在一个 space 里做出了点东西:几张表、一个 AirApp、一些示例数据,还有一套你的 agent 照着跑的流程。把它发布成模板,意味着别人可以从一个 URL 把整套装走——而且他们的 agent 会知道怎么用它,因为你的手册跟着一起走了。

三步:导出把手册补完登记进目录


1. 导出这个 Folder

npx busabase-cli export busa-email -o ./busa-email --template

不加 --template 时这就是普通的包导出:一个 busabase.json 加一棵 content/ 树。--template 在这之上多做三件事。

把手册提到包根。 如果这个 Folder 里有一个被标记为本应用手册的 Skill 节点,它会被写到包根成为 SKILL.md(连同 references/agents/scripts/),而不是写进 content/——这样往返一趟才不会造出这个格式本要消除的那份重复。

还没有手册就写一份草稿。 一个有表但没有 Skill 节点的 Folder,会得到一份从真实存在的结构生成的 SKILL.md——表名、字段、有没有应用——凡是需要判断的地方一律留成显式的 TODO

---
name: "busa-email"
description: "一个收件箱审批台。"
metadata:
  busabase:
    template: true
    resources:
      - "settings"
      - "reviews"
---

# busa-email

TODO: describe the job this app does, and when an agent should reach for it.

## Busabase resources

- `settings`: TODO: what this table is for. Fields: name, payload, vault-refs.
- `reviews`: TODO: what this table is for. Fields: subject, batch-id, item-id.
...

这份草稿是确定性生成的,绝不由模型编造。这段文字会变成 agent 照着执行的指令,而"听起来很像那么回事"的对某张表含义的臆造,比一处明摆着要填的空白更糟。每一个 TODO 都是只有你能做的判断——发布前请把它们填掉。

把还缺什么报出来。 导出结束后它会跑一遍模板校验器,把每条失败打成 Not yet a valid template: …,警告也一并列出。导出本身仍然成功——它是在告诉你要修什么,不是拒绝写文件。

slug 会还原成你当初写的样子

如果你正在导出的这个 Folder 本身就是从模板装来的,它的 Base slug 带着安装时的 Folder 前缀(busa-email-settings)。导出会依据归属戳把包内原本的 slug(settings)还原回来,并同步改写 relation 的指向。

不这么做的话,导出 → 安装 → 导出就不是一个定点:装过一次的包再导出会带着前缀,重装就会双重前缀甚至撞名。


2. 把模板补完整

两处需要手工编辑,细节都在模板格式里:

SKILL.md——把 TODO 换掉。说清楚这个应用是干什么的、每张表装什么,以及——要明确写出来——agent 绝不能拿它做什么。agent 是照着那上面写的东西行动的。

busabase.json——导出留下的是占位的 "category": "uncategorized"。至少把它改成一个真实分类。然后是决定你的卡片好不好读的那几个字段:

"template": {
  "category": "email",
  "tags": ["inbox", "triage"],
  "screenshots": ["assets/screenshots/overview.webp"],
  "agentPrompts": [
    "把今天的收件箱过一遍,需要回的先起草一份回复。"
  ],
  "airapp": "busa-email-app",
  "schemaVersion": 1
}

agentPrompts 值得比它看起来更认真地琢磨。它是对*"那我到底该问这东西什么"*最简短的诚实回答——而这个问题,决定了一个装好的应用是被用起来,还是就摆在那儿。

然后推上去:

cd ./busa-email
git init && git add . && git commit -m "Add busa-email template"
git remote add origin https://github.com/acme/busa-email.git && git push -u origin main

到这一步,它已经可以按 URL 安装了,而且它本来就是一个 Agent Skill。被列出来是下一步的事。

发布前先自检一遍: 把它装进你自己 space 的一个临时 Folder(先 busabase-cli install <url> --into-folder scratch --dry-run,再真装)。Base slug 前缀意味着同一个模板可以装进同一个 space 的两个不同 Folder 而不撞车,所以你完全可以拿自己那份来试。


3. 构建目录

模板中心的画廊读的是一个 JSON 文件,而这个文件不是手工维护的——一张卡片给一个只装五张表的包写"6 tables",那是信任缺陷,不是笔误。busabase-cli index 从一个装着模板的仓库 checkout 构建它:

npx busabase-cli index . --repo acme/templates -o templates.json
Flag作用
--repo <owner/repo>必填。 条目里的 subdir 是相对于哪个仓库的
--ref <ref>按哪个 git ref 读取条目(默认 main)——也就是卡片会装的版本
-o, --out <file>写到这里(默认打印)
--check磁盘上的文件与应写出的内容不一致时以非零码退出

index纯本地命令——它读一个目录,不需要 host、API key 或 space。这正是它能当 CI 步骤用的原因:每个 pull request 上带 --check 跑一遍,目录就永远不会和它所描述的仓库漂移。

一个模板会不会被列出,由与安装器同一个校验器决定,所以卡片不可能承诺它的安装做不到的事。自称模板却没通过的包,会连同理由一起发布在 rejected 数组里——最可能读这个文件的人,正是那个发现自己条目不见了的作者,而"你的 skill 不在目录里"却不给原因,是一个构建能产出的最没法行动的消息。

{
  "format": "busabase-template-index@1",
  "repo": "acme/templates",
  "ref": "main",
  "templates": [
    {
      "subdir": "busa-email",
      "name": "busa-email",
      "description": "一个收件箱审批台。",
      "category": "email",
      "tags": ["inbox", "triage"],
      "screenshots": ["assets/screenshots/overview.webp"],
      "agentPrompts": ["把今天的收件箱过一遍……"],
      "version": "1.2.0",
      "license": "MIT",
      "stats": {
        "folders": 1, "docs": 0, "bases": 3, "records": 7,
        "files": 41, "airapps": 1, "skill": true
      }
    }
  ],
  "rejected": [
    {
      "subdir": "half-done",
      "name": "half-done",
      "errors": ["AirApp \"ui\": package.json has no `dev` script. …"]
    }
  ]
}

条目按名字排序,所以仓库没变时重建出来的是逐字节相同的文件——一个 diff 全是噪声的目录,就没人再评审了。


画廊从哪里读

Busabase 默认从 busabase/templates 仓库读 templates.json

https://raw.githubusercontent.com/busabase/templates/main/templates.json

它是一个独立仓库,而不是 busabase/skills 的一个角落:skills 仓库是拿来装通用 skill 的,而单个模板——一个应用的全部源码、它的截图、它的示例数据——很容易就占掉大部分被跟踪的字节。模板放在那里,会让每一次 skill 安装都为每一个模板买单。

自托管服务器用一个环境变量就能指向自己的目录:

BUSABASE_TEMPLATE_CATALOG_URL=https://raw.githubusercontent.com/acme/templates/main/templates.json

拉取发生在服务端:这个文件是跨源的,浏览器逐用户去拉不共享任何缓存,而且让页面自己决定信哪个 host,就把"该信任哪份目录"变成了客户端的决定。结果缓存一小时——目录只有在有人合并了模板时才变,这很少见;而处在速率限制之下的运维者,也不该因为有人反复刷新页面就被耗尽额度。

截图路径会按卡片所安装的同一个仓库与 ref 解析成 URL,所以卡片不会出现"展示的是这一版、装的是那一版"。


检查清单

  • busabase-cli export <folder> -o <dir> --template 跑完没有 Not yet a valid template: 这样的行
  • SKILL.md 里每个 TODO 都填了,包括"这个应用绝不能做什么"
  • template.category 是真实分类,不是 uncategorized
  • 至少一张截图,至少一条 agentPrompts
  • 示例数据是演示而不是数据集(每张表最多 50 条
  • 必填关联无环,且每个 key 都存在于该关联的目标 Base 中
  • 每个 AirApp 都有 dev 脚本,.busabaseignore 没把 package.json 排除掉
  • 已经装进一个临时 Folder,从变更请求开始完整走过一遍
  • CI 里 busabase-cli index . --repo <owner/repo> --check 通过

相关

On this page