Skip to content

AI 驱动开发 ​

Castor 的目标是:你用自然语言描述业务需求,AI 编程工具自行推断技术细节,端到端交付符合项目规范的功能模块(数据表、接口、页面、权限、迁移),并通过验证门禁。

本页介绍这套流程依赖的四样东西:项目上下文 AGENTS.md、各 AI 工具的配置、代码骨架生成器 pnpm scaffold 和验证门禁 pnpm verify。

AGENTS.md:唯一的项目上下文 ​

仓库根目录的 AGENTS.md 是写给 AI 的完整项目说明,所有 AI 工具都以它为准。内容包括:

  • 技术栈、目录结构和命名规则
  • 后端分层规则、路由规范、权限检查写法、横切约定(时间、数值、错误、CSRF)
  • 前端动态路由、页面结构、公共组件、设计 tokens、多语言规则
  • 导入导出规范、RBAC 约定、菜单 ID 分配规则和当前菜单树
  • 字段类型推断表(业务描述 → 字段类型)
  • 反模式清单和标准交付流程

各工具的专属配置文件只做补充,并都指回 AGENTS.md。修改项目约定时,应当先改 AGENTS.md。

更深入的架构说明在 docs/architecture.md,前端 UI 方案在 docs/frontend-design-system.md。

支持的 AI 工具 ​

工具读取的文件
Claude CodeCLAUDE.md;技能在 .claude/skills/(new-feature-autopilot、shadcn-ui-skills)
Codex CLIAGENTS.md(自动读取);技能在 .agents/skills/
Cursor、Windsurf、GitHub Copilot 等AGENTS.md(这些工具都会自动读取)
文档站上的 AI官网 /llms.txt(入口索引,源文件 website/public/llms.txt)
MCP 客户端apps/mcp,见下文 MCP Server

.claude/skills/ 与 .agents/skills/ 的内容保持一致,后端测试 skills-sync.test.ts 会检查两者是否同步。

新功能交付流程 ​

new-feature-autopilot 技能(Claude Code 中输入 /new-feature-autopilot,或直接说“做一个 XX 功能”)按以下五步执行。其他工具通过各自的规则文件遵循同样的流程。

1. 读取上下文 ​

AI 读取 AGENTS.md、docs/templates/ 下的代码骨架模板、现有的参考模块(后端 apps/api/src/modules/admin/users/,前端 apps/web/src/modules/admin/pages/users/index.tsx),以及 apps/api/scripts/seed-rbac.ts 中的菜单树。如果需求可以通过扩展已有模块实现,会优先扩展。

2. 推断技术规格 ​

AI 在内部推断以下内容,不向你询问:

  • 资源名和所属域(admin 或 component_center)
  • 接口路径,例如 /api/admin/customer-orders
  • 字段名与字段类型(依据下方的字段类型推断表)
  • 权限编码:admin 域为 system_<name>,component_center 域为 cc_<name>,按钮权限加 _add / _edit / _delete / _export / _import
  • 前端文件路径、父菜单、迁移名称,以及页面模板(默认是普通列表,需求需要时换成别的页面模板)

推断结果写成一个 spec 文件,并用 pnpm scaffold -- --spec <file> --validate-only 校验;校验结果还会列出接口路径、权限、表名,以及 scaffold 将分配的菜单 ID。

3. 展示业务预览 ​

AI 只展示业务层面的信息,等你确认或调整:

text
客户管理

位置:业务管理 → 客户管理
展示为:列表
功能:列表查看、新增、编辑、删除、导入、导出
字段:
  · 客户名称(必填)
  · 联系电话
  · 状态

确认这样做吗?或者需要调整什么?

只有在数据模型存在不可逆的歧义、需要外部系统配置、或权限边界有安全影响时,AI 才会额外提问。

4. 实现 ​

  1. 用 pnpm scaffold -- --spec <file> 生成模块(可先加 --dry-run 列出将写入的文件)。
  2. 按 db/schema → schema → repository → service → routes 顺序补充业务逻辑。中文标签、必填 / 唯一 / 默认值规则和选项已经由 spec 生成;超出这些的部分(表间关系、跨字段校验、计算字段)在这一步手写。
  3. 打磨前端页面(页面专属文字的译文、额外的校验),或按选定的页面模板重做页面。
  4. spec 里有 menu 时,scaffold 已经把菜单和按钮权限写进 seed-rbac.ts;否则手动添加。然后运行 pnpm seed:rbac -- --incremental。
  5. 审查新生成的迁移 SQL,运行 pnpm db:migrate,并用 psql -d <库名> -c '\d <表名>' 确认表真实存在。
  6. 接口文档:pnpm scaffold 已把模块的接口写进 docs/apifox-full.openapi.json。改了生成的路由、字段或校验,或新增了路由时,按 AGENTS.md 的「OpenAPI writing rules」照代码同步修改。这一步必须做,否则 pnpm verify 的 openapi_sync 检查不通过。

