name: erp-skeleton-gen description: A2 骨架生成——基于 docs/04 § 零 技术栈 + docs/01-需求清单/README.md 模块索引,生成项目专属的架构文档(docs/04 § 一+、docs/06、docs/07、docs/09)和工具脚本。固定工具文件走 cp,架构文档由 LLM 按大纲生成。 user-invocable: false
allowed-tools: Read Write Edit Skill Grep Glob AskUserQuestion Bash(mkdir *) Bash(cp *) Bash(touch *) Bash(chmod *) Bash(git config *) Bash(cat *) Bash(bash *)
所有输出必须使用中文。
erp-skeleton-gen
前置条件
本 skill 从 docs/08 进度推进而来。
输入文件:
-
docs/04-技术规范.md § 零:erp-scope-lock 写入的技术栈表。本 skill 基于它推导 § 一+ 编码规范、docs/07 依赖清单、docs/09 目录结构、scripts/test.sh 的构建/测试命令。 -
docs/01-需求清单/:A1 已产出 README.md(模块索引表)+ 每模块一份 REQ 卡片(<code>-<name>.md,用户已审阅确认)。A2 只用其中的模块 ID 列表生成 docs/09 的后端/前端模块目录树(module/user//module/order/等),REQ 卡片详情不在本 skill 使用(留给 A4 回填 schema、A5 生成 API/验收清单)。 -
docs/08-模块任务管理.md:末尾 Edit 勾选 A2 checkbox。
产出覆盖提醒:
- 本 skill 会
Write覆盖以下文件:docs/04(拼接 § 零 + § 一+)、docs/06、docs/07、docs/09、scripts/test.sh、scripts/setup-test-db.sh、.githooks/pre-push、.env.local。 - 如果之前运行过本 skill 且用户手动改过这些文件,重新运行会覆盖修改。
执行步骤
步骤 0:打印当前位置流程图
向用户展示当前在 A 阶段流程中的位置(A-only,▶ 标在 A2):
┌──────────────────────────────────────────────────────┐
│ 📋 阶段 A:规划(一次性) │
│ │
│ A0 初始化项目 → A1 锁范围(REQ 卡片) │
│ ↓ │
│ ⏸ 等你审阅 REQ,重新运行 /erp-plan-start 继续 │
│ ↓ │
│ ▶ A2 生成骨架 → A3 初始化 DB → A4 生成 DB 设计 → A5 生成下游文档│
│ ↓ │
│ 规划阶段到此结束 │
└──────────────────────────────────────────────────────┘
A. 读取锁定的输入
用 Read 读取:
-
docs/04-技术规范.md§ 零 技术栈表 -
docs/01-需求清单/README.md模块索引
这两份是本次生成的唯一权威输入,后续所有内容都基于它们推导。
B.1 生成 3 个全新架构文档(docs/06 / 07 / 09)
对下表每个目标文件:
- 用
Read读取对应的大纲模板(只含 section 标题 + HTML 注释形式的填写提示) - 基于步骤 A 的输入,按大纲生成项目专属内容
-
剥掉所有
<!-- -->注释(注释是给 LLM 的提示,不应出现在最终文档里) - 用
Write写入目标路径
| 目标文件 | 大纲模板 |
|---|---|
docs/06-UI交互规范.md(写 § 一 ~ 四,§ 五 占位) |
${CLAUDE_SKILL_DIR}/templates/docs-06-static-template.md |
docs/07-环境配置.md |
${CLAUDE_SKILL_DIR}/templates/docs-07-env-template.md |
docs/09-项目目录结构.md |
${CLAUDE_SKILL_DIR}/templates/docs-09-structure-template.md |
项目专属标识(根包名 / 命名空间)保留 【人工填写:<说明>】 占位,等人工在 docs/09 顶部补填一次后,其他文件复用。
B.2 追加 docs/04 § 一+(保留 § 零 不覆盖)
docs/04 已由 erp-scope-lock 写入 § 零。本步骤追加 § 一 ~ 三。
- 用
Read读取docs/04-技术规范.md(拿现有 § 零 完整内容)。 - 用
Read读取${CLAUDE_SKILL_DIR}/templates/docs-04-skeleton-template.md(§ 一+ 大纲)。 - 基于步骤 A 技术栈,按大纲生成 § 一 ~ 三 的项目专属内容,剥掉 HTML 注释。
- 拼接
<§ 零 原文>\n\n<新生成的 § 一+>→ 用Write写回docs/04-技术规范.md。
C. 生成工具脚本
C.1 零槽位文件:纯 cp
mkdir -p scripts .githooks sql/migrations
touch sql/migrations/.gitkeep
cp "${CLAUDE_SKILL_DIR}/templates/env-local-template" .env.local
cp "${CLAUDE_SKILL_DIR}/templates/githooks-pre-push-template.sh" .githooks/pre-push
cp "${CLAUDE_SKILL_DIR}/templates/scripts-setup-test-db-template.sh" scripts/setup-test-db.sh
说明:
-
sql/migrations/是 Flyway migration 目录。A3erp-db-init会在此生成V1__initial_schema.sql,后续 B 阶段每个 REQ 的业务 DDL 也写在这(V2__xxx.sql/V3__xxx.sql...) -
setup-test-db.sh只做 drop + create 空库,schema 由 Flyway 在 Spring Boot 启动时自动应用 -
硬依赖:项目
pom.xml必须包含flyway-core+flyway-mysql依赖(已列入 docs/04 § 零 技术栈);否则 Spring 启动不会 apply migration,测试会因表不存在而失败 - seed 数据(
sql/seed-data.sql)由 A3erp-db-init从本地 MySQL 导出,开发者选一种装载方式(Spring@Sql/ FlywayR__seed.sql/data.sql);setup-test-db.sh 只负责清空库
C.2 scripts/test.sh —— 直接 Write
基于步骤 A 的技术栈推断 build / lint / unit+integration / e2e 四条命令,直接 Write 完整 scripts/test.sh。
骨架如下(CC 把 4 个 <推断的 ... 命令> 替换成实际值后 Write):
#!/usr/bin/env bash
# scripts/test.sh —— 合并到默认分支(main / master)前的唯一硬闸门。
# 顺序:setup-db → build → lint → unit+integration → e2e → reset-db
# 由 .githooks/pre-push 和 erp-local-test-gate skill(通过子会话)调用。
set -euo pipefail
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$PROJECT_ROOT"
echo "[test.sh] 1/6 setup test db"
./scripts/setup-test-db.sh
echo "[test.sh] 2/6 build"
<推断的 build 命令,如:(cd backend && mvn -DskipTests clean package) && (cd frontend && pnpm build)>
echo "[test.sh] 3/6 lint"
<推断的 lint 命令>
echo "[test.sh] 4/6 unit + integration"
<推断的 unit+integration 命令>
echo "[test.sh] 5/6 E2E"
<推断的 e2e 命令>
echo "[test.sh] 6/6 reset test db"
./scripts/setup-test-db.sh
echo "[test.sh] GREEN"
C.3 赋权 + 配置 git hooks
chmod +x scripts/*.sh .githooks/pre-push
git config core.hooksPath .githooks
D. 追加 .gitignore 忽略项
调用脚本完成合并:
bash "${CLAUDE_SKILL_DIR}/scripts/merge-gitignore.sh" "${CLAUDE_SKILL_DIR}/templates/gitignore-append-template"
脚本行为:
- 若项目根无
.gitignore→ 直接cp整份模板 - 若已有 → 读模板每一行,跳过注释/空行,用
grep -xF整行精确匹配判重(避免.env误匹配.env.local、通配符*.iml被当正则),只追加缺失的规则
E. 占位符智能填充 + .env.local 独立提示 + QA 循环验证
目标:减少用户重复编辑。同一"逻辑占位"(如根包名出现多处)只问用户一次,CC 批量填;派生占位(如 Java 路径从根包名派生)由 CC 自己推断。敏感占位(.env.local 凭据 / 密钥)不进会话上下文,让用户自己去文件里填。
循环执行直到两个条件同时满足: (a) 所有占位符已填(重新扫描命中数 N = 0) (b) 用户选「继续」
E.1 扫描 + 分组
用 Grep 在以下 8 个路径下扫描 【人工填写:,记录每处命中(文件 + 行号 + 说明文字):
docs/04-技术规范.mddocs/06-UI交互规范.mddocs/07-环境配置.mddocs/09-项目目录结构.mdscripts/*.sh.githooks/pre-push.gitignore.env.local
对每个命中提取 【人工填写:<说明>】 的 <说明>,分两组:
-
敏感组:
.env.local路径下的全部命中(密码 / 密钥 / 账号等)。不通过AskUserQuestion问——留给 E.4 提示用户手动编辑。 -
非敏感组:其他文件的命中。按语义聚合成"逻辑占位":
- 说明文字完全相同 → 同一个根占位(多处共用同一值),如"根包名"出现 3 次
- 说明表达派生关系 → 派生占位,派生自某根占位(如"后端 java 根包路径"派生自"根包名")
- 无法归类 → 视作独立根占位单独问
若总 N=0(无任何占位)→ 直接跳 E.5 验证。
E.2 向用户询问非敏感"根占位"
对每个非派生根占位用 AskUserQuestion 询问:
-
question:
请输入 <说明>(将填充到 <N> 处) -
options(给默认值候选 + 自由输入,供用户快速选择;"Other" 由 CC 运行时自动提供,无需显式列):
{"label": "<合理默认值>", "description": "示例默认值"}- (可选)
{"label": "<第二个常见默认>", "description": "..."}
示例(根包名):
question: "请输入 Java 根包名(将填充到 3 处)"
options:
- {"label": "com.example.erp", "description": "示例默认"}
- 用户选默认值 → 取该值
- 用户选 Other → 用户输入的字符串为该值
派生占位不问用户——由 CC 在 E.3 自行推断。
E.3 CC 批量 Edit 填充
对每个根占位:遍历所有命中位置,用
Edit把【人工填写:<说明>】替换为 E.2 用户输入的值。-
对每个派生占位:CC 基于说明文字 + 根占位值自行推断派生值,用
Edit填入。例如:- 根
根包名 = com.xly.erp→ 派生后端 java 根包路径 = backend/src/main/java/com/xly/erp/ - 根
根包名 = com.xly.erp→ 派生测试 java 根包路径 = backend/src/test/java/com/xly/erp/
- 根
若 CC 对某派生占位无法可靠推断(说明不清 / 上下文不足),回退:把该占位当作独立根占位,弹
AskUserQuestion问用户。
E.4 .env.local 敏感占位提示(不弹出 QA)
若敏感组非空,打印提示横幅(只打印,不问):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[erp-skeleton-gen] ⚠️ 敏感占位需你亲自填
📄 文件: ./.env.local
待填字段(<N> 个):
<列出实际命中的字段名,如 DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_SCHEMA / JWT_SECRET>
这些是凭据 / 密钥,CC 不通过会话询问(避免写入聊天记录)。
请直接编辑 .env.local 填入实际值。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
E.5 验证 + QA 循环
用
Grep重新扫【人工填写:于所有 8 个路径,得新命中数 N 和残留清单。打印汇总横幅:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[erp-skeleton-gen] 骨架生成完成,请审阅
📄 架构文档:
✓ docs/04-技术规范.md (§ 一 ~ 三)
✓ docs/06-UI交互规范.md (§ 一 ~ 四)
✓ docs/07-环境配置.md
✓ docs/09-项目目录结构.md
📄 工具文件:
✓ scripts/test.sh
✓ scripts/setup-test-db.sh
✓ .githooks/pre-push
✓ .env.local
✓ .gitignore(新建 / 追加 / 跳过)
✓ sql/migrations/.gitkeep
占位符状态:
<N=0 时打印:✅ 全部填完>
<N>0 时打印:⚠️ 还有 N 处待填:
<文件:行号> — <行内容摘要>
...>
需要调整 → 直接编辑对应文件(尤其 .env.local 敏感字段)
填完后 → 选「继续」放行 A3
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
用
AskUserQuestion询问:-
question:
架构文档审阅 & 占位符补填完毕? -
options:
{"label": "继续", "description": "进入 A3 数据库配置"}{"label": "有疑问想先沟通", "description": "需要讨论或修改"}
-
question:
-
路由:
- 选「有疑问想先沟通」→ 回答用户问题后回 E.5.1 重新扫描
- 选「继续」→ 回 E.5.1 重新扫描验证:
- 新 N = 0 → 进入步骤 F(放行 A3)
- 新 N > 0 → 用户以为填完其实还有残留,回 E.5.2 打印新横幅并再次弹出 QA
关键点:每次弹出 QA 前都重新扫描一次,用户看到的 N 始终是最新的;只有 N = 0 且选「继续」才放行。
F. 勾选 docs/08 进度 + 进入 A3
-
用
Edit在docs/08-模块任务管理.md勾选 4 个 checkbox(A2 的 3 个子项 + A2 父项):-
- [ ] 架构文档已生成(docs/04 § 一+、docs/06、docs/07、docs/09)→[x] -
- [ ] 工具脚本已生成(scripts/*.sh、.githooks/pre-push、.env.local)→[x] -
- [ ] .gitignore 已配置→[x] -
- [ ] A2 骨架生成 — erp-skeleton-gen→[x]
-
输出
skeleton-gen: 完成(docs/04 § 一+ / docs/06 / docs/07 / docs/09 + scripts/*.sh + .githooks/pre-push + .env.local + .gitignore)。立即调用
Skill(erp-db-init)进入 A3,不等用户手动输入。
参考
-
docs/04-技术规范.md§ 零(技术栈输入) -
docs/01-需求清单/README.md(模块索引输入) -
${CLAUDE_SKILL_DIR}/templates/docs-04-skeleton-template.md(大纲) -
${CLAUDE_SKILL_DIR}/templates/docs-06-static-template.md(大纲) -
${CLAUDE_SKILL_DIR}/templates/docs-07-env-template.md(大纲) -
${CLAUDE_SKILL_DIR}/templates/docs-09-structure-template.md(大纲) -
${CLAUDE_SKILL_DIR}/templates/scripts-setup-test-db-template.sh(0 槽位) -
${CLAUDE_SKILL_DIR}/templates/githooks-pre-push-template.sh(0 槽位) -
${CLAUDE_SKILL_DIR}/templates/env-local-template(0 槽位) -
${CLAUDE_SKILL_DIR}/templates/gitignore-append-template(0 槽位) -
${CLAUDE_SKILL_DIR}/scripts/merge-gitignore.sh(.gitignore 逐行判重合并脚本)