创建 DooTask 插件
把「我要一个 DooTask 插件」变成一个结构正确、能装进主程序跑起来的脚手架,外加一个最小可见的示例页/接口。业务逻辑留给后续开发填充,但骨架的每个文件、每条约定都要正确,这样开发者拿到就能直接迭代,而不是先踩一遍配置坑。
核心原则:对照现成同形态插件,别凭空捏
DooTask 的配置约定有文档漂移(例如 menu_items 的菜单模式在不同插件里写成 url_type: 或 type:),照搬过时文档容易出错。最可靠的做法是:选定形态后,找一个同形态的现成插件作为「参照样板」,逐文件对照它的真实写法来生成,而不是套死模板。本技能给出的模板是起点,参照样板是校准基准。
样板与本地参考资料全部用带 ref: 前缀的代表词指代(ref: 是命名空间前缀,把登记条目和正文同名普通词区分开),绝对路径与获取方式统一登记在 references/samples.md(本机有 checkout 就直接读;没有就 git clone --depth=1 到可复用临时目录再读;私有/拿不到则回退本技能 references)。见到 ref:xxx 时先按它的「获取约定」解析。
常用样板(按形态选,详见 references/samples.md):
- 前后端自建镜像型(默认 JS/TS 全栈首选):
ref:crm(TanStack Start,扁平布局与本技能 1:1,优先对照)、ref:asset-hub(Next.js,docs/rules/是权威规范) - 前后端自建镜像型(Go/Python 变体):
ref:approve(Go + MySQL)、ref:ai(Python + Vite 前端) - 代理 + 上游官方镜像型:
ref:memos(包在dootask-plugin/子目录) - 纯配置 / 复用现成镜像型:
ref:mysql-expose-port - 纯前端静态型:参照镜像型,后端换成一个仅托管静态资源的小容器
布局:
ref:crm就是扁平布局(<appid>/含src/+.build.yml+<版本>/),与本技能生成的结构完全一致,优先对照它。ref:asset-hub/ref:memos用dootask-plugin/包裹布局——照抄它们的 Dockerfile / basePath / nginx /@dootask/tools集成写法即可,目录仍落到扁平结构。
生成前先读样板:解析出样板目录后,读它的 config.yml(顶层与版本目录)、docker-compose.yml、nginx.conf、.build.yml,对照真实写法来生成。三档都拿不到时,依赖本技能的参照文件并明确告知用户「未读到现成样板,按规范生成,请重点复核」。
其它本地参考资料(同样登记在 references/samples.md,按代表词获取):ref:appstore-docs(官方插件开发文档)、ref:tools(@dootask/tools 前端 + Go/Node/Python SDK 源码)、ref:app-landing(应用落地目录,只读)、ref:dootask(主程序源码)。
工作流
按顺序推进,每一步把假设说清楚,关键岔路口让用户拍板。
1. 收集需求
至少问清下面这些(用户一句话需求里没有的才问,已知的别重复问):
- 插件名:中文名 + 英文名(
config.yml的name.zh/name.en)。 - appid:小写裸名(如
crm、asset-hub),是镜像名dootask/<appid>、nginx 路径/apps/<appid>、前端 base path 的统一来源。默认取英文名小写连字符,向用户确认。 - 作者:AppStore 发布账号(本机为
kuaifan),只用于部署目录名community_<作者>_<appid>(见第 6 步);不影响 appid/镜像/路径。 - 一句话描述:中英文,进
description。 - 形态:见第 2 步。
- 技术栈:默认走全 JS/TS 栈(前后端一套语言,依赖与工具链统一,最好维护)。
- 前端:Next.js 与 TanStack Start 平级推荐,都配 shadcn/ui + Tailwind。两者各有取舍(Next 更成熟、
ref:asset-hub/ref:kpi有现成 Next.js 参照;TanStack Start 更轻、Vite 底座、类型安全路由),运行时让用户二选一,别替他默认死。 - 后端:默认 Node + TypeScript(用
@dootask/tools的 Node SDK)。 - 其它语言(Go / Python)仍支持,
@dootask/tools有对应 SDK(见references/tools.md),但仅在用户明确要求时才用,不作默认。
- 前端:Next.js 与 TanStack Start 平级推荐,都配 shadcn/ui + Tailwind。两者各有取舍(Next 更成熟、
- 是否需要后端:纯前端就走「纯前端静态型」,有业务接口才加后端。
- 菜单入口:放在哪个位置(
application应用菜单 /application/admin应用管理 /main/menu主菜单)、用哪种打开模式(iframe兼容性最好,默认推荐)。 - 配置字段 fields(可选):安装时让管理员填的参数(端口、密钥、管理员用户等)。
- 首个版本号:默认
0.1.0。
2. 选定形态
四种形态,按需求选一种,然后读 references/forms.md 里对应小节拿到该形态的目录结构、compose、nginx 写法与构建方式:
| 形态 | 何时选 | 镜像 |
|---|---|---|
| 前后端自建镜像型 | 有自己的前端页面 + 后端接口(最常见) | 自建 dootask/<appid> |
| 代理 + 上游官方镜像型 | 想集成一个现成开源服务(如 memos),用轻量代理做鉴权转发 | 上游官方镜像(固定版)+ 自建代理镜像 |
| 纯前端静态型 | 只有前端,无业务后端 | 一个仅托管静态资源的小容器 |
| 纯配置 / 外链型 | 不写代码:复用现成镜像,或只在主程序里加个外链菜单 | 现成镜像或无镜像 |
拿不准时默认「前后端自建镜像型」,它覆盖面最广。
3. 确定落地位置(新目录)
本技能默认在一个新目录里创建独立插件项目,不假设当前已经在某个插件仓库里。
- 确认目标路径:默认
<cwd>/<appid>/,把绝对路径报给用户确认后再动手。 - git 初始化询问:检查目标位置是否已在 git 仓库内(
git -C <目标父目录> rev-parse --is-inside-work-tree)。若不是 git 目录,询问用户是否git init初始化;同意则初始化,不同意就只建普通目录。 - 目录布局——扁平布局:项目根目录就是
<appid>/,直接平铺放config.yml+logo+README*+<版本>/,镜像型再加src/和.build.yml。这与本技能脚本(build_image.sh/upload_to_appstore.sh)预期的布局一致,部署时把整个项目目录当作「插件目录」传入即可。
注:少数现成项目(如 kpi/memos)把 AppStore 包放在
dootask-plugin/子目录、源码与Dockerfile放仓库根。那是它们的历史布局,本技能新建项目统一用上面的扁平布局,更简单且与脚本对齐。
4. 生成骨架
动手前:若用户只给了名字/一句话需求,先问一次(可选)要不要聊聊产品需求(实体、页面、流程、权限)再写代码;已说清楚就直接搭。
按所选形态生成文件。务必遵守的硬约定(细节见 references/config-yml.md、references/forms.md):
- 顶层
config.yml只放元数据:name/description/author/website/tags(都支持en/zh)。标准/镜像型不要把fields/menu_items/hooks放顶层——放版本目录(随版本走)。例外:纯配置型 D1(如ref:mysql-expose-port)没有版本config.yml,fields直接放顶层。 - 版本目录
<版本>/config.yml放功能配置:fields/menu_items/hooks/require_version,以及推荐默认带上的openapi/knowledge_base(见下条)。 - 让主程序 AI 能用上你的插件:适合接入 AI 的插件默认带上对应项——插件有值得被调用的后端操作时声明
openapi(指向后端 OpenAPI/Swagger 规范),主程序的用户/AI 即可doo app call执行你的功能;插件有面向用户、会被问到的用法时随包带一份knowledge_base知识库目录并声明,产品内「AI 助手」即可解答其用法。两者各自独立判断,都没有就都不加。写法/鉴权/目录结构见references/config-yml.md。 - 菜单角标(有「未读/待办」语义时用):菜单可显示数字角标或红点,按 (应用, 菜单, 用户) per-user、插件没打开也生效。给菜单起
key、按语义设badge_clear_on_open(通知类打开即清 / 审批中心等计数由插件维护),后端用应用密钥(内置APP_SECRET/APP_ID)调 SDK 的setAppBadge下推。字段见references/config-yml.md、SDK 用法见references/tools.md。 docker-compose.yml:镜像写dootask/<appid>:${PLUGIN_VERSION};不对外暴露端口(特殊需求除外);环境变量来自 fields 与内置变量;连主程序后端用服务名service、连主 nginx 用nginx、数据库用${DB_HOST}/${DB_PORT}/${DB_DATABASE}/${DB_USERNAME}/${DB_PASSWORD}等内置变量。nginx.conf:location路径必须与menu_items.url的基础路径一致;反代到容器内部服务名:端口;末尾/用来剥离前缀——是否加要看前端有没有用 basePath 自己接管前缀:Next/TanStack basePath 模式不加、代理/剥前缀模式才加,按形态见references/forms.md;SSE/WebSocket 记得关 buffering、带Upgrade/Connection头、调长超时。CHANGELOG.md+CHANGELOG_zh.md:首版写一句初始化说明即可。logo:放一个占位logo.svg(提醒用户替换为真实 logo)。- 右上角是主程序「胶囊」的领地:插件装进弹窗后,主程序会在内容区右上角浮一个胶囊条(「更多」+「关闭」)盖住页面。前端别把自己的操作/关闭按钮放右上角;需调显隐、位置或往「更多」加菜单项时用
setCapsuleConfig(见references/tools.md)。 - 移动端安全距离:移动/沉浸式下屏顶底被系统占位,用
getSafeArea()让布局让位、别用 CSSenv(),详见references/tools.md。 README.md+README_zh.md:这是 AppStore 展示文案,不是开发说明。- 镜像型还要:
.build.yml(image: dootask/<appid>/context: src/dockerfile: src/Dockerfile)和src/。 CLAUDE.md(必须生成,关键):在插件根目录生成一份项目记忆,让后续新会话不丢上下文——它会被每次会话全量注入,价值在「本地参考路径 + 先读本地别上网硬规则 + basePath/端口/部署目录等雷区」,正是新会话最容易忘、会害它跑去联网找 DooTask 文档的部分。做法:读assets/CLAUDE.md.template,填充并裁剪——替换所有{{...}}占位(appid/作者/插件名/技术栈/版本/前端 base 配置文件/产品一句话),并删掉本项目不适用的行,逐行套黄金法则「删了 Claude 会不会犯错,不会就删」,保持精简(CLAUDE.md 越长遵从度越低)。模板已内置一份精简的「本地参考登记」表(取自references/samples.md,让生成的 CLAUDE.md 自带ref:解析依据、脱离本技能也能用):按本插件实际栈裁剪掉不相关的样板行(如选 Next.js 就删ref:crm行、保留ref:asset-hub),用到表外样板时从samples.md补对应ref:行。写到<appid>/CLAUDE.md。如需更系统地写/优化,可参考用户级技能claude-md。
最小可跑示例(这是本技能区别于纯模板的地方):src/ 里给一个能直接看到效果的最小实现——前端一个页面,启动即 appReady() 并 getUserInfo() 把当前用户名显示出来,证明与主程序握手成功;有后端则加一个 /api/.../ping 之类的接口并由前端调用一次。接入方式见 references/tools.md。**有后端时一并给出一份覆盖该 ping 接口的 openapi.yaml 并在 config.yml 声明 openapi,让主程序 AI 能直接 doo app call 调到它——这是「装上就能被 AI 用起来」的最小证明。**目标是「装上就能看到一个活的页面」,不是空壳。
5. 本地构建镜像(镜像型形态)
按 .build.yml 本地构建,镜像 tag 必须等于将要安装的版本号(compose 里 ${PLUGIN_VERSION} 会被替换成该版本),否则安装时找不到镜像:
scripts/build_image.sh <插件目录> <版本号>
# 等价于:docker build -t dootask/<appid>:<版本号> -f <context>/<dockerfile> <context>
构建失败要把错误贴给用户、定位到 Dockerfile/依赖问题,别跳过。
6. 上传到本机应用商店(通过 doo)
把打包后的内容(不含 src/、.build.yml、点文件)打成 .tar.gz,用 doo app upload 导入到本机 DooTask 应用商店(注意:是本机这个开发实例的应用商店,不是公共仓库;公共仓库要靠后续作者发布流程)——等同网页「上传本地应用」,自带后端合规校验,导入后落到 apps/community_<作者>_<appid>/(作者 = 本地 AppStore 账号,本机为 kuaifan):
scripts/upload_to_appstore.sh <插件目录> <版本号> <作者>
打包规则(脚本已实现,理解即可):拷 config.yml + logo.* + README* + 目标 <版本>/ + 其它非版本子目录(如 icon/、resources/);排除 src/、.build.yml、.git 等点文件、非目标的版本目录、以及根目录其它文件(根文件走白名单 config.yml/logo.*/README*,故 CLAUDE.md 等开发文件不会进包)。打成 tar.gz 后调 doo app upload --appid community_<作者>_<appid>,后端做合规校验:缺 config.yml / name 字段非法等会直接报错,不必等到安装时才挂。
前置:本机已装并登录 doo(sudo npm i -g @dootask/cli;doo auth login 或设 DOO_SERVER/DOO_TOKEN)。doo 不可用时脚本立即报错退出。
7. 安装与验证(CLI 全闭环)
doo app upload 走的是后端合规校验通路,与网页「更新应用列表」无关——上传成功即落入本机应用商店,无需再去后台点更新。整套验证可在 CLI 完成:
- 报告已生成的文件清单与镜像构建结果(
docker images | grep <appid>)。 - 列字段并装:
doo app fields community_<作者>_<appid>看 fields 定义(若不为空,说明各项含义/建议值)→doo app install community_<作者>_<appid> [--param K=V ...],每个必填字段用一个--param传值。doo 在装前会做必填校验,缺项立即报错。fields 设计上含密码/密钥的,应在 config.yml 里给default: $random:N(这是 yaml 写法,不是 CLI 参数),后端会在安装时自动生成。 - 验证:
doo app containers community_<作者>_<appid>—— 看容器/服务是否在跑;doo app logs community_<作者>_<appid>—— 看安装/运行日志;- 打开菜单入口
/apps/<appid>,最小示例页应显示当前用户名 → 证明与主程序握手成功。
- 装坏了的回滚:
doo app uninstall community_<作者>_<appid> [--delete-data] --yes;先卸载,再用doo app remove community_<作者>_<appid> --yes彻底清掉社区应用的本地目录(直接 remove 已安装应用会被后端拒绝)。uninstall/remove是危险操作,默认要交互确认;CLI 非交互流程必须显式加--yes(-y),否则会卡在确认提示走不下去。 - 如实说明:本技能完成到「镜像已构建 + 已导入 + 已安装 + 容器在跑」才算闭环;最小示例页用于确认握手,业务逻辑待开发。
不要把「已生成骨架」说成「插件已上线可用」。
参考文件
references/samples.md—— 样板与本地参考登记表:ref:代表词 → 本机路径 + 在线仓库的映射,以及「本地优先 → 缺失则git clone --depth=1到可复用临时目录 → 回退」的获取约定。全技能见到ref:xxx都来这里解析。references/forms.md—— 四种形态各自的目录结构、docker-compose.yml、nginx.conf、构建方式、参照样板与适用场景。选定形态后必读对应小节。references/config-yml.md——config.yml全字段速查:顶层元数据、版本目录的fields(含字段类型与$random/$uuid语法)、menu_items(location/url 变量/打开模式,含url_typevstype的实战说明)、hooks、require_version/conflict_version、docker-compose 内置变量表。references/tools.md——@dootask/tools速查:前端核心 API 与最小握手代码、Go/Node/Python 后端 SDK 的引入与鉴权(默认http://nginx+ token)。references/local-test.md—— 本地构建 + 上传 + 安装验证的完整 CLI 闭环步骤、打包包含/排除规则、常见报错排查。
脚本
scripts/build_image.sh—— 读.build.yml本地构建镜像,tag 自动打成dootask/<appid>:<版本号>。scripts/upload_to_appstore.sh—— 按打包规则打成.tar.gz,通过doo app upload --appid community_<作者>_<appid>导入本机应用商店(前置:本机已装并登录doo)。
资源
assets/CLAUDE.md.template—— 生成到插件根目录的项目记忆模板(第 4 步填充占位后写入<appid>/CLAUDE.md),让后续新会话保留本地路径、硬规则与构建部署命令。模板内自带一份精简的「本地参考登记」表(ref:代表词 → 路径 + 在线仓库,取自references/samples.md):因为生成的插件目录里没有samples.md,把相关条目随模板带过去,生成的 CLAUDE.md 才能自洽解析ref:xxx。生成时按实际栈裁剪,保持精简。