5. 验证门禁 ​

运行 pnpm verify -- --module <name>,失败项由 AI 修复后重新验证。全部通过后输出交付报告,报告中注明迁移版本(如“migrated to 0001_customer”)。

迁移必须真实落库

只生成迁移文件、只通过静态检查都不算完成。必须执行 pnpm db:migrate,用 psql \d 确认,并且 pnpm verify 的 migration_applied 检查通过。

pnpm scaffold ​

pnpm scaffold 一次生成后端模块、前端页面、接口测试和迁移。常用的输入是 spec 文件(中文标签、规则、选项、菜单):

bash
pnpm scaffold -- --spec device.spec.json --validate-only   # 校验,并预览接口、权限、表和菜单
pnpm scaffold -- --spec device.spec.json --dry-run         # 列出将写入的文件,不写入
pnpm scaffold -- --spec device.spec.json                   # 生成

没有 spec 时也可以只给字段列表(标签是英文占位,没有规则和菜单):

bash
# 预览将生成的文件,不写入
pnpm scaffold -- --name customer --domain admin --fields "name:str,phone:str20,status:str20" --dry-run

# 正式生成
pnpm scaffold -- --name customer --domain admin --fields "name:str,phone:str20,status:str20"

参数 ​

参数说明默认值
--name资源名,snake_case,如 customer_order必填
--domain所属域:admin 或 component_centeradmin
--fields字段列表,格式 字段:类型,字段:类型name:str
--spec用 JSON 文件描述模块(代替 --name / --fields),能写中文标签、必填、唯一、默认值、选项和菜单,见下方 spec 文件—
--dry-run只打印将要生成的内容,不写文件、不注册、不生成迁移关闭
--validate-only配合 --spec:只校验规格并说明会生成的接口、权限、表和菜单,不写任何文件;有问题时逐条列出并以 1 退出关闭
--write-schema按脚手架当前的字段类型等规则重新生成 docs/spec.schema.json关闭
--skip-migration不调用 drizzle-kit 生成迁移关闭
--data-scope配合 --fields 时,接入数据权限:表上加 dept_id / created_by,列表、详情、修改、删除、导出按当前用户的数据范围过滤,新建时写入创建人与部门,并生成对应的接口测试。用 --spec 时改在 spec 里写 "dataScope": true(此参数会被忽略)关闭
-h / --help打印用法—

生成内容 ​

已存在的文件会被跳过,不会覆盖。

生成文件说明
apps/api/src/db/schema/<domain-dir>/<name-kebab>.ts表定义 + toDict
apps/api/src/modules/<domain-dir>/<name-kebab>/{schema,repository,service,routes}.ts后端四层
apps/api/test/<admin|cc>-<name-kebab>.test.ts接口基础测试(增删改查、搜索、404、导出、导入模板、导入)
apps/web/src/modules/<module>/api/<name>.ts前端 API 调用,类型来自模块的 OpenAPI 条目(行类型 ApiItem<'/api/admin/<name-kebab>s'>)
前端列表页 index.tsxadmin 域在 pages/<name>/,component_center 域在 pages/patterns/<name>_page/;用公共组件的类型写成(FormValues、DataTableColumn<Row>[])
页面 locales/{en-US,ja-JP}.json公共译文里还没有的译文:模块的 Webhook 事件名(新增 / 修改 / 删除)以及标题、标签等页面文字

<domain-dir> 为 admin 或 component-center,<name-kebab> 是把下划线换成连字符后的资源名。

同时自动完成:

  • 在 apps/api/src/db/schema/index.ts 和 apps/api/src/modules/<domain-dir>/router.ts 注册
  • 把模块的接口写进 docs/apifox-full.openapi.json,并据此重新生成前端 API 类型(apps/web/src/shared/api/openapi.d.ts)
  • spec 里有 menu 时:把菜单和按钮权限写进 apps/api/scripts/seed-rbac.ts,菜单的英文、日文名写进 apps/web/src/locales/menus/
  • 执行 drizzle-kit generate --name <name> 生成迁移

scaffold 会在输出中打印权限编码前缀(Perm prefix)、菜单 component 值和接口路径,手动添加菜单时(用 --fields,或 spec 里没有 menu)直接使用。

字段类型 ​

