diff --git a/README.md b/README.md index 493fc24..afdd2d0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Claude Code 插件:ERP / 后端管理系统全流程开发框架。 -把"从零到 N 个模块上线"的整个流程固化成 **21 个 skill + 1 个 agent + 2 个 hook + 37 份模板**,让 CC 在 schema 演化用 Flyway migration、需求可追溯、人工审核可控的前提下推进编码。 +把"从零到 N 个模块上线"的整个流程固化成 **21 个 skill + 1 个 agent + 2 个 hook + 35 份模板**,让 CC 在 schema 演化用 Flyway migration、需求可追溯、人工审核可控的前提下推进编码。 ## 这个插件做什么 @@ -44,13 +44,13 @@ Claude Code 插件:ERP / 后端管理系统全流程开发框架。 ``` Plan 阶段**三段式**执行,中间有两个人工审阅断点: - - **第一段(首次运行)**:执行 **A0 → A1**(创建骨架 / 锁技术栈 / 填需求 / 生成 REQ 卡片骨架)后**停下**,等你审阅并补全 `docs/01-需求清单//REQ-*.md`(CC 已起草 req_id / title / goal / rules / constraints / acceptance;输入 / 输出 各保留「主表 + 从表1」骨架表,留 `-` 等你按业务补;依赖表 / 依赖接口保留 `TBD(A3/A5 自动补)` 由后续 skill 回填——Plan 阶段第一个人工关口:业务范围) + - **第一段(首次运行)**:执行 **A0 → A1**(创建骨架 / 锁技术栈 / 填需求 / 生成 REQ 卡片骨架)后**停下**,等你审阅并补全 `docs/01-需求清单//REQ-*.md`(CC 已起草 req_id / title / goal / rules / constraints / acceptance;输入 / 输出 各含一句简述 + N 张示例字段表,模板原样复制由你按业务编辑;依赖表 / 依赖接口保留 `TBD(A3/A5 自动补)` 由后续 skill 回填——Plan 阶段第一个人工关口:业务范围) - **第二段(REQ 审阅完重新运行)**:继续 **A2 → A3**(生成骨架 / 从 REQ 正向设计 `docs/03-数据库设计文档.md` 并回填 REQ 依赖表)后再次**停下**,等你审阅 docs/03 的表 / 字段 / 索引 / 外键(第二个人工关口:数据库 schema —— A4 会基于它翻译 DDL 并 apply 到 MySQL,所以这关口与 REQ 审阅同等重要) - **第三段(docs/03 审阅完重新运行)**:执行 **A4 → A5**(解析 docs/03 → 生成 V1 migration → 自动 `DROP+CREATE` 本地 schema 并 apply → 生成下游文档),Plan 完成后再次**停下** 每次运行都会自动接上次停下的地方继续;中途可以随时关闭 CC,下次运行同样的命令即可恢复。Plan 完成后**不会自动进入编码**,需要你手动运行 `/erp-workflow:coding-start`。 - > 两个审阅断点的勾选位置都在 `docs/08-模块任务管理.md § 一`:A1 审阅完后系统已自动勾选 A1 全部子项;A3 审阅完后需要手动勾选「用户已审阅 docs/03 表结构」+ A3 父项 `[ ]` → `[x]`,下次 `/plan-start` 才会派发到 A4。 + > 两个审阅断点的处理方式一致:A1 / A3 完成时 skill 已自动勾选 `docs/08-模块任务管理.md § 一` 该阶段的全部子项 + 顶层;审阅是隐式的——你看完 REQ 卡片 / docs/03 后直接重新运行 `/plan-start`,下次入口就会派发到下一阶段。如果审阅时发现需要修订,直接编辑 docs/01 / docs/03 即可,不依赖 docs/08 的勾选状态。 3. **Coding 阶段入口**(模块循环): ``` @@ -102,7 +102,7 @@ erp-workflow-plugin/ | # | Skill | 作用 | 流程中谁调用 | |---|---|---|---| | A0 | `project-init` | • 依赖检查(`mysql` 在 PATH,缺失则尝试自动安装)
• 空目录初始化:`cp` 模板创建 CLAUDE.md / docs/01/index.md / docs/08
• `git init` | `plan-start` | -| A1 | `scope-lock` | • 引导填项目概述 / 技术栈 / 需求索引
• 按 `docs/01-需求清单//{_module.md, REQ-*.md}` 子目录结构生成 REQ 卡片骨架(CC 起草 req_id / title / goal / rules / constraints / acceptance;输入 / 输出 各保留「主表 + 从表1」骨架,每张表表头 + 1 行 `-` 等人工补;`依赖表 / 依赖接口` 留 `TBD(A3/A5 自动补)`)
• **停下**等人工审阅 + 填输入 / 输出表,审阅完毕用 `/plan-start` 恢复续进 A2 | A0 | +| A1 | `scope-lock` | • 引导填项目概述 / 技术栈 / 需求索引
• 按 `docs/01-需求清单//{_module.md, REQ-*.md}` 子目录结构生成 REQ 卡片骨架(CC 起草 req_id / title / goal / rules / constraints / acceptance;输入 / 输出 各含一句简述 + N 张示例字段表(输入 7 列 / 输出 3 列),全部原样复制自模板,由人工按业务编辑;`依赖表 / 依赖接口` 留 `TBD(A3/A5 自动补)`)
• **停下**等人工审阅 + 改输入 / 输出,审阅完毕用 `/plan-start` 恢复续进 A2 | A0 | | A2 | `skeleton-gen` | • 生成架构文档:docs/04 § 一+ / docs/06 / docs/07 / docs/09
• 生成工具脚本:scripts/*.sh、.githooks/pre-push、.env.local
• 创建 `sql/migrations/` 空目录(Flyway 准备)
• 合并 .gitignore(逐行判重) | `plan-start` | | A3 | `db-design-gen` | • 从 docs/01 REQ 卡片正向设计 `docs/03-数据库设计文档.md`(schema SSoT)
• 回填 REQ 卡片依赖表(`TBD(A3 自动补)` → 实际表名)
• **停下**等人工审阅 docs/03,审阅完毕用 `/plan-start` 恢复续进 A4 | A2 | | A4 | `db-init` | • LLM 解析 docs/03 → `sql/migrations/V1__initial_schema.sql`(DDL only)
• **5 维度全量校验** DDL ↔ docs/03(表名 / 列名+列序 / 类型+nullable+默认值 / 索引名 / FK 名),fail-closed
• 验证 MySQL 连接
• 调 `scripts/setup-test-db.sh` 复用三层防护(与 B 阶段 test.sh 共用)→ DROP+CREATE 空库
• apply V1 + `SHOW TABLES` 行数自检 | A3 | @@ -177,6 +177,22 @@ erp-workflow-plugin/ |---|---|---|---| | `superpower-code-reviewer` | `superpowers:code-reviewer` 5.0.7 agent,仅改 name | 对 REQ diff 做 AI 自审,产出 `must_fix[]` / `nice_to_have[]` / `gaps` | `feature-review` 步骤 1:`Agent(subagent_type=superpower-code-reviewer)` | +## Banners 清单(7 份,`bash cat` 直接输出,绕开 LLM 复读) + +step 0 流程图被抽到独立 `.txt` 文件,SKILL.md 步骤 0 改为 `bash cat` 输出——保证 ASCII 边框对齐不被 LLM 复读破坏 + 减少 LLM 输出 token。 + +| 所属 Skill | Banner 文件 | 用途 | +|---|---|---| +| project-init | `banners/flow.txt` | A 阶段流程图(▶ 标在 A0) | +| scope-lock | `banners/flow.txt` | A 阶段流程图(▶ 标在 A1) | +| skeleton-gen | `banners/flow.txt` | A 阶段流程图(▶ 标在 A2) | +| db-design-gen | `banners/flow.txt` | A 阶段流程图(▶ 标在 A3) | +| db-init | `banners/flow.txt` | A 阶段流程图(▶ 标在 A4) | +| downstream-gen | `banners/flow.txt` | A 阶段流程图(▶ 标在 A5) | +| plan-start | `banners/flow-done.txt` | A 阶段流程图(▶ 标在"规划阶段到此结束",仅 2.1 Plan 完成分支) | + +**字节对齐保证**:每个文件 17 行,每行 visible width = 58 cell(内宽 56 + 2 个 `│` 边框)。改动需重新校准。 + ## Templates 清单(37 份) | 所属 Skill | 模板文件 | 用途 | @@ -185,7 +201,7 @@ erp-workflow-plugin/ | project-init | `docs-01-index-template.md` | 需求清单索引骨架,等用户填模块表 | | project-init | `docs-08-initial-template.md` | 工作流进度文件骨架(Plan A0~A5 checkbox) | | project-init | `docs-04-stack-template.md` | docs/04 § 零 默认技术栈总览(零槽位,cp 即可) | -| scope-lock | `req-card-template.md` | 单张 REQ-XXX-NNN 卡片骨架(6 个 `{{...}}` 占位符由 CC 替换;输入 / 输出 各含「主表 + 从表1」骨架表,留 `-` 等人工补) | +| scope-lock | `req-card-template.md` | 单张 REQ-XXX-NNN 卡片骨架(6 个 `{{...}}` 占位符由 CC 替换;输入 / 输出 各含一句简述 + N 张示例字段表,模板原样复制由人工编辑) | | scope-lock | `_module-template.md` | 模块子目录的 `_module.md` 模块头(4 行:模块代码-名 / 简述 / 依赖模块 TBD / 涉及表 TBD) | | skeleton-gen | `docs-04-skeleton-template.md` | docs/04 § 一+ 编码规范大纲(HTML 注释引导 LLM) | | skeleton-gen | `docs-06-static-template.md` | docs/06 § 一~四 UI 模式大纲 | @@ -196,7 +212,6 @@ erp-workflow-plugin/ | skeleton-gen | `githooks-pre-push-template.sh` | pre-push → 调 scripts/test.sh(0 槽位) | | skeleton-gen | `env-local-template` | 6 字段凭据模板(DB_* + JWT_SECRET) | | skeleton-gen | `gitignore-append-template` | 插件推荐忽略项(`.env.local`、`.tmp/`、构建产物等) | -| db-init | `migration-v1-header-template.sql` | V1 initial migration 文件头部注释 | | db-design-gen | `docs-03-header-template.md` | docs/03 数据库设计头部 | | db-design-gen | `docs-03-table-template.md` | docs/03 单表小节模板 | | downstream-gen | `docs-02-template.md` | docs/02 开发计划 | @@ -204,10 +219,8 @@ erp-workflow-plugin/ | downstream-gen | `docs-05-endpoint-template.md` | docs/05 单接口小节 | | downstream-gen | `docs-06-module-pagelist-template.md` | docs/06 § 五 单模块页面清单 | | downstream-gen | `docs-08-module-row-template.md` | docs/08 § 二 单模块 bullet 行 | -| downstream-gen | `docs-10-header-template.md` | docs/10 验收清单头部 | -| downstream-gen | `docs-10-module-template.md` | docs/10 单模块验收项 | +| downstream-gen | `docs-10-header-template.md` | docs/10 验收清单(项目级 SOP,零槽位 + 引用指针) | | module-start | `module-start-banner-template.md` | 模块启动横幅 | -| module-start | `cross-module-log-template.md` | cross-module 日志头(副本) | | feature-brainstorm | `feature-spec-template.md` | 功能 spec 结构 | | feature-plan | `feature-plan-template.md` | 功能 plan 结构 | | feature-tdd | `commit-message-template.md` | TDD 每步 commit 信息 | @@ -218,7 +231,7 @@ erp-workflow-plugin/ | mr-create | `mr-title-template.md` | MR 标题模板 | | mr-create | `mr-description-template.md` | MR 描述模板(嵌入模块报告) | | interrupt-check | `interrupt-block-template.md` | Blocker 节追加模板 | -| cross-module-log | `cross-module-log-template.md` | cross-module 日志头(主本) | +| cross-module-log | `cross-module-log-template.md` | cross-module 日志头(由 hook log-cross-module.sh 在首次跨模块改动时渲染创建;skill 自身不再读取) | | cross-module-log | `cross-module-log-row-template.md` | 单条改动行模板 | **流程使用情况**:所有模板都被对应 skill 的 `SKILL.md` 引用,没有孤儿模板。 diff --git a/hooks/scripts/log-cross-module.sh b/hooks/scripts/log-cross-module.sh index b48fcbe..0e03f8f 100755 --- a/hooks/scripts/log-cross-module.sh +++ b/hooks/scripts/log-cross-module.sh @@ -65,12 +65,20 @@ log_dir="$project_dir/docs/superpowers/module-reports" mkdir -p "$log_dir" log_file="$log_dir/${current_module}-cross-module.md" if [ ! -f "$log_file" ]; then - { - echo "# 跨模块改动日志 — ${current_module}" - echo "" - echo "| 时间戳 | 目标模块 | 文件 | 改动摘要 | 原因 | 影响评估 |" - echo "|---|---|---|---|---|---|" - } > "$log_file" + # 单一来源:hook 是日志文件的唯一创建者,从插件模板渲染表头。 + plugin_root="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}" + template="$plugin_root/skills/crosscut/cross-module-log/templates/cross-module-log-template.md" + if [ -f "$template" ]; then + sed "s/{{module_name}}/${current_module}/g" "$template" > "$log_file" + else + # 模板缺失的兜底(不该发生):写最小可用表头,避免阻塞当前 Edit/Write + { + echo "# 跨模块改动日志 — ${current_module}" + echo "" + echo "| 时间戳 | 目标模块 | 文件 | 改动摘要 | 原因 | 影响评估 |" + echo "|---|---|---|---|---|---|" + } > "$log_file" + fi fi ts="$(date -u +%FT%TZ)" diff --git a/skills/coding/module-start/SKILL.md b/skills/coding/module-start/SKILL.md index 70124d8..3439876 100644 --- a/skills/coding/module-start/SKILL.md +++ b/skills/coding/module-start/SKILL.md @@ -56,25 +56,21 @@ allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout * - 不存在 → `git checkout -b ` - 若当前工作区有未提交改动且 checkout 失败 → 打印错误并停止(请用户手工处理 dirty state)。 -### 步骤 4:初始化跨模块日志(幂等) - -`docs/superpowers/module-reports/-cross-module.md` — 不存在则从 `${CLAUDE_SKILL_DIR}/templates/cross-module-log-template.md` 创建。 - -### 步骤 5:计算已完成 REQ 集合 `done_reqs[]`(幂等断点恢复关键) +### 步骤 4:计算已完成 REQ 集合 `done_reqs[]`(幂等断点恢复关键) - 对 `req_list[]` 每个 `req_id`,用 `Glob` 查 `docs/superpowers/reviews/*-.md`。 - 命中后用 `Grep`(pattern `^verdict:\s*approve`,`-i` 不敏感)检查首部 verdict。 - 两者都命中 → 加入 `done_reqs[]`。 -### 步骤 6:渲染并打印模块横幅 +### 步骤 5:渲染并打印模块横幅 `Read ${CLAUDE_SKILL_DIR}/templates/module-start-banner-template.md`,填充槽位;`reqs[]` 每项的 `status` 字段根据 `done_reqs[]` 填 `x`(已完成)或空格(未完成)。 -### 步骤 7:推进主循环 +### 步骤 6:推进主循环 - 从 `req_list[]` 取第一个不在 `done_reqs[]` 中的 REQ 作为 `next_req`。 - **没有 `next_req`**(全部完成)→ 调用 `Skill(test-gate)` 进入模块闸门。 -- **有 `next_req`** → 调用 `Skill(feature-brainstorm)` 启动该 REQ 的功能循环。功能循环链(brainstorm → plan → tdd → verify → review)完成后,`feature-review` 在 `verdict=approve` 分支会回调 `Skill(module-start)` —— 再次进入本 skill 时,步骤 5 会把刚通过的 REQ 加入 `done_reqs[]`,步骤 7 自动取下一 REQ,形成可重入推进。 +- **有 `next_req`** → 调用 `Skill(feature-brainstorm)` 启动该 REQ 的功能循环。功能循环链(brainstorm → plan → tdd → verify → review)完成后,`feature-review` 在 `verdict=approve` 分支会回调 `Skill(module-start)` —— 再次进入本 skill 时,步骤 4 会把刚通过的 REQ 加入 `done_reqs[]`,步骤 6 自动取下一 REQ,形成可重入推进。 - 任何停止条件触发(中断触发 / 测试持续失败 / review 5 轮仍 request-changes)→ 停止本模块,不要静默跳下一 REQ。 ## 参考 @@ -83,5 +79,5 @@ allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout * - `docs/08-模块任务管理.md § 二`(模块元数据,含 `MR:` 字段;完成判定以 MR state 为准) - `docs/superpowers/reviews/*.md`(REQ 级进度事实——verdict=approve 即完成) - `${CLAUDE_SKILL_DIR}/templates/module-start-banner-template.md` -- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-template.md` - 下游:`feature-*`、`test-gate` +- 注:跨模块日志文件的创建由 `hooks/scripts/log-cross-module.sh` 在首次跨模块改动时按需完成,本 skill 不再做防御性初始化。 diff --git a/skills/coding/module-start/templates/cross-module-log-template.md b/skills/coding/module-start/templates/cross-module-log-template.md deleted file mode 100644 index e9338fb..0000000 --- a/skills/coding/module-start/templates/cross-module-log-template.md +++ /dev/null @@ -1,8 +0,0 @@ -# 跨模块改动日志 — {{module_name}} - -软规则 S2:本模块开发期间对**非当前模块**代码的改动(无论目标模块是否已 MR merged)记录在此;模块完成报告必须单列「跨模块改动」节完整贴入。漏留痕或未评估影响 → 升级为中断。 - -**本日志由 CC 自主维护**——hook `log-cross-module.sh` 自动落存根(含 `TBD(CC 补)` 占位),CC 调 `cross-module-log` skill 自主推断补「原因 / 影响评估」两列。**不需要人工填写**。 - -| 时间戳 | 目标模块 | 文件 | 改动摘要 | 原因 | 影响评估 | -|---|---|---|---|---|---| diff --git a/skills/crosscut/coding-start/SKILL.md b/skills/crosscut/coding-start/SKILL.md index 3aa8cb1..ab7c9c7 100644 --- a/skills/crosscut/coding-start/SKILL.md +++ b/skills/crosscut/coding-start/SKILL.md @@ -7,20 +7,20 @@ allowed-tools: Skill Read Glob Grep Bash(curl *) Bash(jq *) Bash(git branch *) B **所有输出必须使用中文。** -B 阶段(Coding)的入口分发器。职责:**验证 Plan 已完成 → 按 docs/02 § 二 REQ 序 + MR state 定位当前模块 → 切回默认分支并同步远程 → 派发 module-start**。不直接生成任何文件。开发顺序以 `docs/02 § 二` 为准;完成判定以 `docs/08 条目的 MR: 字段 + GitLab API state` 为准(curl 调用,凭据读 `.env.local` 的 `GITLAB_API_URL` / `GITLAB_TOKEN` / `GITLAB_PROJECT_ID`)。默认分支(`main` 或 `master`)在步骤 4 自动探测,不硬编码。 +B 阶段(Coding)的入口分发器。 ## 执行步骤 ### 步骤 1:确认 docs/08 存在 -用 `Glob` 检查 `docs/08-模块任务管理.md`。 +检查 `docs/08-模块任务管理.md`存在。 - 不存在 → 打印"⚠️ 项目尚未初始化,请先运行 `/erp-workflow:plan-start`"并停下。 ### 步骤 2:Plan 完成性检查 用 `Grep` 在 `docs/08-模块任务管理.md` 搜索 `^- \[ \] A[0-5]`(pattern 可以直接匹配父项未勾的情况;也可以用更宽的 `^[[:space:]]*- \[ \].*A[0-5]` 覆盖子项)。 -- **命中任一 A 未勾** → 打印: +- **命中任一A 阶段未勾选** → 打印并停下: ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [coding-start] ⚠️ Plan 尚未完成 @@ -31,7 +31,6 @@ B 阶段(Coding)的入口分发器。职责:**验证 Plan 已完成 → 继续 Plan 阶段直到 A5 下游文档生成完成,再回来运行 coding-start。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` - **停下**。 - 无命中 → Plan 已完成,进入步骤 3。 diff --git a/skills/crosscut/cross-module-log/SKILL.md b/skills/crosscut/cross-module-log/SKILL.md index 862813e..e6537a0 100644 --- a/skills/crosscut/cross-module-log/SKILL.md +++ b/skills/crosscut/cross-module-log/SKILL.md @@ -18,7 +18,7 @@ allowed-tools: Read Write Edit Bash(git branch *) ## 执行步骤 1. 确定当前模块(从当前 git 分支名推导:`git branch --show-current` → `module-` → 取 `module_id`。`module-start` 步骤 3 保证本 skill 执行时一定处于 `module-*` 分支)。 -2. 打开 `docs/superpowers/module-reports/-cross-module.md`(不存在则从 `${CLAUDE_SKILL_DIR}/templates/cross-module-log-template.md` 初始化)。 +2. 打开 `docs/superpowers/module-reports/-cross-module.md`。**文件不存在 → 打印 `cross-module-log: 无跨模块改动,跳过` 并退出**(hook 是日志文件的唯一创建者;文件缺失意味着本模块周期内 hook 从未追加过条目,没有 TBD 需要补)。 3. 找到「原因」或「影响评估」列中含 `TBD(CC 补)` 的行。 4. 对每个 TBD 行,CC **自主推断**填写以下两列(基于当前 session 的改动上下文 + REQ 卡片 + 目标模块的代码): - **原因**:为什么要修改目标模块的代码?当前模块的哪个需求迫使这样做? @@ -32,6 +32,6 @@ allowed-tools: Read Write Edit Bash(git branch *) ## 参考 -- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-template.md` -- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-row-template.md` +- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-template.md`(仅供 `hooks/scripts/log-cross-module.sh` 创建日志文件时渲染表头;本 skill 自身不再读取) +- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-row-template.md`(行结构参考,hook 内联拼接行字符串,本 skill 直接编辑已有行) - `CLAUDE.md` § 🟡 软规则 S2 diff --git a/skills/crosscut/plan-start/SKILL.md b/skills/crosscut/plan-start/SKILL.md index af030d9..b2ec346 100644 --- a/skills/crosscut/plan-start/SKILL.md +++ b/skills/crosscut/plan-start/SKILL.md @@ -2,7 +2,7 @@ name: plan-start description: A 阶段(Plan)入口与分发器。根据 docs/08 § 一 的 checkbox 状态派发到 A0~A5 对应 skill。Plan 全部完成(A5 已勾)时打印提示让用户运行 /erp-workflow:coding-start 进入 B 阶段。 user-invocable: true -allowed-tools: Skill Read Glob Grep +allowed-tools: Skill Read Glob Grep Bash(cat *) --- **所有输出必须使用中文。** @@ -38,19 +38,13 @@ docs/08 § 一 是**Plan 阶段进度追踪**(A0~A5 的 checkbox)。§ 二 A 阶段所有 checkbox 均 `[x]`。无后续 skill,本步骤**自行打印流程图**,然后**停下**: +```bash +cat "${CLAUDE_PLUGIN_ROOT}/skills/crosscut/plan-start/banners/flow-done.txt" ``` -┌──────────────────────────────────────────────────────┐ -│ 📋 阶段 A:规划(一次性) │ -│ │ -│ A0 初始化项目 → A1 锁范围(REQ 卡片) │ -│ ↓ │ -│ ⏸ 等你审阅 REQ,重新运行 /plan-start 继续 │ -│ ↓ │ -│ A2 生成骨架 → A3 生成 DB 设计 → A4 初始化 DB → A5 生成下游文档│ -│ ↓ │ -│ ▶ 规划阶段到此结束 │ -└──────────────────────────────────────────────────────┘ +再向用户输出完成横幅: + +``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [plan-start] ✅ Plan 阶段全部完成 diff --git a/skills/crosscut/plan-start/banners/flow-done.txt b/skills/crosscut/plan-start/banners/flow-done.txt new file mode 100644 index 0000000..cf83e22 --- /dev/null +++ b/skills/crosscut/plan-start/banners/flow-done.txt @@ -0,0 +1,17 @@ +┌────────────────────────────────────────────────────────┐ +│ 📋 阶段 A:规划(一次性) │ +│ │ +│ A0 初始化项目 │ +│ ↓ │ +│ A1 锁范围(生成 REQ 卡片) → 人工审核 │ +│ ↓ │ +│ A2 生成骨架 │ +│ ↓ │ +│ A3 生成 DB 设计 → 人工审核 │ +│ ↓ │ +│ A4 初始化 DB │ +│ ↓ │ +│ A5 生成下游文档 │ +│ │ +│ ▶ 规划阶段到此结束 │ +└────────────────────────────────────────────────────────┘ diff --git a/skills/plan/db-design-gen/SKILL.md b/skills/plan/db-design-gen/SKILL.md index bb5beb2..b4a23ee 100644 --- a/skills/plan/db-design-gen/SKILL.md +++ b/skills/plan/db-design-gen/SKILL.md @@ -2,7 +2,7 @@ name: db-design-gen description: A3 DB 设计 + REQ 回填——基于 docs/01-需求清单//REQ-*.md 正向设计 docs/03-数据库设计文档.md(业务实体 → 表 + 字段 + 索引 + 外键 + 业务注记),并把回填值写入 REQ 卡片的「依赖表: TBD(A3 自动补)」与模块头的「涉及表: TBD(A3 自动补)」占位。生成完毕停下等人工审阅。 user-invocable: false -allowed-tools: Read Write Edit Grep Glob Skill AskUserQuestion +allowed-tools: Read Write Edit Grep Glob Bash(cat *) --- **所有输出必须使用中文。** @@ -18,74 +18,56 @@ allowed-tools: Read Write Edit Grep Glob Skill AskUserQuestion ### 步骤 0:打印当前位置流程图 -向用户展示当前在 A 阶段流程中的位置(A-only,`▶` 标在 A3): +用 `Bash` 执行 `cat` 命令向用户展示当前位置流程图(stdout 即 ASCII 框图): -``` -┌──────────────────────────────────────────────────────┐ -│ 📋 阶段 A:规划(一次性) │ -│ │ -│ A0 初始化项目 → A1 锁范围(REQ 卡片) │ -│ ↓ │ -│ ⏸ 等你审阅 REQ,重新运行 /plan-start 继续 │ -│ ↓ │ -│ A2 生成骨架 → ▶ A3 生成 DB 设计 → A4 初始化 DB → A5 生成下游文档│ -│ ↓ │ -│ ⏸ 等你审阅 docs/03,重新运行 /plan-start 继续 │ -└──────────────────────────────────────────────────────┘ +```bash +cat "${CLAUDE_PLUGIN_ROOT}/skills/plan/db-design-gen/banners/flow.txt" ``` ### A. 读取设计输入 -用 `Read` / `Glob` 读取: +读: -- `docs/04-技术规范.md` § 一+ 命名规范(表名 / 字段名 / 索引名 / 外键名约定,及主键 / 时间戳 / 软删等审计列约定) -- `docs/01-需求清单/index.md` 模块索引(模块代码 ↔ 中文名) -- `docs/01-需求清单/*/REQ-*.md` 所有 REQ 卡片,提取 `req_id` / `goal` / `输入`(主表+从表的字段清单)/ `输出`(主表+从表的字段清单)/ `跨字段规则` / `边界` / `验收` +- `docs/04-技术规范.md` +- `docs/01-需求清单/index.md` 模块索引 +- `docs/01-需求清单/*/REQ-*.md` 所有 REQ 卡片 -### B. LLM 推导 schema +### B. 推导 schema 基于步骤 A 读到的 REQ + 命名规范,**正向推导**业务实体 → 表 + 字段 + 索引 + 外键。要求: -1. **表名 / 列名 / 索引名 / 外键名** 严格套用 `docs/04 § 一+` 的命名规范 -2. **审计列**:除明确不需要的辅助表外,每张业务表至少含 `created_at` / `updated_at`;如 `docs/04 § 一+` 约定了 `created_by` / `updated_by` / 软删 `deleted_at`,一并补全 -3. **主键**:默认 `id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT`,除非 REQ 明确要求复合主键 / UUID / 业务主键 -4. **外键**:依据 REQ 中的引用关系(如「订单引用客户」),明确列出 `ON DELETE` / `ON UPDATE` 策略;不能确定时默认 `RESTRICT` -5. **索引**:根据 REQ 的查询模式(如"按时间范围查"、"按状态筛选")推导业务索引;外键列默认建索引 -6. **业务注记**:对每张表用一两句话说明业务用途、关键约束、与其他表的关系 +1. 严格套用 `docs/04` 的命名规范 +2. **主键**:模板内置 `iIncrement` 为主键。REQ 明确要求复合主键 / UUID / 业务主键时按 REQ;其他主键变更需同步改 docs-03-header / docs-03-table 两份模板 +3. **外键**:依据 REQ 中的引用关系(如「订单引用客户」),明确列出 `ON DELETE` / `ON UPDATE` 策略;不能确定时默认 `RESTRICT` +4. **索引**:根据 REQ 的查询模式推导业务索引;外键列默认建索引;标准列里 `sBrandsId` / `sSubsidiaryId` 这类多租户隔离列,按业务查询模式建组合索引 +5. **业务注记**:对每张表用一两句话说明业务用途、关键约束、与其他表的关系 -如果某 REQ 表述模糊以致无法推断关键 schema 细节(如:枚举值范围 / 字段长度上限 / 必填性),先按合理默认推导并在该字段「业务含义」列加 `[需用户审阅]` 标注,待步骤 E 用户审阅时调整;**不打断本次推导**。 +如果某 REQ 表述模糊以致无法推断关键 schema 细节(如:枚举值范围 / 字段长度上限 / 必填性),先按合理默认推导并在该字段「业务含义」列加 `【人工填写:需用户审阅】` 标注,待步骤 E 用户审阅时调整;**不打断本次推导**。 ### C. 渲染 docs/03 -1. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-03-header-template.md`,填充 `schema_name`(从 `.env.local` 读 `DB_SCHEMA`,无则填 `【人工填写:DB_SCHEMA】`)、`er_overview`(基于步骤 B 表关系生成的纯文本 ER 概览)。 -2. 渲染「表清单」:每行 `- \`\` — <一句话用途>`。 -3. 对每张表:用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-03-table-template.md`,填充字段 / 索引 / 外键 / 业务注记。 -4. 用 `Write` 写入 `docs/03-数据库设计文档.md`。 +1. 读取 `${CLAUDE_SKILL_DIR}/templates/docs-03-header-template.md`,填充 `schema_name`(从 `.env.local` 读 `DB_SCHEMA`,无则填 `【人工填写:DB_SCHEMA】`)、`er_overview`(纯文本 ER 概览)。 +2. 渲染「表清单」:对每张表:读取并填充 `${CLAUDE_SKILL_DIR}/templates/docs-03-table-template.md`。 +3. 写入 `docs/03-数据库设计文档.md`。 + +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] docs/03-数据库设计文档.md 已生成` ### D. 回填模块头 + REQ 卡片的 TBD 字段 -1. 用 `Glob` 列出 `docs/01-需求清单/*/_module.md`(模块头)和 `docs/01-需求清单/*/REQ-*.md`(REQ 卡片)。 -2. 用 `Grep` 在这些文件中搜索 `TBD(A3 自动补)` 的**行号**(不读全文)。两种行命中: - - `涉及表: TBD(A3 自动补)` → 模块级(仅在 `_module.md`,每文件一次) - - `依赖表: TBD(A3 自动补)` → REQ 级(仅在 `REQ-*.md`,每文件一次) -3. 对每个命中行按类型回填: - - **模块级 `涉及表`**(`_module.md`):用 `Read` 取该文件 `module_code` + `module_name`,聚合步骤 B 中所有归属该模块的表(多表用 `, ` 分隔);`Edit` 替换为 `涉及表: , , ...` - - **REQ 级 `依赖表`**(`REQ-*.md`):从文件名直接得 `req_id`(`REQ-USR-001.md` → `REQ-USR-001`);根据步骤 B 推导结果定位该 REQ 关联的表;`Edit` 替换为 `依赖表: , ` - - **不动** `TBD(A5 自动补)` 的两种行(`依赖接口` REQ 级 / `依赖模块` 模块级)—— 由 A5 `downstream-gen` 生成 docs/05 + 完成模块 DAG 后回填 -4. 用 `Grep` 再次扫 `TBD(A3 自动补)` 应 0 命中;仍有残留则打印残留位置清单并停下。 -5. 打印回填统计:`A3 回填 处模块涉及表 + 处 REQ schema_refs`。 +1. 列出 `docs/01-需求清单/*/_module.md`(模块头)和 `docs/01-需求清单/*/REQ-*.md`(REQ 卡片)。 +2. 在这些文件中搜索 `TBD(A3 自动补)` 的并回填。 不动 `TBD(A5 自动补)` +3. 打印回填统计:`A3 回填 处模块"涉及表" + 处 REQ"依赖表"`。 -### E. 勾选 docs/08 自动项 + 停下等人工审阅 +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] docs/01 各 REQ 卡片"依赖表" + 模块头"涉及表" 已回填` -1. 用 `Edit` 在 `docs/08-模块任务管理.md` 勾选 2 个自动项(**不勾**第 3 个人工闸门,由用户审阅后自己勾): - - ` - [ ] docs/03-数据库设计文档.md 已生成` → `[x]` - - ` - [ ] docs/01 各 REQ 卡片"涉及数据表"已回填` → `[x]` - - ` - [ ] 用户已审阅 docs/03 表结构` 保持 `[ ]`(人工勾) - - `- [ ] A3 DB 设计 + REQ 回填 — db-design-gen` 保持 `[ ]`(最后一个子项勾后再勾父项) +### E. 勾选 A3 顶层 + 停下等人工审阅 -2. 输出 `db-design-gen: docs/03 已生成 + N 处 REQ 已回填依赖表`。 +1. 完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选 A3 顶层(A3 两个子项已在 C / D 步骤分别勾选): + - `- [ ] A3 DB 设计 + REQ 回填 — db-design-gen` -3. 打印停下横幅并**停下**(**不调** `Skill(db-init)`,A4 由用户审阅后手动恢复): +2. 打印停下横幅并**停下**: ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -95,31 +77,22 @@ allowed-tools: Read Write Edit Grep Glob Skill AskUserQuestion ✓ docs/03-数据库设计文档.md ✓ docs/01-需求清单//REQ-*.md 依赖表已回填 + _module.md 涉及表已回填 - ⏸ 现在请你审阅 docs/03(表 / 字段 / 索引 / 外键 / 业务注记)。 + ⏸ 现在请你审阅 docs/03。 重点关注: - 业务实体覆盖是否完整 - 字段类型 / 长度 / 是否可空 / 默认值是否合理 - 索引是否覆盖主要查询模式 - 外键 ON DELETE / ON UPDATE 策略是否符合业务 - - 字段「业务含义」列含 `[需用户审阅]` 标注的位置需逐一确认 + - 字段「业务含义」列含 `【人工填写:需用户审阅】` 标注的位置需逐一确认 - 审阅完成后,到 docs/08 § 一 把 A3 的「用户已审阅 docs/03 表结构」 - 手动勾上(`[ ]` → `[x]`),然后再勾父项 `- [ ] A3 DB 设计 + REQ 回填`, - 再运行: + 审阅完成后,再运行: /erp-workflow:plan-start - (入口会读取 docs/08 进度,自动派发到 A4 db-init) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` **停止**,不调用任何下游 skill。 -## 不变量 - -- 本 skill **不连接任何数据库**,纯文档驱动;MySQL 连接验证 / DDL apply 由 A4 `db-init` 负责 -- REQ 卡片的 `依赖接口` 字段不在此处填充,留给 A5 `downstream-gen` 在生成 docs/05 后按 `TBD(A5 自动补)` 回填 -- DB 设计与 REQ 卡片同等重要,必须留出人工审阅闸门,避免 LLM 推导的表结构未经把关就被 A4 翻译成 DDL 并 apply 到 MySQL - ## 参考 - `${CLAUDE_SKILL_DIR}/templates/docs-03-header-template.md` diff --git a/skills/plan/db-design-gen/banners/flow.txt b/skills/plan/db-design-gen/banners/flow.txt new file mode 100644 index 0000000..1d4c9ac --- /dev/null +++ b/skills/plan/db-design-gen/banners/flow.txt @@ -0,0 +1,17 @@ +┌────────────────────────────────────────────────────────┐ +│ 📋 阶段 A:规划(一次性) │ +│ │ +│ A0 初始化项目 │ +│ ↓ │ +│ A1 锁范围(生成 REQ 卡片) → 人工审核 │ +│ ↓ │ +│ A2 生成骨架 │ +│ ↓ │ +│ ▶ A3 生成 DB 设计 → 人工审核 │ +│ ↓ │ +│ A4 初始化 DB │ +│ ↓ │ +│ A5 生成下游文档 │ +│ │ +│ 规划阶段到此结束 │ +└────────────────────────────────────────────────────────┘ diff --git a/skills/plan/db-design-gen/templates/docs-03-header-template.md b/skills/plan/db-design-gen/templates/docs-03-header-template.md index 85f76df..6ef65d9 100644 --- a/skills/plan/db-design-gen/templates/docs-03-header-template.md +++ b/skills/plan/db-design-gen/templates/docs-03-header-template.md @@ -1,8 +1,22 @@ # 03-数据库设计文档 -Schema: `{{schema_name}}` -Migration 清单: `sql/migrations/V*.sql`(由 Flyway 顺序 apply) -生成方式: 由 A3 `db-design-gen` 基于 `docs/01-需求清单//REQ-*.md` REQ 卡片正向设计生成(schema SSoT)。 +- **Schema**: `{{schema_name}}` +- **Migration 清单**: `sql/migrations/V*.sql`(由 Flyway 顺序 apply) +- **生成方式**: 由 A3 `db-design-gen` 基于 `docs/01-需求清单//REQ-*.md` REQ 卡片正向设计生成(schema SSoT)。 + +## 项目标准列约定 + +下文每张业务表的字段清单都自动包含以下 5 个标准列(匈牙利前缀 `i` int / `s` varchar / `t` datetime)。渲染时由 `docs-03-table-template.md` 模板内置原样输出。 + +| 列名 | 类型 | 可空 | 主键 | 说明 | +|---|---|---|---|---| +| `iIncrement` | int | 否 | 是 | 整数主键 ID(自增方式由实现决定:DB `AUTO_INCREMENT` 或应用 / 触发器分配) | +| `sId` | varchar(100) | 是 | — | 业务 ID(对外暴露的字符串标识,如 UUID / 人类可读编号) | +| `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离) | +| `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离) | +| `tCreateDate` | datetime | 否 | — | 记录创建时间 | + +字典 / 辅助表如有豁免,在该表业务注记里注明豁免原因。 ## ER 关系概览 diff --git a/skills/plan/db-design-gen/templates/docs-03-table-template.md b/skills/plan/db-design-gen/templates/docs-03-table-template.md index dfdc35a..5d16fd6 100644 --- a/skills/plan/db-design-gen/templates/docs-03-table-template.md +++ b/skills/plan/db-design-gen/templates/docs-03-table-template.md @@ -4,6 +4,11 @@ | 字段 | 类型 | Nullable | 默认 | 业务含义 | |---|---|---|---|---| +| `iIncrement` | int | 否 | — | 整数主键 ID(标准列) | +| `sId` | varchar(100) | 是 | — | 业务 ID(标准列) | +| `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离,标准列) | +| `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离,标准列) | +| `tCreateDate` | datetime | 否 | — | 创建时间(标准列) | {{#each columns}} | {{name}} | {{type}} | {{nullable}} | {{default}} | {{business_meaning}} | {{/each}} diff --git a/skills/plan/db-init/SKILL.md b/skills/plan/db-init/SKILL.md index bf485cc..b4b9a0b 100644 --- a/skills/plan/db-init/SKILL.md +++ b/skills/plan/db-init/SKILL.md @@ -2,111 +2,76 @@ name: db-init description: A4 DB 初始化——LLM 解析 docs/03-数据库设计文档.md → 生成 sql/migrations/V1__initial_schema.sql(DDL only,Flyway 初始 migration)→ 全量校验 DDL ↔ docs/03 一致性 → 验证 MySQL 连接 → 调 scripts/setup-test-db.sh 复用三层防护并 DROP+CREATE 空库 → apply V1。 user-invocable: false -allowed-tools: Read Write Edit Glob Skill Bash(mkdir *) Bash(mysql *) Bash(set *) Bash(. .env.local) Bash(sed *) Bash(cat *) Bash(grep *) Bash(./scripts/setup-test-db.sh) +allowed-tools: Read Write Edit Glob Skill Bash(mkdir *) Bash(mysql *) Bash(set *) Bash(. .env.local) Bash(grep *) Bash(bash *) Bash(./scripts/setup-test-db.sh) Bash(cat *) --- **所有输出必须使用中文。** # db-init -## 前置条件 - -- A3 `db-design-gen` 已完成:`docs/03-数据库设计文档.md` 已生成,用户已审阅,docs/08 § 一 A3 全部 checkbox 均 `[x]`。 -- A2 `skeleton-gen` 已完成:`.env.local` 已存在(含 `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASSWORD` / `DB_SCHEMA`)。 - ## 执行步骤 ### 步骤 0:打印当前位置流程图 -向用户展示当前在 A 阶段流程中的位置(A-only,`▶` 标在 A4): +用 `Bash` 执行 `cat` 命令向用户展示当前位置流程图(stdout 即 ASCII 框图): -``` -┌──────────────────────────────────────────────────────┐ -│ 📋 阶段 A:规划(一次性) │ -│ │ -│ A0 初始化项目 → A1 锁范围(REQ 卡片) │ -│ ↓ │ -│ ⏸ 等你审阅 REQ,重新运行 /plan-start 继续 │ -│ ↓ │ -│ A2 生成骨架 → A3 生成 DB 设计 → ▶ A4 初始化 DB → A5 生成下游文档│ -│ ↓ │ -│ 规划阶段最后一步:A5 由本 skill 末尾自动调起 │ -└──────────────────────────────────────────────────────┘ +```bash +cat "${CLAUDE_PLUGIN_ROOT}/skills/plan/db-init/banners/flow.txt" ``` ### A. DDL 生成(不依赖数据库连接) -#### A.1 读 docs/03 并 LLM 翻译为 DDL +#### A.1 读 docs/03 并翻译为 DDL -用 `Read` 读取 `docs/03-数据库设计文档.md`,按字段 / 索引 / 外键 / 业务注记**严格翻译**为: +读取 `docs/03-数据库设计文档.md`,按字段 / 索引 / 外键 / 业务注记**严格翻译**为: -- 每张表一段 `CREATE TABLE \`\` (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='<业务用途>';` +- 每张表一段 `CREATE TABLE` - 字段顺序与 docs/03 表格行序一致;`Nullable` 列直接映射 `NOT NULL` / `NULL`;`默认` 列映射 `DEFAULT `;列尾用 `COMMENT '<业务含义>'` 写注释 -- 索引:在表创建后追加 `CREATE [UNIQUE] INDEX \`\` ON \`\` ();`(主键 / 唯一约束已在 CREATE TABLE 内联的不重复声明) -- 外键:在所有表创建完成后**统一追加**:`ALTER TABLE \`\` ADD CONSTRAINT \`\` FOREIGN KEY (\`\`) REFERENCES \`\` (\`\`) ON DELETE ;`(避免顺序问题) +- 索引 +- 外键:在所有表创建完成后**统一追加** 要求: - **严禁臆造** docs/03 中没有的表 / 字段 / 索引 / 外键 - **严禁省略** docs/03 中已有的列、注释或约束 - 字符集统一 `utf8mb4` + `utf8mb4_unicode_ci`,引擎统一 `InnoDB`,除非 docs/03 业务注记明确要求其他设置 -#### A.2 拼装 V1 文件 +#### A.2 落盘 V1 文件 -用 `Bash` 拼装最终文件: +`Bash`: `mkdir -p sql/migrations`。 -```bash -mkdir -p sql/migrations -PROJECT="<从 CLAUDE.md 读到的项目名称>" -TS="$(date -u +%FT%TZ)" -set -a; . .env.local; set +a -{ - sed -e "s|{{project_name}}|$PROJECT|g" \ - -e "s|{{timestamp}}|$TS|g" \ - -e "s|{{schema_name}}|$DB_SCHEMA|g" \ - "${CLAUDE_SKILL_DIR}/templates/migration-v1-header-template.sql" - echo "" - cat <<'DDL_EOF' -<把 A.1 推导出的 CREATE TABLE / CREATE INDEX / ALTER ADD FK 全部按顺序粘贴在此> -DDL_EOF -} > sql/migrations/V1__initial_schema.sql -``` +用 `Write` 写 `sql/migrations/V1__initial_schema.sql`,内容 = 头部注释 + DDL 主体: -#### A.3 全量校验 DDL ↔ docs/03 一致性 +1. **头部注释**(6 行 SQL 注释): + - `-- Flyway migration V1 — initial schema for `(从 `CLAUDE.md § 🎯 项目概述` 读) + - `-- Generated: `(UTC ISO 8601 时间戳) + - `-- Source: 由 A4 db-init 从 docs/03-数据库设计文档.md 翻译生成(schema SSoT 是 docs/03)` + - `-- This is the FIRST migration; subsequent schema changes must be written as new files sql/migrations/V2__.sql, V3__... etc.` + - `-- Apply: Flyway runs this automatically at Spring Boot startup.` + - `-- Do not hand-edit this file after it is committed; write a new migration instead.` -5 维度全量校验。**任一不一致** → 报错停下,**列出全部差异明细**;V1.sql 保留以便人工 diff。 +2. **DDL 主体**:A.1 推导出的所有 `CREATE TABLE` → `CREATE INDEX` → `ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY`,按此顺序拼接。 -1. **表名集合相等** - - `S_doc` = grep `^## \`\`` of docs/03 - - `S_sql` = grep `CREATE TABLE \`\`` of V1 - - `S_doc - S_sql` 非空(漏表)/ `S_sql - S_doc` 非空(多表)→ 报错列出差集 +#### A.3 校验 V1 ↔ docs/03 集合一致性 + 自主修正 -2. **每张表全列名 + 列序相等**(**遍历 S_sql 全部表,无抽样**) - - 对每张表 t: - - 从 docs/03 抽该表 `### 字段` 表格行序,得 `cols_doc[t]` - - 从 V1 抽该 `CREATE TABLE` 段内 `\`\`` 出现序,得 `cols_sql[t]` - - **数组相等**(含顺序);不等则列出 `
: docs=[...], sql=[...]` +调 `${CLAUDE_SKILL_DIR}/scripts/validate.sh` 做脚本化的校验: -3. **每个字段类型 / Nullable / 默认值一致** - - 对每个 `
.`,归一化后字符串比对。归一化规则: - - 类型主词转小写(`BIGINT` → `bigint`) - - `UNSIGNED` 保留为小写 - - 显示宽度 `INT(11)` → `int`(去括号) - - Nullable:docs/03 `否` ↔ DDL `NOT NULL`;docs/03 `是` ↔ DDL 无 `NOT NULL` - - 默认值:去多余空格 + 统一大小写 - - **`COMMENT` / `COLLATE` / `CHARSET` 不参与比对**(避免装饰差异误报) - - 不一致 → 列出 `
.: docs=, ddl=` - -4. **每张表索引名集合相等** - - `idx_doc[t]` = docs/03 该表 `### 索引` 列表的名字 - - `idx_sql[t]` = V1 中归属该表的 `CREATE INDEX ` + inline `KEY \`\` / UNIQUE KEY \`\``(排除 `PRIMARY` 自动名) - - 集合不等 → 报错列出差集 +```bash +bash "${CLAUDE_SKILL_DIR}/scripts/validate.sh" \ + sql/migrations/V1__initial_schema.sql \ + docs/03-数据库设计文档.md +``` -5. **外键名集合相等**(全局,不分表) - - `fk_doc` = docs/03 各表 `### 外键` 列表的所有名字 - - `fk_sql` = V1 中所有 `ADD CONSTRAINT \`\` FOREIGN KEY` 的名字 - - 集合不等 → 报错列出差集 +退出码与处理: +- `0` → 通过,进入步骤 B +- `1` → **自主修正循环**(最多 3 轮,docs/03 是 SSoT 不动): + 1. 解析 stderr 差异清单,修正 V1.sql + 2. 重跑 validate.sh + 3. 退出 0 → 进入 B;退出 1 且本轮 < 3 → 回步骤 1;本轮 ≥ 3 仍失败 → 停下,打印最终残留差异 + 已尝试的 3 轮修正摘要,让用户介入 +- `2` → 用法错(V1 / docs 路径找不到),打印路径并停下 -校验全部通过 → 进入步骤 B。**任一维度不通过都不许继续**;提示用户 diff `docs/03` ↔ `V1.sql` 后修正 docs/03(SSoT),重跑 A3 让 `db-design-gen` / 本 skill 重新生成 V1。 +完成后(V1 写入并通过 validate.sh 校验),用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] sql/migrations/V1__initial_schema.sql 已生成` +- ` - [ ] DDL 与 docs/03 全量一致` ### B. 数据库环境检查 @@ -126,8 +91,6 @@ done 任一缺失 → 打印缺失字段名并停下,提示用户编辑 `.env.local` 后重跑。 -> 注:密码中含 `$`、`` ` ``、空格、`!` 等字符时,`.env.local` 需用单引号包裹,例如 `DB_PASSWORD='p@ss$w0rd!'`。`set -a; . file; set +a` 与 `source` 行为一致但更明确,配合单引号可避免特殊字符被 shell 展开。 - #### B.2 验证 MySQL 连接 ```bash @@ -136,26 +99,19 @@ mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" -e "SELECT 1;" ``` - **成功** → 进入步骤 C -- **失败** → 打印具体错误(认证 / 主机不可达 / 端口拒接等),提示检查 `.env.local`,**停下**。`sql/migrations/V1__initial_schema.sql` 已在步骤 A 落盘,用户可在修复连接后手动 `mysql < V1.sql`,无需重跑 A4 的 DDL 生成 +- **失败** → 打印具体错误(认证 / 主机不可达 / 端口拒接等),提示检查 `.env.local`,**停下**。 -### C. 自动导入 MySQL +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] .env.local 凭据已验证(mysql -e "SELECT 1" OK)` -#### C.1 调 setup-test-db.sh 做防护 + DROP+CREATE 空库 - -A2 `skeleton-gen` 已生成 `scripts/setup-test-db.sh`,内置三层防护(**单一真相源,与 B 阶段 test.sh 共用同款规则**): -- 防护 1:host 白名单(默认 `localhost / 127.0.0.1 / ::1`,严格相等匹配;可通过 `.env.local` 的 `TEST_DB_ALLOWED_HOSTS` 显式扩展) -- 防护 2:schema 命名后缀检查(必须含 `test` 或以 `_dev / _local / _ci` 结尾) -- 防护 3:远程 host DROP 警告横幅 +### C. 自动导入 MySQL -直接调用,由它处理凭据加载 + 三层防护 + DROP+CREATE: +#### C.1 DROP+CREATE 空库 ```bash ./scripts/setup-test-db.sh ``` -任一防护失败 → 脚本自身停下并打印明细(host 不在白名单 / schema 名不像测试库 / `.env.local` 缺失等),本 skill **立即停下不做后续步骤**。 -`sql/migrations/V1__initial_schema.sql` 已在步骤 A 落盘,用户调整 `.env.local`(如 `TEST_DB_ALLOWED_HOSTS` 加 host)或 schema 命名后可直接重跑 A4,无需重新生成 V1。 - #### C.2 把 V1 灌入已清空的 schema ```bash @@ -166,6 +122,9 @@ mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_SCHEMA" \ 非零退出 → 报错停下,打印 mysql stderr。 +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] setup-test-db.sh 防护通过 + DROP+CREATE + apply V1 已执行` + #### C.3 自检 SHOW TABLES ```bash @@ -180,28 +139,16 @@ EXPECTED=$(grep -c '^## `' docs/03-数据库设计文档.md) ### D. 勾选 docs/08 进度 + 进入 A5 -1. 用 `Edit` 在 `docs/08-模块任务管理.md` 勾选 5 个 checkbox(A4 的 4 个子项 + A4 父项): - - ` - [ ] sql/migrations/V1__initial_schema.sql 已生成` → `[x]` - - ` - [ ] .env.local 凭据已验证(mysql -e "SELECT 1" OK)` → `[x]` - - ` - [ ] setup-test-db.sh 防护通过 + DROP+CREATE + apply V1 已执行` → `[x]` - - ` - [ ] SHOW TABLES 行数 == docs/03 表数量` → `[x]` - - `- [ ] A4 DB 初始化 — db-init` → `[x]` - -2. 输出 `db-init: 完成(V1 ✓, schema=<名称>, tables=,已 apply 到本地 MySQL)`。 - -3. 立即调用 `Skill(downstream-gen)` 进入 A5,不等用户手动输入。 -## 不变量 +1. 完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选(A4 子项 + A4 顶层): + - ` - [ ] SHOW TABLES 行数 == docs/03 表数量` + - `- [ ] A4 DB 初始化 — db-init` -- **DDL 来源唯一**:V1 完全由 docs/03 翻译而来;docs/03 是 schema 单一真相源(SSoT)。后续 V2/V3 由 B 阶段 `feature-tdd` 在 REQ 实现时写入,并**同步**回写 docs/03 对应表小节 -- **测试夹具归属 B 阶段**:测试数据由 B 阶段每个 REQ 在自己的测试代码 / Spring `@Sql` / testcontainers fixture 中按需提供 -- **安全守护单一真相源**:DROP / 写操作的防护逻辑统一在 `scripts/setup-test-db.sh`(三层防护:host 白名单 + schema 命名后缀 + 远程 host 横幅),本 skill 与 B 阶段 `test.sh` 共用同一份规则;本 skill 不重复实现防护 -- **DDL 校验 fail-closed**:A.3 5 维度全量校验任一不通过都不许进入 apply 阶段 -- **失败可恢复**:每次重跑都从空库 `DROP+CREATE` 开始;中途失败重跑无状态残留 +2. 立即调用 `Skill(downstream-gen)` 进入 A5,不等用户手动输入。 ## 参考 -- `${CLAUDE_SKILL_DIR}/templates/migration-v1-header-template.sql`(V1 头部注释) +- `${CLAUDE_SKILL_DIR}/scripts/validate.sh`(A.3 表名 + 每表列名集合校验脚本) - `docs/03-数据库设计文档.md`(DDL 翻译输入,SSoT) - `.env.local`(DB 凭据) - 产物:`sql/migrations/V1__initial_schema.sql`(由 Flyway 在 Spring Boot 启动时验证 / apply) diff --git a/skills/plan/db-init/banners/flow.txt b/skills/plan/db-init/banners/flow.txt new file mode 100644 index 0000000..b8cba75 --- /dev/null +++ b/skills/plan/db-init/banners/flow.txt @@ -0,0 +1,17 @@ +┌────────────────────────────────────────────────────────┐ +│ 📋 阶段 A:规划(一次性) │ +│ │ +│ A0 初始化项目 │ +│ ↓ │ +│ A1 锁范围(生成 REQ 卡片) → 人工审核 │ +│ ↓ │ +│ A2 生成骨架 │ +│ ↓ │ +│ A3 生成 DB 设计 → 人工审核 │ +│ ↓ │ +│ ▶ A4 初始化 DB │ +│ ↓ │ +│ A5 生成下游文档 │ +│ │ +│ 规划阶段到此结束 │ +└────────────────────────────────────────────────────────┘ diff --git a/skills/plan/db-init/scripts/validate.sh b/skills/plan/db-init/scripts/validate.sh new file mode 100644 index 0000000..bed28ad --- /dev/null +++ b/skills/plan/db-init/scripts/validate.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# validate.sh — 校验 V1.sql 与 docs/03 的两个集合一致性: +# 维度 1: 表名集合 +# 维度 2: 每张共有表的列名集合 +# +# 用法: bash validate.sh +# 退出码: +# 0 = 一致 +# 1 = 不一致(差异明细打印到 stderr) +# 2 = 用法错误(路径找不到等) + +set -uo pipefail +export LC_ALL=C # sort / comm 行为确定 + +V1=${1:?missing V1 sql path} +DOC=${2:?missing docs/03 path} + +[ -f "$V1" ] || { echo "validate.sh: V1 not found: $V1" >&2; exit 2; } +[ -f "$DOC" ] || { echo "validate.sh: docs not found: $DOC" >&2; exit 2; } + +ERR=0 + +# ─── 维度 1: 表名集合 ─────────────────────────────────────────── +TABLES_DOC=$(grep -E '^## `[^`]+`' "$DOC" \ + | sed -E 's/^## `([^`]+)`.*/\1/' | sort -u) +TABLES_SQL=$(grep -E '^CREATE TABLE `[^`]+`' "$V1" \ + | sed -E 's/^CREATE TABLE `([^`]+)`.*/\1/' | sort -u) + +ONLY_DOC=$(comm -23 <(echo "$TABLES_DOC") <(echo "$TABLES_SQL")) +ONLY_SQL=$(comm -13 <(echo "$TABLES_DOC") <(echo "$TABLES_SQL")) + +if [ -n "$ONLY_DOC" ] || [ -n "$ONLY_SQL" ]; then + { + echo "=== 维度 1: 表名集合不一致 ===" + [ -n "$ONLY_DOC" ] && { echo " docs/03 有但 V1 无:"; echo "$ONLY_DOC" | sed 's/^/ - /'; } + [ -n "$ONLY_SQL" ] && { echo " V1 有但 docs/03 无:"; echo "$ONLY_SQL" | sed 's/^/ - /'; } + } >&2 + ERR=1 + # 表数差异 → 不再做列校验 + exit 1 +fi + +# ─── 维度 2: 每张共有表的列名集合 ────────────────────────────── +COMMON=$(comm -12 <(echo "$TABLES_DOC") <(echo "$TABLES_SQL")) + +extract_doc_cols() { + local table=$1 + awk -v t="$table" ' + $0 ~ "^## `" t "`" { in_table=1; in_fields=0; next } + in_table && /^## `/ { exit } + in_table && /^### 字段/ { in_fields=1; next } + in_table && in_fields && /^###/ { in_fields=0 } + in_table && in_fields && /^\|/ { + n = split($0, a, "|") + gsub(/^[ ]+|[ ]+$/, "", a[2]) + gsub(/`/, "", a[2]) + if (a[2] != "" && a[2] != "字段" && a[2] !~ /^-+$/) print a[2] + } + ' "$DOC" | sort -u +} + +extract_sql_cols() { + local table=$1 + awk -v t="$table" ' + $0 ~ "^CREATE TABLE `" t "`" { in_table=1; next } + in_table && /^\)/ { in_table=0; next } + in_table && /^[[:space:]]*`[^`]+`/ \ + && $0 !~ /^[[:space:]]*(PRIMARY|UNIQUE|KEY|FOREIGN|CONSTRAINT|INDEX)/ { + match($0, /`[^`]+`/) + print substr($0, RSTART+1, RLENGTH-2) + } + ' "$V1" | sort -u +} + +while IFS= read -r t; do + [ -z "$t" ] && continue + D_COLS=$(extract_doc_cols "$t") + S_COLS=$(extract_sql_cols "$t") + ONLY_D=$(comm -23 <(echo "$D_COLS") <(echo "$S_COLS")) + ONLY_S=$(comm -13 <(echo "$D_COLS") <(echo "$S_COLS")) + if [ -n "$ONLY_D" ] || [ -n "$ONLY_S" ]; then + { + echo "=== 维度 2: 表 \`$t\` 列名不一致 ===" + [ -n "$ONLY_D" ] && { echo " docs/03 有但 V1 无:"; echo "$ONLY_D" | sed 's/^/ - /'; } + [ -n "$ONLY_S" ] && { echo " V1 有但 docs/03 无:"; echo "$ONLY_S" | sed 's/^/ - /'; } + } >&2 + ERR=1 + fi +done <<< "$COMMON" + +if [ $ERR -ne 0 ]; then + exit 1 +fi + +echo "validate.sh: ✓ 表名集合 + 每表列名集合 与 docs/03 一致" +exit 0 diff --git a/skills/plan/db-init/templates/migration-v1-header-template.sql b/skills/plan/db-init/templates/migration-v1-header-template.sql deleted file mode 100644 index 3c5b01f..0000000 --- a/skills/plan/db-init/templates/migration-v1-header-template.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Flyway migration V1 — initial schema for {{project_name}} --- Generated: {{timestamp}} --- Source: 由 A4 `db-init` 从 `docs/03-数据库设计文档.md` 翻译生成(schema SSoT 是 docs/03) --- This is the FIRST migration; subsequent schema changes must be written --- as new files sql/migrations/V2__.sql, V3__..., etc. --- Apply: Flyway runs this automatically at Spring Boot startup. --- Do not hand-edit this file after it is committed; write a new migration instead. --- ========================================================= diff --git a/skills/plan/downstream-gen/SKILL.md b/skills/plan/downstream-gen/SKILL.md index 3aed33b..6cc74ca 100644 --- a/skills/plan/downstream-gen/SKILL.md +++ b/skills/plan/downstream-gen/SKILL.md @@ -2,49 +2,29 @@ name: downstream-gen description: A5 下游文档生成——基于 docs/01 和 docs/03 推导,一次性生成 docs/02 + docs/05 + docs/06 § 五 + docs/10,回填 REQ 卡片依赖接口,把模块清单追加到 docs/08 § 二。 user-invocable: false -allowed-tools: Read Write Edit Glob Grep Skill AskUserQuestion Bash(cat *) Bash(git remote *) +allowed-tools: Read Write Edit Glob Grep Skill AskUserQuestion Bash(cat *) Bash(cp *) --- **所有输出必须使用中文。** # downstream-gen -## 前置条件 - -- `docs/01-需求清单//_module.md` + `docs/01-需求清单//REQ-*.md` 完整 REQ 卡片已就绪(A1 生成 + 人工审阅过 + A3 已回填依赖表)。 -- `docs/03-数据库设计文档.md` 已就绪(A3 生成 + 人工审阅过)。 -- `sql/migrations/V1__initial_schema.sql` 已生成并 apply(A4 完成)。 -- `docs/05` / `docs/06 § 五` / `docs/10` 等下游文件尚不存在(本 skill 创建)。 - ## 执行步骤 ### 步骤 0:打印当前位置流程图 -向用户展示当前在 A 阶段流程中的位置(A-only,`▶` 标在 A5): +用 `Bash` 执行 `cat` 命令向用户展示当前位置流程图(stdout 即 ASCII 框图): -``` -┌──────────────────────────────────────────────────────┐ -│ 📋 阶段 A:规划(一次性) │ -│ │ -│ A0 初始化项目 → A1 锁范围(REQ 卡片) │ -│ ↓ │ -│ ⏸ 等你审阅 REQ,重新运行 /plan-start 继续 │ -│ ↓ │ -│ A2 生成骨架 → A3 生成 DB 设计 → A4 初始化 DB → ▶ A5 生成下游文档│ -│ ↓ │ -│ 规划阶段到此结束 │ -└──────────────────────────────────────────────────────┘ +```bash +cat "${CLAUDE_PLUGIN_ROOT}/skills/plan/downstream-gen/banners/flow.txt" ``` -### A. docs/02 — 开发计划(含 REQ 级开发顺序清单,CC 分发权威) +### A. docs/02 — 开发计划(含 REQ 级开发顺序清单) -**清单粒度**:一行一个 REQ,同一模块的 REQ 必须**连续排列**。 +**清单颗粒度**:一行一个 REQ,同一模块的 REQ 必须**连续排列**。 -1. 构建**模块依赖 DAG**: - - docs/03 中的外键(表 A → 表 B ⇒ A 的模块依赖 B 的模块) - - docs/01 index 中的显式 `depends_on` 提示 -2. 对模块 DAG 做拓扑排序得 `module_topo_order[]`。 -3. 对**每个模块内部**构建 REQ 间依赖(从 REQ 卡片 `goal` / `rules` / `依赖表` 推断 REQ-A 是 REQ-B 的前置),得到模块内 REQ 顺序。 +1. 构建**模块依赖 DAG**。 +3. 对**每个模块内部**构建 REQ 间依赖,得到模块内 REQ 顺序。 4. 合成 `req_order[]`:按 `module_topo_order[]` 依次铺开每个模块内的 REQ 序列(**同模块 REQ 连续**)。 5. **环依赖打破**: - **模块级**:若模块 DAG 存在环(module_A ↔ module_B),按启发式(字母序 / 被依赖次数多者先)破环排出 `module_topo_order`,并在**参与环的模块里第一个 REQ** 的 `note` 字段填入原因(如 "A↔B 互依赖:先做 A 的骨架")。 @@ -54,135 +34,84 @@ allowed-tools: Read Write Edit Glob Grep Skill AskUserQuestion Bash(cat *) Bash( - `index`:行号(从 1 开始) - `req_id`:如 `REQ-SYS-001` - `module_id`:该 REQ 所属模块,如 `module_sys` - - `rationale`(一行**选中理由**):依赖驱动的简短描述,如 `所属模块无依赖,基础模块` / `依赖 REQ-SYS-001 已在前` / `所属模块依赖 module_sys 已在前` - - `note`(一行**备注**):默认 `—`;仅环依赖打破场景填原因 -7. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-02-template.md`,填充 `modules[]`(含 `id` / `name` / `deps` / `tables`)/ `req_order[]`(每项含 `index` / `req_id` / `module_id` / `rationale` / `note`)/ `notes`。 + - `rationale`(**选中理由**):依赖驱动的简短描述,如 `所属模块无依赖,基础模块` / `依赖 REQ-SYS-001 已在前` / `所属模块依赖 module_sys 已在前` + - `note`(**备注**):默认 `—`;仅环依赖打破场景填原因 +7. 读取并填充 `${CLAUDE_SKILL_DIR}/templates/docs-02-template.md`。 8. 写入 `docs/02-开发计划.md`。 +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] docs/02 开发计划已生成` + ### B. docs/05 — API 接口契约 -1. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-05-header-template.md`,写入 `docs/05-API接口契约.md` 头部。 -2. 对所有模块的每个 REQ:用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-05-endpoint-template.md`,推断 method / path / auth / permission / 请求和响应 schema(不明确的询问用户),追加到 docs/05。 +1. 读取 `${CLAUDE_SKILL_DIR}/templates/docs-05-header-template.md`,写入 `docs/05-API接口契约.md` 头部。 +2. 对所有模块的每个 REQ:读取并推断 `${CLAUDE_SKILL_DIR}/templates/docs-05-endpoint-template.md`,追加到 docs/05。 + +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] docs/05 API 契约已生成` ### B2. 回填模块头 + REQ 卡片的 TBD(A5) 字段 -1. 用 `Glob` 列出 `docs/01-需求清单/*/_module.md`(模块头)和 `docs/01-需求清单/*/REQ-*.md`(REQ 卡片)。 -2. 用 `Grep` 在这些文件中搜索 `TBD(A5 自动补)` 的行号(不读全文)。两种行命中: - - `依赖模块: TBD(A5 自动补)` → 模块级(仅在 `_module.md`,每文件一次) - - `依赖接口: TBD(A5 自动补)` → REQ 级(仅在 `REQ-*.md`,每文件一次) -3. 对每个命中行按类型回填: - - **模块级 `依赖模块`**(`_module.md`):用 `Read` 取该文件 `module_code` + `module_name`,从步骤 A 的 `module_topo_order[]` + DAG 查该模块的上游依赖(多模块用 `, ` 分隔);`Edit` 替换为 `依赖模块: , `(无依赖填 `—`) - - **REQ 级 `依赖接口`**(`REQ-*.md`):从文件名直接得 `req_id`(`REQ-USR-001.md` → `REQ-USR-001`);在步骤 B 刚生成的 `docs/05-API接口契约.md` 里 Grep 属于该 REQ 的 endpoint;`Edit` 替换为 `依赖接口: POST /api/xxx, GET /api/yyy`(多个用 `, ` 分隔) -4. 打印回填统计:`A5 回填 处模块依赖 + 处 REQ api_refs`。 +1. 在`docs/01-需求清单/*/_module.md`(模块头)和 `docs/01-需求清单/*/REQ-*.md`(REQ 卡片)中搜索并回填 `TBD(A5 自动补)` +2. 打印回填统计:`A5 回填 处模块"依赖模块" + 处 REQ"依赖接口"`。 + +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] REQ 卡片依赖接口已回填` ### C. docs/06 — 页面清单 -1. 对每个有前端页面的模块:用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-06-module-pagelist-template.md`,填充 `pages[]`(page_name、route、page_type、req_ids、menu_path、interactions)。 -2. 将每个渲染块追加到 `docs/06-UI交互规范.md` § 五。 - -### D. docs/08 § 二 — 追加模块清单(含 REQ 子项) - -docs/08 已由 A0 project-init 创建(含 Plan 进度骨架)。本步骤只往 § 二 追加模块行,**不重写整个文件**。 - -1. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-08-module-row-template.md`(单模块 bullet 行模板)。 -2. **按 `module_id` 字母序**对每个模块: - - 从步骤 A `req_order[]` 过滤出 `module_id` 匹配的 REQ 列表(保持步骤 A 算出的模块内 REQ 顺序)。 - - 对每个 REQ 从 `docs/01-需求清单//.md` 读出其一句话标题(REQ 卡片标题行或 `goal` 字段)。 - - 渲染 `{{req_checklist}}` 块:每行 ` - [ ] `(4 空格缩进对齐 `- 功能:`)。 - - 渲染整条 bullet,填充 `module_id` / `module_name` / `depends_on` / `path_scopes` / `req_checklist`。 - - > 注意一:docs/08 § 二 的**模块行序**不决定分发顺序——分发以 docs/02 § 二 REQ 清单为准,此处模块按字母序仅方便查找。 - > 注意二:REQ 子项的 `[ ]` 由 `feature-review` 在 `verdict=approve` 时自动勾选,作为功能级进度可视化;**模块完成判定仍由 `MR:` 字段 + GitLab API state 单独决定**,不依赖子项勾选状态。 -3. 所有模块行拼成一段文本,用 `Edit` 在 `docs/08-模块任务管理.md` 的 `## 二、Coding 阶段(按模块循环)` 标题后插入(定位用下一行注释"(A5 填入后..."作为锚,把模块清单插到该注释之前)。 - -### E. docs/10 — 验收清单 - -1. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-10-header-template.md`,写入 `docs/10-验收检查清单.md` 头部。 -2. 对每个模块:用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-10-module-template.md`,填充 `reqs[]`、`data_checks[]`、`ui_checks[]`,追加。 - -### F. 验证 + 勾选 docs/08 进度 + 终止 Plan 阶段 - -1. 一致性检查: - - docs/01 的每个 REQ 都出现在 docs/05(作为接口,如适用)、docs/10(作为验收项)。 - - `docs/02 § 二` 开发顺序清单的 `module_id` 集合 = `docs/08 § 二` 的 `module_id` 集合(数量、ID 全等);不相等 → 报错停下,列出差集。 - -2. **最终占位符扫描**(覆盖 Plan 阶段全部产出:`docs/01-需求清单/index.md` + `docs/01-需求清单/*/_module.md` + `docs/01-需求清单/*/REQ-*.md` + `docs/02` / `docs/03` / `docs/05` / `docs/06` / `docs/10`): - - a. **`TBD` → CC 自动补齐**:用 `Grep` 搜索 `TBD(A3 自动补)` 和 `TBD(A5 自动补)`。有命中则按 A3 / B2 同样逻辑就地补填(A3 查 docs/03 填 `依赖表:`;A5 查 docs/05 按 REQ-ID 填 `依赖接口:`),再 Grep 确认 0 命中;仍残留报错停下。 - - b. **`【人工填写:...】` → QA 循环等用户补**(与 A2 skeleton-gen 步骤 E 同风格): - - 循环执行直到两个条件**同时满足**:(1) 扫描 0 命中;(2) 用户选「继续」 - - - **b.1 扫描**:用 `Grep` 在上述所有文件中搜索 `【人工填写:`,得到命中数 N 和残留位置清单(`<文件:行号> — <行内容摘要>`) - - **b.2 打印汇总横幅**: - ``` - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - [downstream-gen] 最终占位符审阅 - - <N=0 时打印:✅ 全部填完> - <N>0 时打印:⚠️ 还有 N 处待填: - <文件:行号> — <行内容摘要> - ...> - - 需要调整 → 直接编辑对应文件 - 填完后 → 回来选「继续」放行完成 Plan - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ``` - - **b.3 弹出 QA**:用 `AskUserQuestion` 询问: - - **question**: `最终占位符补填完毕?` - - **options** (每项为对象): - - `{"label": "继续", "description": "放行完成 Plan 阶段"}` - - `{"label": "有疑问想先沟通", "description": "需要讨论或修改"}` - - **b.4 路由**: - - 选「有疑问想先沟通」→ 回答用户问题后**回 b.1 重新扫描** - - 选「继续」→ **回 b.1 重新扫描验证**: - - 新 N = 0 → 进入步骤 3 - - 新 N > 0 → 用户以为填完其实还有残留,回 b.2 打印新横幅并再次弹出 QA - - 关键:**每次弹出 QA 前都重新扫描一次**,用户看到的 N 始终是最新的;只有 N = 0 且选「继续」才放行。 - -3. 用 `Edit` 在 `docs/08-模块任务管理.md` 勾选 7 个 checkbox(A5 的 6 个子项 + A5 父项): - - ` - [ ] docs/02 开发计划已生成` → `[x]` - - ` - [ ] docs/05 API 契约已生成` → `[x]` - - ` - [ ] docs/06 § 五 页面清单已填入` → `[x]` - - ` - [ ] docs/10 验收清单已生成` → `[x]` - - ` - [ ] 下方模块列表已填入` → `[x]` - - ` - [ ] REQ 卡片依赖接口已回填` → `[x]` - - `- [ ] A5 下游文档生成 — downstream-gen` → `[x]` - -4. **从 `origin` 远程派生 GitLab 凭据并回填 `.env.local`** - - 仅当对应字段仍是 `TBD(A5 自动补)` 或空时回填;用户已手填为别的值一律不动。 - - a. 取远程 URL:`Bash`: `git remote get-url origin 2>/dev/null` → `REMOTE_URL`。为空 → 跳过本步骤,仅打印提示"未配置 origin 远程,`GITLAB_*` 留给用户手填"。 - b. 解析: - - `PROJECT_PATH` = 去掉 `<scheme>://<host>/` 或 `git@<host>:` 或 `ssh://<host>/` 前缀 + 去掉末尾 `.git`(如 `zhuzc/test`) - - `PROJECT_ID_ENC` = `PROJECT_PATH` 的 `/` 替换为 `%2F`(如 `zhuzc%2Ftest`) - - `HOST` = 远程主机名(如 `git.xlyprint.cn`) - - `SCHEME` 三分支(**绝不把 SSH remote 降级成 http**——Private Token 不能走明文): - - `REMOTE_URL` 以 `https://` 开头 → `https` - - `REMOTE_URL` 以 `http://` 开头 → `http`(明确声明,非推断) - - 其他(`git@` / `ssh://` / 异常)→ `https`(默认走安全侧) - - `API_URL_GUESS` = `<SCHEME>://<HOST>/api/v3` - c. 用 `Read` 读 `.env.local`: - - 若 `GITLAB_PROJECT_ID` 当前值为 `TBD(A5 自动补)` 或空 → 用 `Edit` 改为 `GITLAB_PROJECT_ID=<PROJECT_ID_ENC>` - - 若 `GITLAB_API_URL` 当前值为 `TBD(A5 自动补)` 或空 → `Edit` 改为 `GITLAB_API_URL=<API_URL_GUESS>` - - 上两字段若已被用户手改过(非 `TBD(A5 自动补)` 也非空)→ 不覆盖,仅核对打印 - - `GITLAB_TOKEN` 派生不了,占位保持 `【人工填写:...】`,用户去 GitLab Profile → Account → Private token 手填 - d. 打印回填摘要: - ``` - [downstream-gen] GitLab 凭据自动派生(从 origin 远程): - GITLAB_PROJECT_ID = <回填值 或「保留占位: 已手填 xxx」> - GITLAB_API_URL = <回填值 或「保留占位: 已手填 xxx」> - GITLAB_TOKEN = 保持占位 — 请去 GitLab Profile → Account → Private token 手填 - ``` - 若 `SCHEME` 是 `http`,额外提示:"⚠️ 本次派生用 `http://`——Private Token 将走明文;仅当你的 GitLab 部署真的没上 HTTPS 时可以保留,否则请手动改为 `https://`。" - 若 `SCHEME` 是推断来的 `https`(来自 ssh/git@ remote),额外提示:"API URL 的 scheme 是从 SSH remote 默认推成 `https`;如实际 API 走 http,请手动改 `GITLAB_API_URL`。" - -5. 输出:`downstream-gen: 写入 4 个文件 + <N> 个模块 + <M> 处 REQ 依赖接口回填。` - -6. 打印 Plan 阶段终止横幅并**停下**(不自动进入 B 阶段): +对每个有前端页面的模块:读取并填充 `${CLAUDE_SKILL_DIR}/templates/docs-06-module-pagelist-template.md`,追加到 `docs/06-UI交互规范.md` § 五。 + +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] docs/06 § 五 页面清单已填入` + +### D. docs/08 — 追加模块清单 + +读 `${CLAUDE_SKILL_DIR}/templates/docs-08-module-row-template.md`,按 `module_id` 字母序为每个模块渲染 bullet(模块元数据 + REQ 子项清单,REQ 序保持步骤 A 模块内顺序),追加到 docs/08。 + +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] 下方模块列表已填入` + +### E. docs/10 — 验收清单(项目级 SOP) + +```bash +cp "${CLAUDE_SKILL_DIR}/templates/docs-10-header-template.md" docs/10-验收检查清单.md +``` + +完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: +- ` - [ ] docs/10 验收清单已生成` + +### F. 验证 + 勾选 docs/08 进度 + 结束 Plan + +1. 一致性检查 + 自主修复循环(最多 3 轮,docs/01 是 REQ SSoT 不动): + + **检查项**: + - 每个 docs/01 REQ 都出现在 docs/05(作为接口,如适用) + - `docs/02 § 二` 的 `module_id` 集合 = `docs/08 § 二` 的 `module_id` 集合 + + **不一致 → 按差异类型自主修复**: + - **REQ 缺 docs/05 endpoint** → 按步骤 B 规则为该 REQ 推测,并追加到 docs/05 + - **module_id 缺 docs/08 § 二** → 按步骤 D 规则渲染该模块 bullet(含 REQ 子项),按 `module_id` 字母序插入正确位置 + - **module_id 缺 docs/02 § 二** → 重算该模块的 `req_order` 段(步骤 A 子流程),按拓扑序插入 docs/02 § 二 + + 修复后重跑检查;通过 → 进入 2;3 轮仍失败 → 停下,打印最终残留差异 + 已尝试的 3 轮修复摘要让用户介入。 + +2. **最终占位符扫描**(覆盖 Plan 阶段全部产出): + + a. **`TBD` → 自动补齐**:Grep 搜索 `TBD(A3 自动补)` 和 `TBD(A5 自动补)`。有命中则就地补填(A3 残留 → 查 docs/03 填 `依赖表`;A5 残留 → 查 docs/05 按 REQ-ID 填 `依赖接口`),再 Grep 确认 0 命中;仍残留报错停下。 + + b. **`【人工填写:...】` → QA 循环等用户补**: + + 循环执行直到搜索不到 `【人工填写:` 且用户选「继续」: + - 0 命中 → 直接放行进入步骤 3 + - 有命中 → 打印残留清单(`<文件:行号> — <内容摘要>`),用 `AskUserQuestion` 弹「继续」/「有疑问想先沟通」二选一;用户回答后重扫验证再决定放行 / 继续循环 + + **每次弹 QA 前都重扫一次**——保证用户看到的 N 是最新的,避免「用户以为填完但实际还有残留」直接放行。 + +3. 完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 勾选 A5 父项: + - `- [ ] A5 下游文档生成 — downstream-gen` + +4. 打印 Plan 阶段终止横幅并**停下**(不自动进入 B 阶段): ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -191,7 +120,7 @@ docs/08 已由 A0 project-init 创建(含 Plan 进度骨架)。本步骤只 所有规划文档已就绪,docs/08 § 一 全部勾选。 ⚠️ 进入 B 阶段前必须完成: - 1. 人工通读 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/* + 1. 审核 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/* 2. 把全部 Plan 产物 commit: git add -A && git commit -m "chore: plan phase A0~A5 done" @@ -201,26 +130,19 @@ docs/08 已由 A0 project-init 创建(含 Plan 进度骨架)。本步骤只 情况 A — 远程仓库是全新的(尚无 main / master): git remote add origin <gitlab-url> # 若尚未添加 git -c core.hooksPath=/dev/null push -u origin master - # 首次 push 本地 DB 尚未就位、scripts/test.sh 必然失败。 - # `-c core.hooksPath=/dev/null` 只对本次 push 临时指向空的 hooksPath, - # 跳过 .githooks/pre-push,不动 repo 的 core.hooksPath 配置, - # 也不用 --no-verify(后者被 CC 的 deny-no-verify hook 硬拦)。 - # push 完成后到 GitLab UI 把 master(或 main)设为 protected 情况 B — 远程已有 main 需要走 MR 审核: git checkout -b plan-init git push -u origin plan-init # 在 GitLab 打开 plan-init → main 的 MR,审核并合并 - 4. 补齐 `.env.local`: - - `GITLAB_TOKEN`(必填):去 GitLab Profile → Account → Private token 生成后粘贴 - - `GITLAB_API_URL` / `GITLAB_PROJECT_ID`:步骤 4 已从 origin 远程自动回填(若仍显示 - `TBD(A5 自动补)`,说明 origin 没配,手动填;若 scheme 错请改 http↔https) - B 阶段 mr-create 用 token 通过 curl 创建 MR。 + 4. 补齐 `.env.local` 三个 GITLAB_* 字段 + + - `GITLAB_TOKEN`:去 GitLab Profile → Account → Private token 生成后粘贴 + - `GITLAB_PROJECT_ID`,`GITLAB_API_URL`:运行 + bash <plugin-skill-dir>/scripts/derive-gitlab.sh - 5. main(或 master)就绪后,再运行 /erp-workflow:coding-start - 进入 B 阶段。届时 .env.local 应指向本地 MySQL(非共享远程 DB), - 否则 B 阶段每次测试闸门都会因 setup-test-db.sh 的防护失败。 + 5. remote git 就绪后,再运行 /erp-workflow:coding-start ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` @@ -232,4 +154,4 @@ docs/08 已由 A0 project-init 创建(含 Plan 进度骨架)。本步骤只 - `${CLAUDE_SKILL_DIR}/templates/docs-06-module-pagelist-template.md` - `${CLAUDE_SKILL_DIR}/templates/docs-08-module-row-template.md`(模块 bullet 行模板) - `${CLAUDE_SKILL_DIR}/templates/docs-10-header-template.md` -- `${CLAUDE_SKILL_DIR}/templates/docs-10-module-template.md` +- `${CLAUDE_SKILL_DIR}/scripts/derive-gitlab.sh`(**用户可选辅助**:在 add origin 之后手动跑一次,自动派生 GITLAB_PROJECT_ID / GITLAB_API_URL 写入 .env.local) diff --git a/skills/plan/downstream-gen/banners/flow.txt b/skills/plan/downstream-gen/banners/flow.txt new file mode 100644 index 0000000..f53fce1 --- /dev/null +++ b/skills/plan/downstream-gen/banners/flow.txt @@ -0,0 +1,17 @@ +┌────────────────────────────────────────────────────────┐ +│ 📋 阶段 A:规划(一次性) │ +│ │ +│ A0 初始化项目 │ +│ ↓ │ +│ A1 锁范围(生成 REQ 卡片) → 人工审核 │ +│ ↓ │ +│ A2 生成骨架 │ +│ ↓ │ +│ A3 生成 DB 设计 → 人工审核 │ +│ ↓ │ +│ A4 初始化 DB │ +│ ↓ │ +│ ▶ A5 生成下游文档 │ +│ │ +│ 规划阶段到此结束 │ +└────────────────────────────────────────────────────────┘ diff --git a/skills/plan/downstream-gen/scripts/derive-gitlab.sh b/skills/plan/downstream-gen/scripts/derive-gitlab.sh new file mode 100644 index 0000000..085ea14 --- /dev/null +++ b/skills/plan/downstream-gen/scripts/derive-gitlab.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# derive-gitlab.sh — 从 git origin 远程派生 GitLab 凭据并回填 .env.local +# +# 用法: bash derive-gitlab.sh [.env.local 路径,默认 .env.local] +# +# 派生字段: +# GITLAB_PROJECT_ID = origin 的 namespace/path(/ 编码成 %2F) +# GITLAB_API_URL = <scheme>://<host>/api/v3 +# +# 仅当字段值为 TBD(A5 自动补) 或空时回填;用户手填值不动。 +# 仅支持 http(s) origin URL;其他协议(ssh / git@)跳过。 +# GITLAB_TOKEN 不派生,留给用户手填。 +# +# 退出码: +# 0 = 派生完成或主动跳过(origin 不存在 / 协议不支持) +# 2 = .env.local 路径错 + +set -uo pipefail + +ENV_FILE=${1:-.env.local} +[ -f "$ENV_FILE" ] || { echo "derive-gitlab.sh: env file not found: $ENV_FILE" >&2; exit 2; } + +URL=$(git remote get-url origin 2>/dev/null || true) +if [ -z "$URL" ]; then + echo "derive-gitlab.sh: 未配置 origin 远程,GITLAB_* 留给用户手填" >&2 + exit 0 +fi + +case "$URL" in + https://*) SCHEME=https ;; + http://*) SCHEME=http ;; + *) + echo "derive-gitlab.sh: origin 不是 http(s) URL ($URL),跳过派生" >&2 + exit 0 + ;; +esac + +REST=${URL#${SCHEME}://} +HOST=${REST%%/*} +PATH_RAW=${REST#*/} +PATH_RAW=${PATH_RAW%.git} + +PROJECT_ID=${PATH_RAW//\//%2F} +API_URL="${SCHEME}://${HOST}/api/v3" + +update_field() { + local key=$1 newval=$2 + local line + line=$(grep -E "^${key}=" "$ENV_FILE" | head -1) + if [ -z "$line" ]; then + echo " ${key} = (.env.local 中无此行,跳过)" >&2 + return + fi + local current=${line#${key}=} + if [ -z "$current" ] || [ "$current" = "TBD(A5 自动补)" ]; then + sed -i.bak -E "s|^${key}=.*$|${key}=${newval}|" "$ENV_FILE" && rm -f "${ENV_FILE}.bak" + echo " ${key} = ${newval}" + else + echo " ${key} = (保留用户手填: ${current})" + fi +} + +echo "derive-gitlab.sh: 从 origin ($URL) 派生 GitLab 凭据:" +update_field GITLAB_PROJECT_ID "$PROJECT_ID" +update_field GITLAB_API_URL "$API_URL" +echo " GITLAB_TOKEN = (保持占位,请去 GitLab Profile → Account → Private token 手填)" diff --git a/skills/plan/downstream-gen/templates/docs-05-endpoint-template.md b/skills/plan/downstream-gen/templates/docs-05-endpoint-template.md index 1583d4e..ff10df8 100644 --- a/skills/plan/downstream-gen/templates/docs-05-endpoint-template.md +++ b/skills/plan/downstream-gen/templates/docs-05-endpoint-template.md @@ -4,19 +4,8 @@ - **Path**: `{{path}}` - **Auth**: {{auth}} - **Permission**: {{permission}} - -#### 请求参数 -{{request_params}} - -#### 请求体 -```json -{{request_body_schema}} -``` - -#### 响应体 -```json -{{response_body_schema}} -``` +- **请求**: {{request_summary}} +- **响应**: {{response_summary}} #### 错误码 {{#each errors}} diff --git a/skills/plan/downstream-gen/templates/docs-10-header-template.md b/skills/plan/downstream-gen/templates/docs-10-header-template.md index a4132b3..3a9c860 100644 --- a/skills/plan/downstream-gen/templates/docs-10-header-template.md +++ b/skills/plan/downstream-gen/templates/docs-10-header-template.md @@ -1,6 +1,6 @@ # 10-验收检查清单 -通用验收项(全项目适用): +通用验收项(全项目适用): - [ ] `scripts/test.sh` 本地全绿 - [ ] 所有 schema 改动都有对应 `sql/migrations/V_n__<desc>.sql` @@ -10,5 +10,7 @@ - [ ] 异常走全局处理器,不暴露堆栈到前端 - [ ] 前端不存敏感信息到 localStorage -## 模块专项 -(各模块验收段落见下方,由 `downstream-gen` 按模块填入) +> 本文档仅维护项目级验收 SOP。粒度更细的验收信息分散在: +> - **每 REQ 的业务验收点**:`docs/01-需求清单/<module>/<req_id>.md § 验收` +> - **每模块的实测验收(数据 / UI / 自动化用例位置)**:由 B 阶段 `module-report` 在该模块完成时填入模块完成报告 +> - **REQ 开发进度(feature-review approve 状态)**:`docs/08 § 二` diff --git a/skills/plan/downstream-gen/templates/docs-10-module-template.md b/skills/plan/downstream-gen/templates/docs-10-module-template.md deleted file mode 100644 index 9342e2e..0000000 --- a/skills/plan/downstream-gen/templates/docs-10-module-template.md +++ /dev/null @@ -1,18 +0,0 @@ -### {{module_id}} {{module_name}} - -#### 功能级验收(每个 REQ 对应可执行用例) -{{#each reqs}} -- [ ] {{req_id}} {{title}} - - 自动化用例: `{{test_file}}::{{test_name}}` - - 手动验收: {{manual_check}} -{{/each}} - -#### 数据级验收 -{{#each data_checks}} -- [ ] {{check}} -{{/each}} - -#### UI 级验收(若含前端) -{{#each ui_checks}} -- [ ] {{check}} -{{/each}} diff --git a/skills/plan/project-init/SKILL.md b/skills/plan/project-init/SKILL.md index 5c4f3bd..431dd21 100644 --- a/skills/plan/project-init/SKILL.md +++ b/skills/plan/project-init/SKILL.md @@ -2,7 +2,7 @@ name: project-init description: A0 项目初始化——从插件模板幂等地复制 CLAUDE.md / docs/01-需求清单/index.md / docs/04-技术规范.md / docs/08-模块任务管理.md(已存在则跳过),并初始化 Git(如未初始化)。session-start 在 docs/08 缺失时派发本 skill。 user-invocable: false -allowed-tools: Glob Edit Skill Bash(mkdir *) Bash(cp -n *) Bash(git init) Bash(command -v *) Bash(uname *) Bash(brew *) Bash(apt *) Bash(apt-get *) Bash(yum *) Bash(apk *) Bash(export PATH=*) Bash(echo *) +allowed-tools: Glob Edit Skill Bash(mkdir *) Bash(cp -n *) Bash(git init) Bash(command -v *) Bash(uname *) Bash(brew *) Bash(apt *) Bash(apt-get *) Bash(yum *) Bash(apk *) Bash(export PATH=*) Bash(echo *) Bash(cat *) --- **所有输出必须使用中文。** @@ -13,20 +13,10 @@ allowed-tools: Glob Edit Skill Bash(mkdir *) Bash(cp -n *) Bash(git init) Bash(c ### 步骤 0:打印当前位置流程图 -向用户展示当前在 A 阶段流程中的位置: +用 `Bash` 执行 `cat` 命令向用户展示当前位置流程图(stdout 即 ASCII 框图): -``` -┌──────────────────────────────────────────────────────┐ -│ 📋 阶段 A:规划(一次性) │ -│ │ -│ ▶ A0 初始化项目 → A1 锁范围(REQ 卡片) │ -│ ↓ │ -│ 等你审阅 REQ,重新运行 /plan-start 继续 │ -│ ↓ │ -│ A2 生成骨架 → A3 生成 DB 设计 → A4 初始化 DB → A5 生成下游文档│ -│ ↓ │ -│ 规划阶段到此结束 │ -└──────────────────────────────────────────────────────┘ +```bash +cat "${CLAUDE_PLUGIN_ROOT}/skills/plan/project-init/banners/flow.txt" ``` ### A. 幂等复制模板文件 @@ -89,3 +79,11 @@ cp -n "${CLAUDE_SKILL_DIR}/templates/docs-08-initial-template.md" docs/08-模块 ``` 立即调用 `Skill(scope-lock)` 进入 A1,不等用户手动输入。 + +## 参考 + +- `${CLAUDE_SKILL_DIR}/templates/CLAUDE-template.md`(项目级 CLAUDE.md 模板) +- `${CLAUDE_SKILL_DIR}/templates/docs-01-index-template.md`(需求索引初稿) +- `${CLAUDE_SKILL_DIR}/templates/docs-04-stack-template.md`(默认技术栈) +- `${CLAUDE_SKILL_DIR}/templates/docs-08-initial-template.md`(A 阶段进度初始化) +- 下游:`scope-lock`(A1,自动派发) diff --git a/skills/plan/project-init/banners/flow.txt b/skills/plan/project-init/banners/flow.txt new file mode 100644 index 0000000..fd2e547 --- /dev/null +++ b/skills/plan/project-init/banners/flow.txt @@ -0,0 +1,17 @@ +┌────────────────────────────────────────────────────────┐ +│ 📋 阶段 A:规划(一次性) │ +│ │ +│ ▶ A0 初始化项目 │ +│ ↓ │ +│ A1 锁范围(生成 REQ 卡片) → 人工审核 │ +│ ↓ │ +│ A2 生成骨架 │ +│ ↓ │ +│ A3 生成 DB 设计 → 人工审核 │ +│ ↓ │ +│ A4 初始化 DB │ +│ ↓ │ +│ A5 生成下游文档 │ +│ │ +│ 规划阶段到此结束 │ +└────────────────────────────────────────────────────────┘ diff --git a/skills/plan/project-init/templates/docs-01-index-template.md b/skills/plan/project-init/templates/docs-01-index-template.md index bbfb33c..08e326a 100644 --- a/skills/plan/project-init/templates/docs-01-index-template.md +++ b/skills/plan/project-init/templates/docs-01-index-template.md @@ -1,6 +1,6 @@ # 需求清单 -> 本目录按模块组织所有功能需求。每个模块一个子目录,含 `_module.md`(模块头)和 `REQ-XXX-NNN.md`(每张 REQ 卡片一个文件)。下方核心功能点供 CC 拆分出 REQ 编号 + 标题 + 草拟规则;卡片内输入 / 输出的主从表字段由人工填写。 +> 本目录按模块组织所有功能需求。每个模块一个子目录,含 `_module.md`(模块头)和 `REQ-XXX-NNN.md`(每张 REQ 卡片一个文件)。下方核心功能点供 CC 拆分出 REQ 编号 + 标题 + 草拟规则;卡片内输入 / 输出的简述句和 N 张字段表由人工编辑。 ## 模块索引 @@ -12,5 +12,5 @@ ## 填写说明 1. 每个模块占一行,`模块代码` 用大写英文缩写(如 SYS / PUR / INV / SAL / FIN / HR) -2. `核心功能点` 只需列关键词,CC 会基于此拆分出 N 张 REQ 卡片骨架(卡片内输入 / 输出的主从表字段仍由人工填) +2. `核心功能点` 只需列关键词,CC 会基于此拆分出 N 张 REQ 卡片骨架(卡片内输入 / 输出的简述句和字段表仍由人工编辑) 3. 填完后运行 `/erp-workflow:plan-start`,CC 会自动检测并进入需求生成阶段 diff --git a/skills/plan/project-init/templates/docs-08-initial-template.md b/skills/plan/project-init/templates/docs-08-initial-template.md index bf52487..c36c112 100644 --- a/skills/plan/project-init/templates/docs-08-initial-template.md +++ b/skills/plan/project-init/templates/docs-08-initial-template.md @@ -24,12 +24,11 @@ - [ ] A3 DB 设计 + REQ 回填 — db-design-gen - [ ] docs/03-数据库设计文档.md 已生成 - - [ ] docs/01 各 REQ 卡片"涉及数据表"已回填 - - [ ] 用户已审阅 docs/03 表结构 + - [ ] docs/01 各 REQ 卡片"依赖表" + 模块头"涉及表" 已回填 - [ ] A4 DB 初始化 — db-init - [ ] sql/migrations/V1__initial_schema.sql 已生成 - - [ ] DDL 与 docs/03 全量一致(5 维度校验通过) + - [ ] DDL 与 docs/03 全量一致 - [ ] .env.local 凭据已验证(mysql -e "SELECT 1" OK) - [ ] setup-test-db.sh 防护通过 + DROP+CREATE + apply V1 已执行 - [ ] SHOW TABLES 行数 == docs/03 表数量 diff --git a/skills/plan/scope-lock/SKILL.md b/skills/plan/scope-lock/SKILL.md index 5cda61f..2e39871 100644 --- a/skills/plan/scope-lock/SKILL.md +++ b/skills/plan/scope-lock/SKILL.md @@ -1,8 +1,8 @@ --- name: scope-lock -description: A1 计划范围锁定——引导用户填写项目概述 + 技术栈 + 需求索引,并按模块子目录生成 REQ 卡片骨架(CC 推断 req_id/title/goal/rules/constraints/acceptance;输入/输出 主从表骨架留 `-` 等人工填;依赖表/依赖接口模板写死 `TBD(A3/A5 自动补)` 由后续 skill 回填)。 +description: A1 计划范围锁定——引导用户填写项目概述 + 技术栈 + 需求索引,并按模块子目录生成 REQ 卡片骨架(CC 推断 req_id/title/goal/rules/constraints/acceptance;输入/输出 各含一句简述 + N 张示例字段表全部原样复制由人工编辑;依赖表/依赖接口模板写死 `TBD(A3/A5 自动补)` 由后续 skill 回填)。 user-invocable: false -allowed-tools: Read Write Edit Grep Skill AskUserQuestion Bash(mkdir *) +allowed-tools: Read Edit Grep Skill AskUserQuestion Bash(mkdir *) Bash(cp *) Bash(sed *) Bash(bash *) Bash(cat *) --- **所有输出必须使用中文。** @@ -13,20 +13,10 @@ allowed-tools: Read Write Edit Grep Skill AskUserQuestion Bash(mkdir *) ### 步骤 0:打印当前位置流程图 -向用户展示当前在 A 阶段流程中的位置: +用 `Bash` 执行 `cat` 命令向用户展示当前位置流程图(stdout 即 ASCII 框图): -``` -┌──────────────────────────────────────────────────────┐ -│ 📋 阶段 A:规划(一次性) │ -│ │ -│ A0 初始化项目 → ▶ A1 锁范围(REQ 卡片) │ -│ ↓ │ -│ ⏸ 等你审阅 REQ,重新运行 /plan-start 继续 │ -│ ↓ │ -│ A2 生成骨架 → A3 生成 DB 设计 → A4 初始化 DB → A5 生成下游文档│ -│ ↓ │ -│ 规划阶段到此结束 │ -└──────────────────────────────────────────────────────┘ +```bash +cat "${CLAUDE_PLUGIN_ROOT}/skills/plan/scope-lock/banners/flow.txt" ``` ### A. 提示用户填写项目概述并等待 @@ -101,7 +91,7 @@ allowed-tools: Read Write Edit Grep Skill AskUserQuestion Bash(mkdir *) 请按业务列出所有模块: - 每行一个模块(如 SYS 系统管理 / PUR 采购 / SAL 销售) - - 「核心功能点」只需关键词,CC 会拆分为 REQ 卡片骨架(卡片内输入 / 输出的主从表字段由你后续人工填) + - 「核心功能点」只需关键词,CC 会拆分为 REQ 卡片 改完后回来选择「继续」。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` @@ -118,16 +108,20 @@ allowed-tools: Read Write Edit Grep Skill AskUserQuestion Bash(mkdir *) 1. 用 `Grep` 校验 `docs/01-需求清单/index.md` 无 `【人工填写:` 残留;有则回步骤 C。 2. 用 `Read` 读 `index.md` 解析模块索引。 -3. 对每个模块: - - 基于「核心功能点」推断出 N 个 REQ,每个 REQ 产出六元组 - `{req_id, title, goal, rules, constraints, acceptance}`: - - `req_id` / `title`:从核心功能点拆分命名(如「用户/角色/权限」→ `REQ-SYS-001 用户管理`、`REQ-SYS-002 角色管理`、`REQ-SYS-003 权限管理`) - - `goal`:用一句话展开 `title` - - `rules` / `constraints` / `acceptance`:业务语义层面的合理起草,待人工审阅修订 - - **不推断输入 / 输出的主从表字段**——模板内保留 `主表 + 从表1` 骨架(每张表表头 + 1 行 `-`),等人工按业务自行复制行 / 增删从表 - - 用 `Bash`: `mkdir -p docs/01-需求清单/<module_code>-<module_name>` 创建模块目录 - - 用 `${CLAUDE_SKILL_DIR}/templates/_module-template.md` 渲染 `_module.md`,填充 `{module_code, module_name, module_brief}`,`Write` 到 `docs/01-需求清单/<module_code>-<module_name>/_module.md` - - 对每个 REQ:用 `${CLAUDE_SKILL_DIR}/templates/req-card-template.md` 渲染(渲染约定见模板顶部 HTML 注释),替换 6 个占位符,`Write` 到 `docs/01-需求清单/<module_code>-<module_name>/<req_id>.md` +3. **单次 Bash 写入所有文件**(替代 N×9 次 Edit): + + - **每模块推断**:三元组 `{module_code, module_name, module_brief}` + N 个 REQ 的六元组 `{req_id, title, goal, rules, constraints, acceptance}`。`req_id`/`title` 从核心功能点拆分;`goal` 展开 `title`;`rules`/`constraints`/`acceptance` 起草业务语义。**不推断**输入 / 输出(模板原样保留示例)。 + - **单次 Bash**:调 `${CLAUDE_SKILL_DIR}/scripts/render.sh` 落盘所有文件(脚本内部完成模板读取 + 占位符替换 + HTML 注释剥离)。下面是**调用形态**示例,`<MOD>` / `<模块名>` / `<REQ-MOD-NNN>` 等尖括号位置 CC 按 `index.md` 实际值替换: + + ```bash + R="${CLAUDE_SKILL_DIR}/scripts/render.sh" + # 每个模块:mkdir + 1 个 render module + N 个 render req(每 REQ 一行) + mkdir -p "docs/01-需求清单/<MOD>-<模块名>" + bash "$R" module "docs/01-需求清单/<MOD>-<模块名>/_module.md" "<MOD>" "<模块名>" "<module_brief>" + bash "$R" req "docs/01-需求清单/<MOD>-<模块名>/<REQ-MOD-NNN>.md" "<REQ-MOD-NNN>" "<title>" "<goal>" "<rules>" "<constraints>" "<acceptance>" + ``` + + - **兜底**:值含字面 `$xxx` 或 `}}` 的 REQ 单独走 cp + Edit。 4. 用 `Edit` 在 `docs/08-模块任务管理.md` 勾选(A1 子项 + A1 顶层): - ` - [ ] REQ 卡片骨架已生成(docs/01-需求清单/<module>/REQ-*.md,业务内容留待人工填写)` - `- [ ] A1 范围锁定 — scope-lock` @@ -145,16 +139,14 @@ allowed-tools: Read Write Edit Grep Skill AskUserQuestion Bash(mkdir *) ✓ docs/01-需求清单/<module>/REQ-*.md REQ 卡片骨架 ⏸ 现在请你逐张打开 REQ 卡片: - - **必填**:输入(主表 + N 从表)、输出(主表 + N 从表) - · 每张表先把 `-` 行按实际字段数复制;没有从表就删掉 `从表1` 整段,多于一个就复制为 `从表2 / 从表3 / ...` - - **审阅**:目标 / 跨字段规则 / 边界 / 验收(CC 已起草,对照业务校正) - - **保留**:依赖表 / 依赖接口 行的 `TBD(A3/A5 自动补)` 不要改,由 A3/A5 自动回填 + - **必改**:输入 / 输出 两段 + · `表1` / `表2` 是模板示例,按本 REQ 业务**改字段 / 增删行 / 增删整张表** + - **审阅**:目标 / 跨字段规则 / 边界 / 验收(已起草,对照业务校正) + - **保留**:`TBD` 不要改,由之后流程自动回填 - 审阅是 Plan 阶段的关键人工关口,请认真逐张过一遍。 审阅完成后,运行以下命令继续进入 A2: /erp-workflow:plan-start - (入口会读取 docs/08 进度,自动派发到 A2 skeleton-gen) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` @@ -167,3 +159,4 @@ allowed-tools: Read Write Edit Grep Skill AskUserQuestion Bash(mkdir *) - `docs/01-需求清单/<module>/REQ-*.md`(REQ 卡片骨架输出,A3 db-design-gen / A5 downstream-gen 会回填 TBD 字段) - `${CLAUDE_SKILL_DIR}/templates/req-card-template.md` - `${CLAUDE_SKILL_DIR}/templates/_module-template.md` +- `${CLAUDE_SKILL_DIR}/scripts/render.sh`(步骤 D 渲染助手,dispatch `module` / `req` 两种模式) diff --git a/skills/plan/scope-lock/banners/flow.txt b/skills/plan/scope-lock/banners/flow.txt new file mode 100644 index 0000000..e012667 --- /dev/null +++ b/skills/plan/scope-lock/banners/flow.txt @@ -0,0 +1,17 @@ +┌────────────────────────────────────────────────────────┐ +│ 📋 阶段 A:规划(一次性) │ +│ │ +│ A0 初始化项目 │ +│ ↓ │ +│ ▶ A1 锁范围(生成 REQ 卡片) → 人工审核 │ +│ ↓ │ +│ A2 生成骨架 │ +│ ↓ │ +│ A3 生成 DB 设计 → 人工审核 │ +│ ↓ │ +│ A4 初始化 DB │ +│ ↓ │ +│ A5 生成下游文档 │ +│ │ +│ 规划阶段到此结束 │ +└────────────────────────────────────────────────────────┘ diff --git a/skills/plan/scope-lock/examples/example.md b/skills/plan/scope-lock/examples/example.md deleted file mode 100644 index 30f7b9f..0000000 --- a/skills/plan/scope-lock/examples/example.md +++ /dev/null @@ -1,43 +0,0 @@ -<!-- -本文件示例 scope-lock 步骤 D 渲染后的单张 REQ 卡片产物。 -路径:docs/01-需求清单/USR-用户管理/REQ-USR-001.md - -CC 已替换 6 个占位符(req_id / title / goal / rules / constraints / acceptance); -输入 / 输出 各保留「主表 + 从表1」骨架,每张表只有表头 + 1 行 `-`,等人工按业务补; -依赖表 / 依赖接口 保留 `TBD(...)`,等 A3/A5 自动回填。 ---> -### REQ-USR-001 增加用户 - -**目标**: 在系统中创建新用户账号,绑定员工并配置登录凭据、用户类型与权限组 - -- **输入**: - - **主表**: - - | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | - | --- | --- | --- | --- | --- | --- | --- | - | - | - | - | - | - | - | - | - - - **从表1**: - - | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | - | --- | --- | --- | --- | --- | --- | --- | - | - | - | - | - | - | - | - | - -- **输出**: - - **主表**: - - | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | - | --- | --- | --- | --- | --- | --- | --- | - | - | - | - | - | - | - | - | - - - **从表1**: - - | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | - | --- | --- | --- | --- | --- | --- | --- | - | - | - | - | - | - | - | - | - -- **跨字段规则**: 员工名选定后用户号 / 用户名 / 手机号 / 邮箱自动带出;权限组至少 1 个 -- **边界**: 同一员工名不能重复创建账号;超级管理员仅允许系统内置创建 -- **验收**: 新增成功后可立即登录;字段校验失败时给出明确提示 -- **依赖表**: TBD(A3 自动补) -- **依赖接口**: TBD(A5 自动补) diff --git a/skills/plan/scope-lock/scripts/render.sh b/skills/plan/scope-lock/scripts/render.sh new file mode 100644 index 0000000..8a83488 --- /dev/null +++ b/skills/plan/scope-lock/scripts/render.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# render.sh — scope-lock 步骤 D 渲染单个 _module.md 或 REQ-*.md +# +# 用法: +# bash render.sh module <out_path> <module_code> <module_name> <module_brief> +# bash render.sh req <out_path> <req_id> <title> <goal> <rules> <constraints> <acceptance> +# +# 模板路径自定位:脚本同级父目录下 templates/{_module-template.md, req-card-template.md} + +set -euo pipefail + +TYPE=${1:?missing type (module|req)} +shift + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +TPL_DIR="$SCRIPT_DIR/../templates" + +case "$TYPE" in + module) + out=${1:?missing out_path}; code=${2:?}; name=${3:?}; brief=${4:?} + c=$(cat "$TPL_DIR/_module-template.md") + c="${c//\{\{module_code\}\}/$code}" + c="${c//\{\{module_name\}\}/$name}" + c="${c//\{\{module_brief\}\}/$brief}" + ;; + req) + out=${1:?missing out_path}; req_id=${2:?}; title=${3:?}; goal=${4:?}; rules=${5:?}; constraints=${6:?}; acceptance=${7:?} + c=$(sed '/^<!--$/,/^-->$/d' "$TPL_DIR/req-card-template.md") + c="${c//\{\{req_id\}\}/$req_id}" + c="${c//\{\{title\}\}/$title}" + c="${c//\{\{goal\}\}/$goal}" + c="${c//\{\{rules\}\}/$rules}" + c="${c//\{\{constraints\}\}/$constraints}" + c="${c//\{\{acceptance\}\}/$acceptance}" + ;; + *) + echo "render.sh: unknown type '$TYPE' (expect module|req)" >&2 + exit 1 + ;; +esac + +printf '%s\n' "$c" > "$out" diff --git a/skills/plan/scope-lock/templates/_module-template.md b/skills/plan/scope-lock/templates/_module-template.md index 518776d..5425b83 100644 --- a/skills/plan/scope-lock/templates/_module-template.md +++ b/skills/plan/scope-lock/templates/_module-template.md @@ -1,5 +1,5 @@ # {{module_code}}-{{module_name}} -模块简述: {{module_brief}} -依赖模块: TBD(A5 自动补) -涉及表: TBD(A3 自动补) +- **模块简述**: {{module_brief}} +- **依赖模块**: TBD(A5 自动补) +- **涉及表**: TBD(A3 自动补) diff --git a/skills/plan/scope-lock/templates/req-card-template.md b/skills/plan/scope-lock/templates/req-card-template.md index 955528d..8aba89a 100644 --- a/skills/plan/scope-lock/templates/req-card-template.md +++ b/skills/plan/scope-lock/templates/req-card-template.md @@ -6,9 +6,10 @@ req-card-template:单张 REQ 卡片骨架。每张卡片是 docs/01-需求清 - req_id / title:从核心功能点拆分命名得来 - goal:用一句话展开 title - rules / constraints / acceptance:业务语义层面的合理起草,待人工审阅修订 -2) `**输入**` / `**输出**` 各含「主表 + N 个从表」二级结构,每张表内字面 `-` 是**字面占位**,模板原样保留。等人工拿到生成的卡片后按实际业务自己改: - - 每张表:仅保留表头 + 1 行 `-` 示意;人工按字段数复制行 - - 没有从表的 REQ:删掉 `从表1` 整段;多于一个从表:复制 `从表1` 段并改成 `从表2 / 从表3 / ...` +2) `**输入**` / `**输出**` 二级结构:每段先一句话总结(如「用户提交...」/「返回用户 id」),再跟「表1 / 表2 / ...」N 张平铺的字段表。CC 渲染时对**两段简述 + 所有表(含示例数据)全部原样复制**,不要根据 REQ 业务篡改示例字段、也不要换列结构: + - `输入` 的表:7 列(字段 / 类型 / 必填 / 输入方式 / 显示来源 / 默认值 / 业务规则),模板内含示例数据(表1 6 行、表2 3 行) + - `输出` 的表:3 列(字段 / 类型 / 显示来源),模板内含示例数据(表1 / 表2 各 3 行) + - 表的数量是可变的:人工拿到卡片后按 REQ 实际情况增删表(删多余的 `表N` 整段 / 复制为 `表3 / 表4 / ...`)和编辑字段 3) `**依赖表**: TBD(A3 自动补)` 和 `**依赖接口**: TBD(A5 自动补)` 是后续 skill 自动回填的占位,渲染时**保持原样**,不要替换为 `-` 也不要替换为 `{{...}}` 4) 渲染后这段 HTML 注释要**剥掉**,不进入最终卡片 --> @@ -16,31 +17,44 @@ req-card-template:单张 REQ 卡片骨架。每张卡片是 docs/01-需求清 **目标**: {{goal}} -- **输入**: - - **主表**: +- **输入**: 用户提交用户名(3-20 位字母数字下划线,系统内唯一)、姓名(2-50字符)、手机号(11 位数字,系统内唯一)、邮箱(标准邮箱格式,可选)、初始密码(8-20位含大小写字母和数字) + + - **表1**: - | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | - | --- | --- | --- | --- | --- | --- | --- | - | - | - | - | - | - | - | - | + | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | + | ---- | ---- | --- | ---- | ----- | --- | ------------------- | + | 用户名 | 文本 | 是 | 手工输入 | `职员表` | — | 3-20 位字母数字下划线;系统内唯一 | + | 姓名 | 文本 | 是 | 下拉单选 | `职员表` | — | 2-50 个字符 | + | 手机号 | 文本 | 是 | 手工输入 | `职员表` | — | 11 位数字;系统内唯一 | + | 邮箱 | 文本 | 否 | 手工输入 | `职员表` | — | 标准邮箱格式 | + | 角色 | 文本 | 是 | 下拉单选 | 普通用户/超级管理员 | 普通用户 | 至少选择 1 个 | + | 初始密码 | 文本 | 是 | 手工输入 | — | — | 8-20 位;含大小写字母和数字;显示星号 | - - **从表1**: + - **表2**: | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | | --- | --- | --- | --- | --- | --- | --- | - | - | - | - | - | - | - | - | + | 用户名 | 文本 | 是 | 手工输入 | `职员表` | — | 3-20 位字母数字下划线;系统内唯一 | + | 姓名 | 文本 | 是 | 下拉单选 | `职员表` | — | 2-50 个字符 | + | 手机号 | 文本 | 是 | 手工输入 | `职员表` | — | 11 位数字;系统内唯一 | -- **输出**: - - **主表**: +- **输出**: 返回用户 id + + - **表1**: - | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | - | --- | --- | --- | --- | --- | --- | --- | - | - | - | - | - | - | - | - | + | 字段 | 类型 | 显示来源 | + | --- | --- | --- | + | 用户名 | 文本 | `职员表` | + | 姓名 | 文本 | `职员表` | + | 角色 | 文本 | `职员表` | - - **从表1**: + - **表2**: - | 字段 | 类型 | 必填 | 输入方式 | 显示来源 | 默认值 | 业务规则 | - | --- | --- | --- | --- | --- | --- | --- | - | - | - | - | - | - | - | - | + | 字段 | 类型 | 显示来源 | + | --- | --- | --- | + | 用户名 | 文本 | `职员表` | + | 姓名 | 文本 | `职员表` | + | 角色 | 文本 | `职员表` | - **跨字段规则**: {{rules}} - **边界**: {{constraints}} diff --git a/skills/plan/skeleton-gen/SKILL.md b/skills/plan/skeleton-gen/SKILL.md index a11eded..401bec7 100644 --- a/skills/plan/skeleton-gen/SKILL.md +++ b/skills/plan/skeleton-gen/SKILL.md @@ -13,20 +13,10 @@ allowed-tools: Read Write Edit Skill Grep Glob AskUserQuestion Bash(mkdir *) Bas ### 步骤 0:打印当前位置流程图 -向用户展示当前在 A 阶段流程中的位置: +用 `Bash` 执行 `cat` 命令向用户展示当前位置流程图(stdout 即 ASCII 框图): -``` -┌──────────────────────────────────────────────────────┐ -│ 📋 阶段 A:规划(一次性) │ -│ │ -│ A0 初始化项目 → A1 锁范围(REQ 卡片) │ -│ ↓ │ -│ ⏸ 等你审阅 REQ,重新运行 /plan-start 继续 │ -│ ↓ │ -│ ▶ A2 生成骨架 → A3 生成 DB 设计 → A4 初始化 DB → A5 生成下游文档│ -│ ↓ │ -│ 规划阶段到此结束 │ -└──────────────────────────────────────────────────────┘ +```bash +cat "${CLAUDE_PLUGIN_ROOT}/skills/plan/skeleton-gen/banners/flow.txt" ``` ### A. 读取锁定的输入 @@ -159,6 +149,7 @@ QA 横幅涵盖:产出文件清单(docs/04 / 06 / 07 / 09 + scripts/*.sh + . - `${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-test-template.sh`(推断命令填充:build/lint/test/e2e) - `${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 槽位) diff --git a/skills/plan/skeleton-gen/banners/flow.txt b/skills/plan/skeleton-gen/banners/flow.txt new file mode 100644 index 0000000..798f4a9 --- /dev/null +++ b/skills/plan/skeleton-gen/banners/flow.txt @@ -0,0 +1,17 @@ +┌────────────────────────────────────────────────────────┐ +│ 📋 阶段 A:规划(一次性) │ +│ │ +│ A0 初始化项目 │ +│ ↓ │ +│ A1 锁范围(生成 REQ 卡片) → 人工审核 │ +│ ↓ │ +│ ▶ A2 生成骨架 │ +│ ↓ │ +│ A3 生成 DB 设计 → 人工审核 │ +│ ↓ │ +│ A4 初始化 DB │ +│ ↓ │ +│ A5 生成下游文档 │ +│ │ +│ 规划阶段到此结束 │ +└────────────────────────────────────────────────────────┘ diff --git a/skills/plan/skeleton-gen/templates/docs-06-static-template.md b/skills/plan/skeleton-gen/templates/docs-06-static-template.md index ed1baf3..107391e 100644 --- a/skills/plan/skeleton-gen/templates/docs-06-static-template.md +++ b/skills/plan/skeleton-gen/templates/docs-06-static-template.md @@ -43,5 +43,5 @@ UI 细节(布局参数、颜色、组件选型)来自 § 零 前端 UI 组 ## 四、主题与颜色 <!-- 基于 § 零 UI 库的主题 token;主色 / 成功 / 警告 / 错误 色值。 --> -## 五、页面清单(CC 生成) +## 五、页面清单 (由 `downstream-gen` 按模块追加段落)