类型Drizzle 列表单组件说明
strvarchar(100)FormInput
str20varchar(20)FormInput
str50varchar(50)FormInput
str500varchar(500)FormInput
texttextFormTextarea
intintegerFormNumber
floatnumeric(10, 2)FormNumber接口输出为字符串,如 "12.50"
boolbooleanFormSwitch
datedate(字符串模式)FormDateYYYY-MM-DD
datetimetimestamp(字符串模式)FormDateTime
filevarchar(36),存文件中心的文件 IDFormFileUpload列表显示「查看」链接;保存时自动登记引用
imagevarchar(36),存文件中心的文件 IDFormImageUpload列表显示缩略图;保存时自动登记引用
enumvarchar(50),存选项值FormSelect固定选项(只能用 --spec 写 options);列表可按它筛选,并以徽标显示选项名称(颜色取选项的 tone),导出显示名称,导入时名称和值都接受
dictvarchar(100),存字典项的值FormSelect选项来自「数据字典」(--spec 写 dict 字典编码);列表显示字典标签

用 --fields 时未知类型按 str 处理;spec 里写未知类型会报错。id、created_at、updated_at 会自动添加。

字段类型推断 ​

AI 根据业务描述推断类型,你不需要指定:

业务描述关键词类型
名称、标题、姓名、邮箱str
编码、代码、编号str50
手机、电话、颜色str20
选项固定的状态、类型、等级enum(spec 里写 options;只用 --fields 时为 str20)
选项由管理员维护的分类、来源、行业dict(spec 里写数据字典编码)
URL、链接、地址(外部地址)str500
图片、头像、封面、照片image
附件、文件、合同、扫描件file
描述、备注、简介、内容、正文、标签(JSON 字符串)text
金额、价格、费用、成本float
数量、次数、进度、百分比、排序、权重int
日期(无时间)date
时间datetime
是否、启用、禁用、开关bool

spec 文件 ​

--spec 读取一个 JSON 文件。AI 推断出规格后写成这样的文件再生成,中文标签、必填、唯一、默认值、选项和菜单一次到位:

json
{
  "name": "device",
  "title": "设备台账",
  "fields": [
    { "name": "code", "type": "str50", "label": "设备编号", "required": true, "unique": true },
    { "name": "name", "type": "str", "label": "设备名称", "required": true },
    { "name": "status", "type": "enum", "label": "状态", "required": true, "default": "idle",
      "options": [{ "value": "idle", "label": "闲置" }, { "value": "in_use", "label": "使用中", "tone": "success" }] },
    { "name": "category", "type": "dict", "label": "分类", "dict": "device_category" },
    { "name": "price", "type": "float", "label": "采购价格" }
  ],
  "menu": {},
  "i18n": { "en-US": { "设备台账": "Devices", "设备编号": "Device no." }, "ja-JP": { "设备台账": "設備台帳" } }
}
bash
pnpm scaffold -- --spec device.spec.json --validate-only   # 先校验,看清会生成什么
pnpm scaffold -- --spec device.spec.json
  • title 和每个字段的 label 必填;拼错的属性名(如 requried)直接报错,不会被悄悄忽略
  • 完整格式见 docs/spec.schema.json(在 JSON 里写 "$schema": "<相对路径>/docs/spec.schema.json",编辑器就能补全和提示);4 个「一句需求 → spec」示例及逐字段的推断理由见 docs/examples/specs/
  • required:列加 NOT NULL,新增 / 编辑时为空返回 400「<标签>不能为空」,表单标出必填并校验;image / file 不能必填
  • unique:列加 UNIQUE,重复时返回 400;只用于文本和数字类型
  • default:列默认值,新增时留空就用它,表单也预先填好
  • label / title:页面、表头、导入导出和报错里的中文;i18n 是它们的英文、日文,没写的用字段名代替
  • menu:同时把菜单和按钮权限(新增 / 编辑 / 删除 / 导出 / 导入)写进 apps/api/scripts/seed-rbac.ts,默认挂在顶级目录「业务管理」下(ID 1000,第一次生成时创建;模块 ID 从 1001 起);component_center 域的模块则挂在组件示例中心的「页面模板」目录下(ID 4301–4399,接口在 /api/admin/component-center/ 下);parentId 可以指定其他目录;菜单的英文、日文名写进 apps/web/src/locales/menus/
  • dataScope:设为 true 时模块接入数据权限,相当于 --fields 方式下的 --data-scope
  • options[].tone:该选项在列表中的徽标颜色(默认 neutral;状态类字段用 success / warning / danger 等)
  • 生成的接口测试多一条「字段规则」用例,覆盖必填、选项、唯一和默认值

已知限制 ​

  • 只用 --fields 时表达不了必填、唯一、默认值,标题和标签是英文占位——需要这些时用 --spec。
  • 表名固定为资源名加 s,接口路径同理。选资源名时要考虑复数形式。
  • 加了业务规则后,要同步维护生成的接口测试。
  • 后端报错里的字段名是中文标签,英文、日文界面下也显示中文标签。

脚手架不可用时,可以照 docs/templates/ 手写,替换规则见 docs/templates/backend/README.md。

pnpm verify ​

pnpm verify 是交付门禁,全部通过才算完成。

bash
pnpm verify -- --module customer                 # 全部检查
pnpm verify -- --module customer --skip-build    # 跳过前端构建(调试时提速)
pnpm verify -- --module customer --json          # 输出结构化 JSON(stdout 只有 JSON)

检查项 ​

共 16 项检查,分三组。不适用的检查(例如没有数据权限的模块上的 data_scope_filter)会显示为跳过。

全局检查(每次都执行):

检查内容
typescript_compiletsc --noEmit,覆盖 apps/api(含 scripts、test)、apps/mcp 和 apps/web(含测试)
no_local_has_permissionroutes 文件中不得自定义 hasPermission
migration_chaindrizzle 迁移 journal 线性、快照链完整、每条记录都有 SQL、没有多余 SQL
migration_applied对比 journal 与数据库中的 drizzle.__drizzle_migrations,并确认模块表存在
openapi_sync每个已注册的 /api 路由都按 AGENTS.md「OpenAPI writing rules」写进了 docs/apifox-full.openapi.json,请求体也要与代码一致(执行 pnpm openapi:generate -- --dry-run --strict)
docs_pathsAI 上下文文档中引用的路径是否存在(默认只警告,--strict-docs 时阻断)

模块检查(传入 --module 时执行):

检查内容
backend_file后端 routes / repository / service 文件存在
data_scope_filterschema.ts 声明了 DATA_SCOPE 的模块,repository 必须用 dataScopeWhere 过滤;未声明时跳过
frontend_page前端页面文件存在
frontend_api前端 API 文件存在
router_registration路由已在 src/router.ts 或域 router.ts 注册
schema_registration表定义已在 db/schema/index.ts 注册
rbac_seedseed-rbac.ts 中包含该模块的菜单或权限编码

构建与测试(可跳过):

检查内容跳过参数
frontend_build前端 Vite 构建--skip-build
frontend_tests前端 Vitest--skip-frontend-tests
api_tests后端 Vitest(需要测试库)--skip-api-tests

其他参数 ​

参数说明
--skip-db跳过 migration_applied(不连接数据库)
--run-rbac-sync额外执行一次 seed:rbac --incremental(检查项 rbac_sync)
--database-url <url>指定 migration_applied 使用的数据库连接
--strict-docsdocs_paths 失败时阻断

调试过程中可以用跳过参数提速,交付前必须完整跑一次。

MCP Server ​

apps/mcp 把工具链暴露为 MCP 工具,MCP 客户端(如 Claude Desktop)不需要命令行也能走完整的开发流程。

工具作用
get_project_context返回 AGENTS.md 全文和当前模块结构,实现新功能前调用
get_menu_tree返回数据库中的菜单树,用于确定 parent_id 和可用 ID
get_spec_guide返回 spec 的 JSON Schema 和「需求 → spec」示例,写 spec 前调用
validate_spec校验 spec(参数 spec),说明会生成什么;不写文件
scaffold_feature调用 pnpm scaffold:传 spec(推荐),或 name、domain、fields;dry_run 只预览
check_openapi按 AGENTS.md「OpenAPI writing rules」检查接口文档,列出不合规的接口
run_verify调用 pnpm verify --json 并返回结果(参数 module、skip_build)
init_rbac调用 pnpm seed:rbac -- --incremental
run_migration执行 db:generate + db:migrate(参数 message 作为迁移描述)
list_templates列出 docs/templates/ 下的模板

Claude Desktop 配置示例(claude_desktop_config.json):

json
{
  "mcpServers": {
    "castor": {
      "command": "pnpm",
      "args": ["--dir", "/path/to/castorjs", "-s", "mcp"]
    }
  }
}

也可以先构建再用 node 直接运行:

bash
pnpm --filter @castorjs/mcp build
node /path/to/castorjs/apps/mcp/dist/index.js

MCP Server 默认以自身所在位置推算仓库根目录,可用环境变量 CASTOR_KIT_ROOT 覆盖。

相关页面 ​

Released under the MIT License.