feat: 融合 devflow-claude P0 批机制 (REQ-20260416-0017)
P0-1: SessionStart Hook — hooks/session-context.sh 从分支名解析 REQ-ID,调 MCP API 查询需求详情注入 system-reminder P0-2: PreToolUse Hook — hooks/pre-tool-confirm.sh 拦截生产推送、force push、docker prod 容器操作、git reset --hard 等 P0-3: Release Draft 闸门设计文档 — docs/design/release-draft-gate.md 完整架构 + 渐进式落地路径(拆 7 个子任务延后) 附最小可用脚本 hooks/release-draft.sh 创建 Gitea draft release P0-4: Memory 隔离规则 — 写入 req-prd / req-design / req-workflow 禁止 auto-memory 污染模板产出物(章节结构、字段定义、文档结构) P0-5: CLAUDE.md 架构检查 + 架构片段库 dev-coding skill 执行前检查架构关键词 新增 templates/claude-md-snippets/ 含 Go+Gin / React+AntD / Vue+Element / MCP+TS / generic 五套骨架 P0-6: /commit 分支保护自动化 — 新 skill dev-commit-plugin 保护分支自动建功能分支 + Conventional Commits + REQ-XXX 自动关联 安装: bash hooks/install.sh 后续: P0-3 完整实现拆 7 个子任务(P0-3.1 ~ P0-3.7) 建议先部署 hooks 跑 1-2 周观察,再推进 Release 机制落地
This commit is contained in:
@@ -124,6 +124,19 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-commit-plugin",
|
||||
"source": "./skills-dev/dev-commit-plugin",
|
||||
"description": "智能 /commit 命令:分支保护 + 自动建功能分支 + Conventional Commits 生成",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-deploy-plugin",
|
||||
"source": "./skills-dev/dev-deploy-plugin",
|
||||
|
||||
253
docs/design/release-draft-gate.md
Normal file
253
docs/design/release-draft-gate.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# Release Draft 闸门机制 — 设计文档
|
||||
|
||||
**REQ**: REQ-20260416-0017 P0-3
|
||||
**状态**: 设计阶段
|
||||
**创建时间**: 2026-04-16
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景
|
||||
|
||||
### 当前问题
|
||||
|
||||
ai-proj 当前 CI/CD 流程(`.gitea/workflows/build.yaml`):
|
||||
|
||||
```
|
||||
push 到 main → 自动 build 镜像 → 自动部署生产
|
||||
```
|
||||
|
||||
**这个链路没有"最后一道人工闸门"**。merge develop→main 的 PR 一合并,生产就开始部署。一旦合并,只能事后回滚。
|
||||
|
||||
**典型事故场景**:PR 评审时漏了某个改动,merge 后立即推到线上,5 分钟后发现问题,但已经影响用户 5 分钟。
|
||||
|
||||
### 借鉴方案
|
||||
|
||||
**devflow-claude `/req:release` 命令的核心做法:**
|
||||
|
||||
1. merge PR 不直接部署
|
||||
2. 创建一个 **draft release**(草稿)
|
||||
3. **需要人工在 Gitea/GitHub 平台点 "Publish" 按钮** 才触发部署
|
||||
4. Draft 可以审查 SQL migration、回滚脚本、changelog 等"产物清单"
|
||||
5. Publish 后才打 tag + 触发 build.yaml
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标
|
||||
|
||||
- 在 merge main 和"真正部署"之间插入 **人工 publish 闸门**
|
||||
- 提供 **release 产物清单预览**(SQL migration / 回滚脚本 / changelog)
|
||||
- 支持 **一键回滚**(从 draft release 追溯回滚脚本)
|
||||
- 与现有 ai-proj MCP 工具链集成
|
||||
|
||||
---
|
||||
|
||||
## 3. 架构设计
|
||||
|
||||
### 3.1 流程对比
|
||||
|
||||
**当前流程:**
|
||||
```
|
||||
[feat/xxx] → PR → merge develop → 测试
|
||||
[develop] → PR → merge main → ✗ 立即触发 build.yaml → 生产部署
|
||||
```
|
||||
|
||||
**新流程:**
|
||||
```
|
||||
[feat/xxx] → PR → merge develop → 测试
|
||||
[develop] → PR → merge main → Gitea draft release(待审查)
|
||||
↓
|
||||
人工 publish
|
||||
↓
|
||||
tag 推送 + 触发 build.yaml → 生产部署
|
||||
```
|
||||
|
||||
### 3.2 组件职责
|
||||
|
||||
| 组件 | 新增/改造 | 职责 |
|
||||
|------|---------|------|
|
||||
| **ai-proj backend** | 新增 | `/api/v1/releases` REST API(创建 draft、publish、查询、回滚) |
|
||||
| **mcp-task-bridge** | 新增工具 | `create_release_draft` / `publish_release` / `list_releases` / `get_release` / `rollback_release` |
|
||||
| **Gitea Actions** | 改造 | `build.yaml` 触发条件改为 `release.published` 事件 |
|
||||
| **`release` skill** | 新增 | AI 侧命令封装(`/release new`, `/release publish`, `/release rollback`) |
|
||||
| **release-draft.sh** | 新增脚本 | 本地命令行工具,用于在 CI 外手动创建 draft |
|
||||
|
||||
### 3.3 数据模型
|
||||
|
||||
**新增 `releases` 表:**
|
||||
|
||||
```sql
|
||||
CREATE TABLE releases (
|
||||
id SERIAL PRIMARY KEY,
|
||||
display_id VARCHAR(64) UNIQUE NOT NULL, -- e.g. "RELEASE-20260416-001"
|
||||
version VARCHAR(64) NOT NULL, -- e.g. "v1.2.0"
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(32) NOT NULL, -- draft / published / rolled_back
|
||||
git_base_ref VARCHAR(255) NOT NULL, -- e.g. previous tag "v1.1.9"
|
||||
git_head_ref VARCHAR(255) NOT NULL, -- e.g. commit sha after merge
|
||||
git_tag VARCHAR(64), -- 生成的 tag,publish 后才有
|
||||
changelog_md TEXT NOT NULL, -- 自动生成的 changelog
|
||||
sql_migration_paths TEXT[], -- 合并的 SQL 文件路径列表
|
||||
sql_rollback_md TEXT, -- 回滚脚本
|
||||
gitea_release_id BIGINT, -- Gitea 上的 release ID
|
||||
gitea_release_url VARCHAR(512),
|
||||
created_by INT REFERENCES users(id),
|
||||
published_by INT REFERENCES users(id),
|
||||
published_at TIMESTAMP,
|
||||
rolled_back_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 3.4 API 设计
|
||||
|
||||
| Endpoint | 作用 | 权限 |
|
||||
|----------|------|------|
|
||||
| `POST /api/v1/releases/drafts` | 创建 draft release(自动推导版本、生成 changelog) | 开发者 |
|
||||
| `POST /api/v1/releases/:id/publish` | 发布(推 tag、触发部署) | 需确认 |
|
||||
| `GET /api/v1/releases` | 列表 | 所有人 |
|
||||
| `GET /api/v1/releases/:id` | 详情 | 所有人 |
|
||||
| `POST /api/v1/releases/:id/rollback` | 回滚到上一版本 | 管理员 |
|
||||
|
||||
### 3.5 Gitea 集成
|
||||
|
||||
**创建 draft release(调 Gitea API):**
|
||||
```http
|
||||
POST /api/v1/repos/{owner}/{repo}/releases
|
||||
{
|
||||
"tag_name": "v1.2.0",
|
||||
"target_commitish": "main",
|
||||
"name": "v1.2.0",
|
||||
"body": "...<changelog>...",
|
||||
"draft": true
|
||||
}
|
||||
```
|
||||
|
||||
**build.yaml 改造:**
|
||||
|
||||
```yaml
|
||||
# Before
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
# After
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch: # 保留手动触发
|
||||
```
|
||||
|
||||
**关键变化**:`release.published` 事件只在草稿被 publish 时触发,merge main 不再自动触发。
|
||||
|
||||
---
|
||||
|
||||
## 4. 用户流程(典型场景)
|
||||
|
||||
### 4.1 开发者发版
|
||||
|
||||
```
|
||||
1. 合并 PR 到 main(照旧)
|
||||
2. 在 Claude Code 里:/release new
|
||||
- AI 读 git log,自动推导版本号
|
||||
- 汇总 SQL migration
|
||||
- 生成 changelog
|
||||
- 生成回滚脚本
|
||||
- 展示产物清单,等用户确认
|
||||
3. 确认后:MCP create_release_draft → 后端 API → Gitea draft
|
||||
4. 收到 Slack/企微通知:"draft release v1.2.0 已创建,请审查"
|
||||
5. 打开 Gitea 查看产物清单
|
||||
6. 点 "Publish release" 按钮
|
||||
7. CI/CD 自动触发 → 生产部署
|
||||
```
|
||||
|
||||
### 4.2 回滚
|
||||
|
||||
```
|
||||
1. /release rollback v1.2.0
|
||||
2. MCP rollback_release → 后端执行回滚 SQL → 重新部署上一版本镜像
|
||||
3. 记录到 releases 表:status=rolled_back
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 渐进式落地路径(P0-3 拆分子任务)
|
||||
|
||||
由于 P0-3 完整实现涉及后端 + MCP + CI/CD + skill 四个层面,**不建议单会话完成**。拆分为:
|
||||
|
||||
| 子任务 | 范围 | 风险 |
|
||||
|-------|------|------|
|
||||
| **P0-3.1** | 后端 release model + migration | 低(只是建表) |
|
||||
| **P0-3.2** | 后端 /api/v1/releases API(draft + publish) | 中 |
|
||||
| **P0-3.3** | MCP 工具 create_release_draft / publish_release | 中 |
|
||||
| **P0-3.4** | `release` skill 创建 | 低 |
|
||||
| **P0-3.5** | Gitea draft release 集成 | 中 |
|
||||
| **P0-3.6** | build.yaml 改为 release.published 触发 | **高**(影响所有人的发版流程) |
|
||||
| **P0-3.7** | 回滚能力 | 中 |
|
||||
|
||||
**推荐落地节奏:**
|
||||
- 先做 P0-3.1 ~ P0-3.5(构建基础能力)
|
||||
- **保留旧 push-to-main 流程作为 fallback**
|
||||
- 跑 2 周观察
|
||||
- 再做 P0-3.6(切换 CI/CD 触发源)
|
||||
- 最后 P0-3.7(回滚能力)
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与应对
|
||||
|
||||
### R1: build.yaml 触发源切换影响所有开发者
|
||||
|
||||
**影响**:目前大家习惯 "merge main 即部署",切换后需等 publish
|
||||
|
||||
**应对**:
|
||||
- 上线前 1 周发邮件 + 企微通知
|
||||
- 提供手动触发(workflow_dispatch)作为应急入口
|
||||
- 文档化新流程
|
||||
|
||||
### R2: 回滚脚本自动生成不够可靠
|
||||
|
||||
**影响**:复杂 migration(如数据迁移)的回滚无法自动推导
|
||||
|
||||
**应对**:
|
||||
- AI 只生成简单反向 DDL(CREATE→DROP 等)
|
||||
- 复杂情况标记 `⚠️ 需手工补全`
|
||||
- 回滚 SQL 由人工审查后纳入 draft release
|
||||
|
||||
### R3: draft release 被忘记 publish
|
||||
|
||||
**影响**:功能开发完但生产没部署
|
||||
|
||||
**应对**:
|
||||
- 企微机器人每天检查超 24h 未 publish 的 draft,发提醒
|
||||
- 管理员 dashboard 显示 draft 列表
|
||||
|
||||
---
|
||||
|
||||
## 7. 验收标准
|
||||
|
||||
- [ ] `releases` 表创建,migration 落地
|
||||
- [ ] `/api/v1/releases/drafts` 创建 draft(至少支持最小字段)
|
||||
- [ ] MCP `create_release_draft` 工具可用
|
||||
- [ ] 能从 Claude Code 一键创建 Gitea draft release
|
||||
- [ ] build.yaml 支持 `release.published` 触发
|
||||
- [ ] 至少跑通 1 次完整流程(draft → 人工 publish → 自动部署)
|
||||
- [ ] 回滚脚本自动生成覆盖 >80% 的 DDL 场景
|
||||
|
||||
---
|
||||
|
||||
## 8. 参考
|
||||
|
||||
- devflow-claude: `plugins/req/commands/release.md`
|
||||
- REQ-20260416-0017(母需求)
|
||||
- 当前 build.yaml: `/Users/donglinlai/coding/qiudl/new-ai-proj/.gitea/workflows/build.yaml`
|
||||
- Gitea Release API: https://docs.gitea.com/api/next/#tag/repository/operation/repoCreateRelease
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一步行动
|
||||
|
||||
- [ ] 本文档提交评审
|
||||
- [ ] 评审通过后拆 7 个子任务(P0-3.1 ~ P0-3.7)并分别关联到 REQ-20260416-0017
|
||||
- [ ] 从 P0-3.1(数据模型)开始实施
|
||||
100
hooks/README.md
Normal file
100
hooks/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Claude Code Hooks
|
||||
|
||||
本目录包含 ai-proj-helper 体系的 Claude Code 钩子脚本。
|
||||
|
||||
**REQ-20260416-0017 P0 批 — 源自 devflow-claude 借鉴**
|
||||
|
||||
## 脚本清单
|
||||
|
||||
| 脚本 | 事件 | 作用 |
|
||||
|------|------|------|
|
||||
| `session-context.sh` | SessionStart | 从分支名解析 REQ-ID,注入需求上下文到会话 |
|
||||
| `pre-tool-confirm.sh` | PreToolUse (Bash) | 拦截生产发布、force push、docker 生产容器、reset --hard 等危险操作 |
|
||||
|
||||
## 安装(用户级,一次即可)
|
||||
|
||||
### 方式 1:编辑 `~/.claude/settings.json`
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
|
||||
"timeout": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/pre-tool-confirm.sh",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方式 2:一键安装脚本
|
||||
|
||||
```bash
|
||||
bash /Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/install.sh
|
||||
```
|
||||
|
||||
## 验证
|
||||
|
||||
### SessionStart hook
|
||||
|
||||
1. 切到一个带 REQ-ID 的分支:`git checkout feat/REQ-20260416-0017-xxx`
|
||||
2. 打开新的 Claude Code 会话
|
||||
3. 会话开头应看到 `# 需求上下文(SessionStart Hook)` 块
|
||||
|
||||
### PreToolUse hook
|
||||
|
||||
让 AI 尝试执行这些命令中的任一条,应弹出原生确认对话框:
|
||||
|
||||
- `git push origin main`
|
||||
- `git push --force`
|
||||
- `tea pr merge --base main`
|
||||
- `docker rm ai_postgres_prod`
|
||||
- `git reset --hard`
|
||||
|
||||
## 依赖
|
||||
|
||||
- `bash` (macOS / Linux 默认)
|
||||
- `jq` (PreToolUse hook 需要,`brew install jq`)
|
||||
- `python3` (SessionStart hook 解析 JSON)
|
||||
- `curl` (SessionStart hook 调 MCP API)
|
||||
|
||||
## 自定义
|
||||
|
||||
### 修改 MCP API 地址
|
||||
|
||||
在项目根目录创建 `.ai-proj-env`:
|
||||
|
||||
```bash
|
||||
export AI_PROJ_API_BASE="https://api.ai-proj.example.com"
|
||||
export AI_PROJ_MCP_KEY="your-mcp-api-key"
|
||||
```
|
||||
|
||||
或设置全局环境变量。
|
||||
|
||||
### 拦截更多命令
|
||||
|
||||
编辑 `pre-tool-confirm.sh`,在 `REASON` 赋值的 elif 链中增加规则。
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **快速失败退出**:不处理的命令立即 `exit 0`,不影响性能
|
||||
2. **非侵入性**:网络/依赖缺失时静默退出,不阻塞正常工作流
|
||||
3. **可复现**:hook 脚本跟随仓库分发,方便团队一致部署
|
||||
81
hooks/install.sh
Executable file
81
hooks/install.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
# install.sh
|
||||
# 一键把 ai-proj-helper hooks 注册到 ~/.claude/settings.json
|
||||
#
|
||||
# 用法: bash hooks/install.sh
|
||||
|
||||
set -e
|
||||
|
||||
HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SETTINGS_FILE="$HOME/.claude/settings.json"
|
||||
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo "❌ 需要 python3(用于合并 JSON)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "⚠️ 未安装 jq,PreToolUse hook 将无法正常工作"
|
||||
echo " 请执行: brew install jq"
|
||||
fi
|
||||
|
||||
mkdir -p "$HOME/.claude"
|
||||
|
||||
python3 << EOF
|
||||
import json
|
||||
import os
|
||||
|
||||
settings_file = "$SETTINGS_FILE"
|
||||
hooks_dir = "$HOOKS_DIR"
|
||||
|
||||
# 读已有配置
|
||||
if os.path.exists(settings_file):
|
||||
with open(settings_file) as f:
|
||||
data = json.load(f)
|
||||
else:
|
||||
data = {}
|
||||
|
||||
# 合并 hooks
|
||||
hooks = data.setdefault("hooks", {})
|
||||
|
||||
# SessionStart
|
||||
session_start = hooks.setdefault("SessionStart", [])
|
||||
session_cmd = f"{hooks_dir}/session-context.sh"
|
||||
already_registered = any(
|
||||
any(h.get("command") == session_cmd for h in entry.get("hooks", []))
|
||||
for entry in session_start
|
||||
)
|
||||
if not already_registered:
|
||||
session_start.append({
|
||||
"hooks": [{"type": "command", "command": session_cmd, "timeout": 10}]
|
||||
})
|
||||
print("✅ 注册 SessionStart hook")
|
||||
else:
|
||||
print("⏭️ SessionStart hook 已存在")
|
||||
|
||||
# PreToolUse (Bash)
|
||||
pre_tool = hooks.setdefault("PreToolUse", [])
|
||||
pre_cmd = f"{hooks_dir}/pre-tool-confirm.sh"
|
||||
already_registered = any(
|
||||
entry.get("matcher") == "Bash" and any(h.get("command") == pre_cmd for h in entry.get("hooks", []))
|
||||
for entry in pre_tool
|
||||
)
|
||||
if not already_registered:
|
||||
pre_tool.append({
|
||||
"matcher": "Bash",
|
||||
"hooks": [{"type": "command", "command": pre_cmd, "timeout": 30}]
|
||||
})
|
||||
print("✅ 注册 PreToolUse (Bash) hook")
|
||||
else:
|
||||
print("⏭️ PreToolUse hook 已存在")
|
||||
|
||||
# 写回
|
||||
with open(settings_file, "w") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n📝 已更新: {settings_file}")
|
||||
print("\n下一步:")
|
||||
print(" 1. 重启 Claude Code 会话")
|
||||
print(" 2. 切到含 REQ-ID 的分支测试 SessionStart hook")
|
||||
print(" 3. 让 AI 尝试 'git push origin main' 测试 PreToolUse hook")
|
||||
EOF
|
||||
94
hooks/pre-tool-confirm.sh
Executable file
94
hooks/pre-tool-confirm.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# pre-tool-confirm.sh
|
||||
# PreToolUse Hook: 拦截危险操作,强制原生确认对话框
|
||||
#
|
||||
# 输入格式(stdin JSON):
|
||||
# { "tool_name": "Bash", "tool_input": { "command": "..." } }
|
||||
#
|
||||
# 输出格式(stdout JSON):
|
||||
# { "hookSpecificOutput": { "hookEventName": "PreToolUse",
|
||||
# "permissionDecision": "ask",
|
||||
# "permissionDecisionReason": "..." } }
|
||||
#
|
||||
# 安装方式:在 ~/.claude/settings.json 配置
|
||||
# hooks.PreToolUse:
|
||||
# - matcher: "Bash"
|
||||
# hooks:
|
||||
# - type: command
|
||||
# command: "<path>/hooks/pre-tool-confirm.sh"
|
||||
# timeout: 30
|
||||
#
|
||||
# 参考:devflow-claude confirm-before-commit.sh + ai-proj memory 规则
|
||||
# REQ-20260416-0017 P0-2
|
||||
|
||||
set -e
|
||||
|
||||
INPUT=$(cat)
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
# 没有 jq 就不处理
|
||||
exit 0
|
||||
fi
|
||||
|
||||
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
||||
|
||||
if [ -z "$COMMAND" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REASON=""
|
||||
|
||||
# ============ 1. 生产分支推送 ============
|
||||
if echo "$COMMAND" | grep -qE '\bgit\s+push\b.*\b(origin\s+)?(main|master)\b'; then
|
||||
REASON="⚠️ 即将推送到 main/master 生产分支。确认已过 PR 评审?"
|
||||
|
||||
# ============ 2. 强制推送 ============
|
||||
elif echo "$COMMAND" | grep -qE '\bgit\s+push\b.*(--force|--force-with-lease|-f\b)'; then
|
||||
REASON="⛔ 危险:force push 会覆盖远程历史,可能丢失他人提交。确认继续?"
|
||||
|
||||
# ============ 3. tea pr merge --base main ============
|
||||
elif echo "$COMMAND" | grep -qE '\btea\s+pr\s+merge\b.*--base\s+main\b'; then
|
||||
REASON="⚠️ 即将合并 PR 到 main 分支,合并后将触发生产部署。确认 PR 已测试?"
|
||||
|
||||
# ============ 4. docker rm/stop 生产容器 ============
|
||||
elif echo "$COMMAND" | grep -qE '\bdocker\s+(rm|stop|kill)\b.*\b(ai_postgres_prod|ai_backend_prod|ai_frontend_prod|ai_redis_prod)\b'; then
|
||||
REASON="⛔ 危险:即将停止/删除生产容器!2026-04-05 曾因此宕机 15 分钟。确认是灾难恢复?"
|
||||
|
||||
# ============ 5. docker rm/stop 其他 prod 相关 ============
|
||||
elif echo "$COMMAND" | grep -qE '\bdocker\s+(rm|stop|kill)\b.*_prod\b'; then
|
||||
REASON="⚠️ 即将停止/删除含 _prod 的容器。确认是生产环境?"
|
||||
|
||||
# ============ 6. git reset --hard / clean -fd ============
|
||||
elif echo "$COMMAND" | grep -qE '\bgit\s+reset\s+--hard\b'; then
|
||||
REASON="⚠️ git reset --hard 会丢弃所有未提交改动。确认继续?"
|
||||
|
||||
elif echo "$COMMAND" | grep -qE '\bgit\s+clean\s+.*-f'; then
|
||||
REASON="⚠️ git clean -f 会删除未跟踪文件。确认继续?"
|
||||
|
||||
# ============ 7. rm -rf / 系统路径 ============
|
||||
elif echo "$COMMAND" | grep -qE '\brm\s+.*-[rf]+.*\s+(/|/\*|~|\$HOME)'; then
|
||||
REASON="⛔ 危险:rm -rf 指向系统根或家目录。确认继续?"
|
||||
|
||||
# ============ 8. ssh 生产服务器 + 破坏性命令 ============
|
||||
elif echo "$COMMAND" | grep -qE 'ssh\s+\S*prod\S*.*\b(rm|drop|truncate|delete)\b'; then
|
||||
REASON="⛔ 危险:在生产服务器执行破坏性命令。确认继续?"
|
||||
|
||||
# ============ 9. psql/mysql 生产数据库 + DROP/TRUNCATE/DELETE ============
|
||||
elif echo "$COMMAND" | grep -qiE '(psql|mysql).*prod.*\b(DROP|TRUNCATE|DELETE FROM)\b'; then
|
||||
REASON="⛔ 危险:生产数据库 DROP/TRUNCATE/DELETE。确认已备份?"
|
||||
|
||||
# ============ 10. MCP advance_delivery_stage force=true ============
|
||||
# 这种会走 MCP 而不是 Bash,本 hook 不好拦,留给另一个 matcher 处理
|
||||
fi
|
||||
|
||||
if [ -n "$REASON" ]; then
|
||||
jq -n --arg reason "$REASON" '{
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "PreToolUse",
|
||||
permissionDecision: "ask",
|
||||
permissionDecisionReason: $reason
|
||||
}
|
||||
}'
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
161
hooks/release-draft.sh
Executable file
161
hooks/release-draft.sh
Executable file
@@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
# release-draft.sh
|
||||
# 最小可用版:从本地 git 仓库创建 Gitea draft release
|
||||
#
|
||||
# 用法:
|
||||
# export GITEA_TOKEN=$(bw get password "Gitea - qiudl Token")
|
||||
# bash release-draft.sh v1.2.0 [--from v1.1.9]
|
||||
#
|
||||
# 输出: 创建的 draft release URL(待人工 publish)
|
||||
# REQ-20260416-0017 P0-3 最小可用脚本
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="$1"
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "❌ 用法: $0 <version> [--from <previous_tag>]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FROM_TAG=""
|
||||
if [ "$2" = "--from" ]; then
|
||||
FROM_TAG="$3"
|
||||
fi
|
||||
|
||||
if ! git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
echo "❌ 不在 git 仓库内"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPO_SLUG=$(git remote get-url origin 2>/dev/null | \
|
||||
sed -E 's|.*[:/]([^/]+/[^/]+)\.git$|\1|' | \
|
||||
sed -E 's|.*[:/]([^/]+/[^/]+)$|\1|')
|
||||
|
||||
if [ -z "$REPO_SLUG" ]; then
|
||||
echo "❌ 无法从 git remote 推断 OWNER/REPO"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "❌ 需要 GITEA_TOKEN 环境变量"
|
||||
echo " export GITEA_TOKEN=\$(bw get password 'Gitea - qiudl Token')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITEA_URL="${GITEA_URL:-https://gitea.pipexerp.com}"
|
||||
|
||||
# 推断 from
|
||||
if [ -z "$FROM_TAG" ]; then
|
||||
FROM_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
TO_REF="HEAD"
|
||||
|
||||
# 生成 changelog 内容
|
||||
echo "📋 生成 changelog..."
|
||||
|
||||
CHANGELOG=""
|
||||
if [ -n "$FROM_TAG" ]; then
|
||||
COMMITS=$(git log --pretty=format:'- %s (%h)' "${FROM_TAG}..${TO_REF}" 2>/dev/null || echo "")
|
||||
else
|
||||
COMMITS=$(git log --pretty=format:'- %s (%h)' "${TO_REF}" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
if [ -z "$COMMITS" ]; then
|
||||
echo "⚠️ 无 commit,放弃"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 按类型分组
|
||||
FEATS=$(echo "$COMMITS" | grep -iE 'feat(\(|:)|新功能' || true)
|
||||
FIXES=$(echo "$COMMITS" | grep -iE 'fix(\(|:)|修复' || true)
|
||||
CHORES=$(echo "$COMMITS" | grep -iE 'chore(\(|:)' || true)
|
||||
OTHERS=$(echo "$COMMITS" | grep -vE 'feat(\(|:)|fix(\(|:)|chore(\(|:)|新功能|修复' || true)
|
||||
|
||||
CHANGELOG="## 发布内容
|
||||
|
||||
**版本**: \`${VERSION}\`
|
||||
**区间**: \`${FROM_TAG:-init}..${TO_REF}\`
|
||||
|
||||
"
|
||||
|
||||
if [ -n "$FEATS" ]; then
|
||||
CHANGELOG="${CHANGELOG}### 新功能
|
||||
|
||||
${FEATS}
|
||||
|
||||
"
|
||||
fi
|
||||
|
||||
if [ -n "$FIXES" ]; then
|
||||
CHANGELOG="${CHANGELOG}### Bug 修复
|
||||
|
||||
${FIXES}
|
||||
|
||||
"
|
||||
fi
|
||||
|
||||
if [ -n "$CHORES" ]; then
|
||||
CHANGELOG="${CHANGELOG}### 杂项
|
||||
|
||||
${CHORES}
|
||||
|
||||
"
|
||||
fi
|
||||
|
||||
if [ -n "$OTHERS" ]; then
|
||||
CHANGELOG="${CHANGELOG}### 其他
|
||||
|
||||
${OTHERS}
|
||||
|
||||
"
|
||||
fi
|
||||
|
||||
CHANGELOG="${CHANGELOG}
|
||||
|
||||
---
|
||||
|
||||
⚠️ **这是 draft release**,审查无误后点击 'Publish release' 按钮才会触发生产部署。
|
||||
|
||||
📋 审查要点:
|
||||
- [ ] 所有改动已过 PR 评审
|
||||
- [ ] SQL migration 已验证(如有)
|
||||
- [ ] 回滚方案已确认(如有)
|
||||
- [ ] 生产环境准备就绪"
|
||||
|
||||
# 创建 draft release
|
||||
echo "🚀 创建 Gitea draft release..."
|
||||
|
||||
BODY_JSON=$(python3 -c "
|
||||
import json
|
||||
print(json.dumps({
|
||||
'tag_name': '$VERSION',
|
||||
'target_commitish': 'main',
|
||||
'name': '$VERSION',
|
||||
'body': '''$CHANGELOG''',
|
||||
'draft': True,
|
||||
'prerelease': False,
|
||||
}))
|
||||
")
|
||||
|
||||
RESP=$(curl -s -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$BODY_JSON" \
|
||||
"${GITEA_URL}/api/v1/repos/${REPO_SLUG}/releases")
|
||||
|
||||
HTML_URL=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('html_url',''))" 2>/dev/null)
|
||||
|
||||
if [ -n "$HTML_URL" ]; then
|
||||
echo "✅ Draft release 已创建"
|
||||
echo "🔗 $HTML_URL"
|
||||
echo ""
|
||||
echo "⏭️ 下一步:"
|
||||
echo " 1. 打开链接审查产物清单"
|
||||
echo " 2. 确认无误后点 'Publish release' 按钮"
|
||||
echo " 3. CI/CD 将自动触发生产部署"
|
||||
else
|
||||
echo "❌ 创建失败"
|
||||
echo "$RESP" | head -20
|
||||
exit 1
|
||||
fi
|
||||
130
hooks/session-context.sh
Executable file
130
hooks/session-context.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
# session-context.sh
|
||||
# SessionStart Hook: 会话启动时自动注入需求上下文
|
||||
#
|
||||
# 从当前 Git 分支名解析 REQ-ID,调用 ai-proj MCP API 查询需求详情,
|
||||
# 把标题 / 状态 / delivery_stage / reviewer / 进行中需求数注入 system-reminder。
|
||||
#
|
||||
# 安装方式:
|
||||
# 在 ~/.claude/settings.json 的 hooks.SessionStart 配置:
|
||||
# {
|
||||
# "command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
|
||||
# "timeout": 10
|
||||
# }
|
||||
#
|
||||
# 参考:devflow-claude 同名脚本 + ai-proj MCP 适配
|
||||
# REQ-20260416-0017 P0-1
|
||||
|
||||
set -e
|
||||
|
||||
# 仅在 git 仓库内执行
|
||||
if ! git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||
if [ -z "$REPO_ROOT" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
# ============ 1. 当前分支 → REQ ID ============
|
||||
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
|
||||
REQ_ID=""
|
||||
|
||||
if [ -n "$BRANCH" ]; then
|
||||
# 匹配 feat/REQ-20260416-0017-xxx / fix/REQ-20260416-0017 / feature/req-20260416-0017
|
||||
REQ_ID=$(echo "$BRANCH" | grep -oiE 'REQ-[0-9]{8}-[0-9]{4}' | head -1 | tr '[:lower:]' '[:upper:]')
|
||||
fi
|
||||
|
||||
# ============ 2. 无 REQ 时仅输出分支信息(静默退出条件) ============
|
||||
if [ -z "$REQ_ID" ]; then
|
||||
# 只要不在 main/develop 上就提示一下
|
||||
case "$BRANCH" in
|
||||
main|master|develop|"") exit 0 ;;
|
||||
esac
|
||||
echo "# 会话上下文"
|
||||
echo ""
|
||||
echo "- 当前分支: \`${BRANCH}\`(未检测到 REQ ID)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ============ 3. 查询 MCP API ============
|
||||
# MCP API 通过 localhost:8080 直连(ai-proj 本地后端)或 ai-proj-prod
|
||||
# 这里优先读项目根的 .ai-proj-env 决定环境
|
||||
API_BASE="${AI_PROJ_API_BASE:-}"
|
||||
API_TOKEN="${AI_PROJ_MCP_KEY:-}"
|
||||
|
||||
if [ -f "$REPO_ROOT/.ai-proj-env" ]; then
|
||||
# shellcheck disable=SC1091
|
||||
source "$REPO_ROOT/.ai-proj-env"
|
||||
fi
|
||||
|
||||
if [ -z "$API_BASE" ]; then
|
||||
# 默认走本地 dev
|
||||
API_BASE="http://localhost:8080"
|
||||
fi
|
||||
|
||||
# 查询需求
|
||||
RESP=""
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
if [ -n "$API_TOKEN" ]; then
|
||||
RESP=$(curl -s --max-time 3 -H "X-MCP-API-Key: $API_TOKEN" \
|
||||
"${API_BASE}/api/v1/mcp/requirements/by-display-id/${REQ_ID}" 2>/dev/null || echo "")
|
||||
else
|
||||
RESP=$(curl -s --max-time 3 \
|
||||
"${API_BASE}/api/v1/mcp/requirements/by-display-id/${REQ_ID}" 2>/dev/null || echo "")
|
||||
fi
|
||||
fi
|
||||
|
||||
# ============ 4. 解析并输出 ============
|
||||
echo "# 需求上下文(SessionStart Hook)"
|
||||
echo ""
|
||||
echo "- 分支: \`${BRANCH}\`"
|
||||
echo "- 需求: **${REQ_ID}**"
|
||||
|
||||
if [ -n "$RESP" ] && command -v python3 >/dev/null 2>&1; then
|
||||
# 尝试用 python 解析
|
||||
PARSED=$(python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
d = json.loads('''$RESP''')
|
||||
data = d.get('data', {})
|
||||
if not data:
|
||||
sys.exit(0)
|
||||
title = data.get('title', '?')
|
||||
status = data.get('status', '?')
|
||||
stage = data.get('delivery_stage', '?')
|
||||
priority = data.get('priority', '?')
|
||||
project = data.get('project_name', '?')
|
||||
print(f'title={title}')
|
||||
print(f'status={status}')
|
||||
print(f'stage={stage}')
|
||||
print(f'priority={priority}')
|
||||
print(f'project={project}')
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null)
|
||||
|
||||
if [ -n "$PARSED" ]; then
|
||||
TITLE=$(echo "$PARSED" | grep '^title=' | sed 's/^title=//')
|
||||
STATUS=$(echo "$PARSED" | grep '^status=' | sed 's/^status=//')
|
||||
STAGE=$(echo "$PARSED" | grep '^stage=' | sed 's/^stage=//')
|
||||
PRIORITY=$(echo "$PARSED" | grep '^priority=' | sed 's/^priority=//')
|
||||
PROJECT=$(echo "$PARSED" | grep '^project=' | sed 's/^project=//')
|
||||
|
||||
[ -n "$TITLE" ] && echo "- 标题: ${TITLE}"
|
||||
[ -n "$PROJECT" ] && echo "- 项目: ${PROJECT}"
|
||||
[ -n "$STATUS" ] && echo "- 状态: ${STATUS}"
|
||||
[ -n "$STAGE" ] && echo "- 交付阶段: ${STAGE}"
|
||||
[ -n "$PRIORITY" ] && echo "- 优先级: ${PRIORITY}"
|
||||
else
|
||||
echo "- 📡 MCP API 响应为空或未授权(API_BASE=${API_BASE})"
|
||||
fi
|
||||
else
|
||||
echo "- ⚠️ 无法连接 MCP API(${API_BASE}),仅显示分支信息"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "💡 相关命令:\`/req get ${REQ_ID}\` 查看详情 · \`/commit\` 智能提交"
|
||||
@@ -490,3 +490,55 @@ fi
|
||||
| `dev-ios` | iOS 开发(插件,按需加载)|
|
||||
| `dev-android` | Android 开发(插件,按需加载)|
|
||||
| `dev-mcp` | MCP bridge 开发(插件,按需加载)|
|
||||
|
||||
---
|
||||
|
||||
## CLAUDE.md 架构检查机制(REQ-20260416-0017 P0-5)
|
||||
|
||||
**原则:本 skill 不硬编码任何项目的架构细节,从项目 CLAUDE.md 读取**。
|
||||
|
||||
### 为什么
|
||||
|
||||
同一套 skill 要支持多个技术栈(Go+Gin / React+AntD / Vue+Element / Python+FastAPI)。如果把分层、命名、目录结构写死在 SKILL.md 里,跨项目就会冲突。
|
||||
|
||||
devflow-claude 的做法(借鉴):skill 只管**流程和模板**,项目架构由 CLAUDE.md 的 "Architecture" / "项目架构" 章节定义。
|
||||
|
||||
### 执行前检查
|
||||
|
||||
开始编码任务前,skill 先检查项目根 `CLAUDE.md`:
|
||||
|
||||
```bash
|
||||
# 检查 CLAUDE.md 是否含架构关键词
|
||||
if [ -f "CLAUDE.md" ]; then
|
||||
if grep -qiE "(架构|分层|目录结构|tech stack|architecture|project structure)" CLAUDE.md; then
|
||||
echo "✅ 检测到项目架构信息"
|
||||
else
|
||||
echo "⚠️ CLAUDE.md 缺少架构描述"
|
||||
echo " dev-coding 需要架构信息来生成准确的文件路径和分层顺序"
|
||||
echo ""
|
||||
echo " 📋 建议操作:"
|
||||
echo " - 查看预置架构片段: ai-proj-helper/skills-dev/dev-coding-plugin/templates/claude-md-snippets/"
|
||||
echo " - 选择匹配技术栈的片段,补充到 CLAUDE.md 的 '## Architecture' 章节"
|
||||
echo ""
|
||||
echo " ⚠️ 继续执行,但生成的文件路径可能不够准确"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ 未找到项目 CLAUDE.md,建议创建"
|
||||
fi
|
||||
```
|
||||
|
||||
### 架构片段模板库
|
||||
|
||||
位于 `skills-dev/dev-coding-plugin/templates/claude-md-snippets/`:
|
||||
|
||||
| 文件 | 适用场景 |
|
||||
|------|---------|
|
||||
| `go-gin-gorm.md` | Go + Gin + GORM 后端(ai-proj backend 风格) |
|
||||
| `react-antd.md` | React + TypeScript + Ant Design(ai-proj frontend 风格) |
|
||||
| `vue-element.md` | Vue 3 + Element Plus(coolbuy-paas 风格) |
|
||||
| `mcp-typescript.md` | MCP bridge TypeScript(mcp-task-bridge 风格) |
|
||||
| `generic.md` | 通用空白骨架 |
|
||||
|
||||
### 非阻断原则
|
||||
|
||||
架构信息缺失时**仅警告不阻止**。用户仍可继续,但会被告知"生成的建议可能不够准确"。
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节,按实际情况填写 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 技术栈
|
||||
|
||||
- **语言**: _TODO_
|
||||
- **框架**: _TODO_
|
||||
- **数据库**: _TODO_
|
||||
- **缓存**: _TODO_
|
||||
- **部署**: _TODO_
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
project-root/
|
||||
├── ???/ # _TODO: 说明_
|
||||
├── ???/ # _TODO_
|
||||
└── ???/
|
||||
```
|
||||
|
||||
### 分层 / 模块规则
|
||||
|
||||
1. _TODO: 依赖方向_
|
||||
2. _TODO: 允许/禁止的跨层调用_
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| _TODO_ | _TODO_ | _TODO_ |
|
||||
|
||||
### 错误处理
|
||||
|
||||
_TODO_
|
||||
|
||||
### 日志
|
||||
|
||||
_TODO_
|
||||
|
||||
### 测试
|
||||
|
||||
_TODO_
|
||||
|
||||
### 其他关键约定
|
||||
|
||||
- _TODO_
|
||||
@@ -0,0 +1,56 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 分层结构(Go + Gin + GORM)
|
||||
|
||||
```
|
||||
backend/
|
||||
├── routes/ # HTTP 路由定义(按模块拆分)
|
||||
├── handlers/ # 请求解析 + 响应组装(薄层,不含业务)
|
||||
├── services/ # 业务逻辑(事务、组合、校验)
|
||||
├── models/ # GORM 数据模型
|
||||
├── database/ # Repository 层(SQL、查询)
|
||||
├── middleware/ # 认证、CORS、日志、限流
|
||||
├── migrations/ # SQL 迁移文件
|
||||
└── utils/ # 通用工具(密码、签名等)
|
||||
```
|
||||
|
||||
### 分层规则(强制)
|
||||
|
||||
1. **请求流向**:Route → Handler → Service → Database → Models
|
||||
2. **Handler 禁止直接访问 database**:必须走 Service 层
|
||||
3. **Service 禁止调用 Handler 或 Route**:单向依赖
|
||||
4. **Model 仅定义结构 + GORM tag**:不含业务方法
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| 文件名 | snake_case | `user_service.go` |
|
||||
| 包名 | lowercase | `services`, `handlers` |
|
||||
| 导出函数/类型 | PascalCase | `CreateUser`, `UserRepository` |
|
||||
| 内部函数 | camelCase | `validatePassword` |
|
||||
| 常量 | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` |
|
||||
|
||||
### 错误处理
|
||||
|
||||
- 使用 `errors.New()` 或自定义 error type
|
||||
- Handler 层统一返回 `{"code": X, "msg": "...", "data": ...}`
|
||||
- Service 层返回原始 error,由 Handler 转换
|
||||
|
||||
### 日志
|
||||
|
||||
- 使用结构化 log:`log.WithField("user_id", uid).Info("...")`
|
||||
- 禁用 `fmt.Println` / `print`
|
||||
|
||||
### 测试
|
||||
|
||||
- 单元测试文件名:`xxx_test.go`
|
||||
- 使用 `testify/assert`
|
||||
- Mock 用 `testify/mock` 或 `gomock`
|
||||
|
||||
### 依赖检查
|
||||
|
||||
- **新 handler 禁止直接 `import database/`**:需走 Service 层
|
||||
- `./scripts/check-architecture.sh check` 作为 CI 门禁
|
||||
@@ -0,0 +1,75 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 目录结构(MCP Bridge - TypeScript)
|
||||
|
||||
```
|
||||
mcp-task-bridge/
|
||||
├── src/
|
||||
│ ├── tools/ # MCP tool 定义(每个工具一个文件)
|
||||
│ ├── resources/ # MCP resources(若有)
|
||||
│ ├── prompts/ # MCP prompts(若有)
|
||||
│ ├── client/ # 后端 REST API 客户端
|
||||
│ ├── utils/ # 工具函数
|
||||
│ └── index.ts # 入口
|
||||
├── tests/
|
||||
└── dist/ # 编译产物(不提交)
|
||||
```
|
||||
|
||||
### 工具定义规范
|
||||
|
||||
每个 MCP tool 一个文件:
|
||||
|
||||
```typescript
|
||||
// tools/create-task.ts
|
||||
export const createTaskTool: Tool = {
|
||||
name: 'create_task',
|
||||
description: '...',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { ... },
|
||||
required: [...]
|
||||
}
|
||||
};
|
||||
|
||||
export async function handleCreateTask(args) { ... }
|
||||
```
|
||||
|
||||
### 后端 API 调用
|
||||
|
||||
- 所有 REST 请求通过 `src/client/api.ts` 统一封装
|
||||
- 认证头由 client 自动附加(不在 tool 里处理)
|
||||
- 错误统一转成 MCP error response
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| MCP tool name | snake_case | `create_task`, `list_requirements` |
|
||||
| 文件名 | kebab-case | `create-task.ts` |
|
||||
| 函数名 | camelCase | `handleCreateTask` |
|
||||
| Tool 变量 | camelCase + `Tool` | `createTaskTool` |
|
||||
|
||||
### 构建与部署
|
||||
|
||||
- `npm run build` → `dist/`
|
||||
- **修改代码后必须重新 build**:`pkill -f mcp-task-bridge/dist/index.js` 重启 MCP server
|
||||
- 不能直接运行 TypeScript 源码
|
||||
|
||||
### 环境配置
|
||||
|
||||
- `dev` 环境:`ai-proj-dev` MCP server
|
||||
- `prod` 环境:`ai-proj-prod` MCP server
|
||||
- 禁止跨环境传数据(dev 需求不能关联 prod 任务)
|
||||
|
||||
### 测试
|
||||
|
||||
- Jest + ts-jest
|
||||
- 集成测试模拟真实 MCP 协议
|
||||
|
||||
### 常见错误
|
||||
|
||||
- **Rule 1**: MCP 端点必须 `/api/v1/mcp/` 前缀
|
||||
- **Rule 2**: 修改后必须 rebuild + 重启
|
||||
- **Rule 3**: 环境隔离(dev / prod)
|
||||
@@ -0,0 +1,78 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 目录结构(React + TypeScript + Ant Design)
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── pages/ # 页面级组件(路由对应)
|
||||
├── components/ # 可复用 UI 组件
|
||||
├── services/ # API 客户端(Axios 封装)
|
||||
├── hooks/ # 自定义 React Hooks
|
||||
├── contexts/ # Context Providers(auth, timer 等)
|
||||
├── utils/ # 工具函数(auth, validation, date 等)
|
||||
├── types/ # TypeScript 类型定义
|
||||
└── config/ # Feature flags, 性能配置
|
||||
```
|
||||
|
||||
### 状态管理
|
||||
|
||||
| 状态类型 | 方案 |
|
||||
|---------|------|
|
||||
| 服务器状态 | React Query (TanStack Query) |
|
||||
| 全局状态 | Context API |
|
||||
| 本地状态 | useState / useReducer |
|
||||
| 表单状态 | Ant Design Form |
|
||||
|
||||
**禁止**:Redux / MobX(本项目不使用)
|
||||
|
||||
### 路由
|
||||
|
||||
- React Router v6
|
||||
- 路由定义集中在 `src/routes/`
|
||||
- 懒加载:`const Page = lazy(() => import(...))`
|
||||
|
||||
### API 调用
|
||||
|
||||
- 使用 `services/` 下的封装函数,不要在组件里直接 `axios.get`
|
||||
- 响应类型必须有 TypeScript interface
|
||||
- 错误统一由 axios 拦截器处理
|
||||
|
||||
### 样式
|
||||
|
||||
- Ant Design 组件 + CSS Module
|
||||
- 禁止内联 `style={{ ... }}` 用于复杂样式
|
||||
- 全局变量走 CSS Variables
|
||||
|
||||
### Modal 安全规则(重要)
|
||||
|
||||
`Modal.success/info/warning/error` 是非阻塞调用,后续 UI 操作必须放在 `onOk` 回调中:
|
||||
|
||||
```tsx
|
||||
// WRONG
|
||||
Modal.success({ title: '成功' });
|
||||
setNextModalOpen(true); // 立即执行,两个 modal 冲突
|
||||
|
||||
// CORRECT
|
||||
Modal.success({
|
||||
title: '成功',
|
||||
onOk: () => setNextModalOpen(true),
|
||||
});
|
||||
```
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| 组件文件 | PascalCase | `UserProfile.tsx` |
|
||||
| Hook 文件 | camelCase | `useAuth.ts` |
|
||||
| 工具文件 | kebab-case | `date-utils.ts` |
|
||||
| 组件名 | PascalCase | `UserProfile` |
|
||||
| Hook 名 | `use` 前缀 | `useAuth` |
|
||||
|
||||
### 测试
|
||||
|
||||
- 单测:Jest + React Testing Library
|
||||
- E2E:Playwright
|
||||
- 测试文件:`xxx.test.tsx` 与源文件同目录
|
||||
@@ -0,0 +1,67 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 目录结构(Vue 3 + TypeScript + Element Plus)
|
||||
|
||||
```
|
||||
src/
|
||||
├── views/ # 页面级组件(路由对应)
|
||||
├── components/ # 可复用组件
|
||||
├── api/ # API 封装
|
||||
├── stores/ # Pinia stores
|
||||
├── composables/ # 组合式函数(use* hooks)
|
||||
├── utils/ # 工具函数
|
||||
├── types/ # TypeScript 类型定义
|
||||
└── router/ # Vue Router 配置
|
||||
```
|
||||
|
||||
### 状态管理
|
||||
|
||||
- **Pinia**(官方推荐)
|
||||
- 每个业务模块一个 store:`stores/user.ts`、`stores/order.ts`
|
||||
- 禁止直接在组件里写持久状态
|
||||
|
||||
### 路由
|
||||
|
||||
- Vue Router 4
|
||||
- 路由守卫统一在 `router/guards.ts`
|
||||
- 懒加载:`component: () => import('@/views/...')`
|
||||
|
||||
### Composition API
|
||||
|
||||
- **强制使用 `<script setup>`**,禁止 Options API
|
||||
- Props 用 `defineProps<T>()`,Emits 用 `defineEmits<T>()`
|
||||
|
||||
### API 调用
|
||||
|
||||
- `api/` 下按模块划分:`api/user.ts`、`api/order.ts`
|
||||
- 每个函数返回类型明确
|
||||
- 错误由 axios 拦截器统一处理
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| 组件文件 | PascalCase | `UserProfile.vue` |
|
||||
| Composable | camelCase + use 前缀 | `useAuth.ts` |
|
||||
| Store | camelCase | `useUserStore` |
|
||||
| API 文件 | kebab-case | `user-api.ts` |
|
||||
| 工具函数 | camelCase | `formatDate` |
|
||||
|
||||
### 样式
|
||||
|
||||
- SCSS + Element Plus 主题
|
||||
- scoped style(避免全局污染)
|
||||
- 全局变量走 SCSS 变量或 CSS Variables
|
||||
|
||||
### 国际化
|
||||
|
||||
- 使用 vue-i18n
|
||||
- 消息文件:`src/locales/zh.json` / `en.json`
|
||||
- 禁止硬编码文本:用 `t('path.to.key')`
|
||||
|
||||
### 测试
|
||||
|
||||
- 单测:Vitest + Vue Test Utils
|
||||
- E2E:Playwright / Cypress
|
||||
8
skills-dev/dev-commit-plugin/.claude-plugin/plugin.json
Normal file
8
skills-dev/dev-commit-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "dev-commit-plugin",
|
||||
"description": "智能 /commit 命令:分支保护 + 自动建功能分支 + Conventional Commits 生成",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
144
skills-dev/dev-commit-plugin/skills/SKILL.md
Normal file
144
skills-dev/dev-commit-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
name: dev-commit
|
||||
description: 智能 git commit — 分支保护 + 自动建功能分支 + Conventional Commits 生成。当用户说"提交代码"、"commit"、"/commit"、"保存修改"时自动激活。
|
||||
---
|
||||
|
||||
# dev-commit Skill — 智能提交
|
||||
|
||||
借鉴自 devflow-claude `/req:commit`。源自 REQ-20260416-0017 P0-6。
|
||||
|
||||
## 核心功能
|
||||
|
||||
**用户说"/commit" 或 "提交代码" 时执行:**
|
||||
|
||||
1. **分支合规检查**
|
||||
2. **自动建功能分支**(若在保护分支)
|
||||
3. **Conventional Commits 生成**
|
||||
4. **自动关联 REQ-XXX**
|
||||
|
||||
## 流程详解
|
||||
|
||||
### 1. 分支合规检查(强制)
|
||||
|
||||
```bash
|
||||
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)
|
||||
|
||||
# 保护分支名单
|
||||
PROTECTED_BRANCHES="main master develop production"
|
||||
|
||||
for b in $PROTECTED_BRANCHES; do
|
||||
if [ "$CURRENT_BRANCH" = "$b" ]; then
|
||||
IS_PROTECTED=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
**铁律**:**绝对禁止**在 main / develop / master / production 分支上直接 commit。
|
||||
|
||||
### 2. 保护分支上有改动 → 自动建功能分支
|
||||
|
||||
```
|
||||
检测到 main/develop 有未提交改动:
|
||||
|
||||
推荐方案:
|
||||
1. 自动建分支 feat/xxx 并切换过去(推荐)
|
||||
2. 取消本次 commit,手动切分支
|
||||
3. 临时 stash 后切分支
|
||||
|
||||
【默认选 1】
|
||||
```
|
||||
|
||||
**分支命名自动推断:**
|
||||
- 从对话上下文:如果刚在讨论 "REQ-20260416-0017" → `feat/REQ-20260416-0017-summary`
|
||||
- 从文件变更:扫描 staged files 的路径 → 推断模块(如 `backend/services/user/` → `feat/user-xxx`)
|
||||
- 从 commit message 意图:如果意图是 fix → `fix/xxx`
|
||||
|
||||
**前缀规则:**
|
||||
- `feat/` — 新功能
|
||||
- `fix/` — bug 修复
|
||||
- `chore/` — 杂项(依赖升级、CI 调整等)
|
||||
- `refactor/` — 重构
|
||||
- `docs/` — 文档
|
||||
|
||||
### 3. Conventional Commits 生成
|
||||
|
||||
**格式:**
|
||||
```
|
||||
<type>(<scope>): <description> [(REQ-XXX)] [closes #N]
|
||||
```
|
||||
|
||||
**type:**
|
||||
- feat - 新功能
|
||||
- fix - 修复
|
||||
- chore - 杂项
|
||||
- refactor - 重构
|
||||
- docs - 文档
|
||||
- test - 测试
|
||||
- perf - 性能
|
||||
- style - 格式
|
||||
|
||||
**scope:**
|
||||
- 从修改的文件路径推断:`backend/services/user/` → `user`
|
||||
- `frontend/src/pages/login/` → `login`
|
||||
|
||||
**示例:**
|
||||
```
|
||||
feat(mcp): 新增 set_requirement_reviewers 工具 (REQ-20260415-0023)
|
||||
fix(frontend): 403 权限重载死循环
|
||||
chore(cicd): 精简 CI/CD 流程,移除 Staging 环境 (REQ-20260415-0004) closes #242
|
||||
```
|
||||
|
||||
### 4. REQ-XXX 自动关联
|
||||
|
||||
**查找顺序:**
|
||||
1. 当前分支名:`feat/REQ-20260416-0017-xxx` → 提取 `REQ-20260416-0017`
|
||||
2. 最近对话中提到的 REQ ID
|
||||
3. MCP 查询当前用户的"进行中"需求(如只有一个,直接用)
|
||||
|
||||
**分支名含 `-iN` 时追加 `closes #N`:**
|
||||
```
|
||||
feat/REQ-xxx-i42 + commit → commit message 自动加 "closes #42"
|
||||
```
|
||||
|
||||
### 5. 提交确认
|
||||
|
||||
提交前展示预览:
|
||||
```
|
||||
即将执行:
|
||||
分支: feat/mcp-set-reviewers
|
||||
Files: backend/mcp/tools/set_reviewers.go, mcp-task-bridge/src/tools/set-reviewers.ts
|
||||
Message:
|
||||
feat(mcp): 新增 set_requirement_reviewers 工具 (REQ-20260415-0023)
|
||||
|
||||
确认提交?(y/n)
|
||||
```
|
||||
|
||||
## 与 ai-proj 集成
|
||||
|
||||
- **查询当前需求**:通过 MCP `mcp__ai-proj__find_requirement` 或 `list_requirements` 找 user 进行中的
|
||||
- **commit 后可选**:调用 `mcp__ai-proj__update_task` 更新关联任务进度
|
||||
|
||||
## 排除项
|
||||
|
||||
本 skill **不做**:
|
||||
- 推送远程(留给 `/pr` 或 `git push`)
|
||||
- 创建 PR(留给 pull-request skill)
|
||||
- 代码评审(留给 dev-review skill)
|
||||
|
||||
职责边界清晰,防止单命令膨胀。
|
||||
|
||||
## 风险控制
|
||||
|
||||
1. **保护分支改动不得 commit** — 强制拦截
|
||||
2. **message 必须用 Conventional Commits** — 后续 changelog 依赖
|
||||
3. **REQ-XXX 关联尽量自动推断** — 但推断不出不阻断
|
||||
|
||||
## 安装方式
|
||||
|
||||
本 skill 自动随 ai-proj-helper marketplace 加载。用户说"/commit" 即激活。
|
||||
|
||||
## 参考
|
||||
|
||||
- devflow-claude: `plugins/req/commands/commit.md`
|
||||
- REQ-20260416-0017 P0-6
|
||||
@@ -447,9 +447,30 @@ PRD 说「做什么」(用户故事、业务规则、验收标准),设计
|
||||
|
||||
---
|
||||
|
||||
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||
|
||||
**规则:本 skill 产出的设计文档禁止受 auto-memory 影响结构和字段定义。**
|
||||
|
||||
### 禁止行为
|
||||
1. 不得用 memory 里的历史 API 契约填充当前设计(避免张冠李戴)
|
||||
2. 不得根据 memory 偏好省略"变更文件清单"/"数据模型变更"等章节
|
||||
3. 不得读取 `~/.claude/projects/*/memory/` 生成 API 字段或 SQL
|
||||
|
||||
### 允许行为
|
||||
- memory 可影响交互风格、命令推荐
|
||||
- memory 可记住"用户偏好 RESTful 而非 RPC" 这类**偏好**,但字段定义必须基于当前需求
|
||||
|
||||
### Why
|
||||
设计文档是开发契约,字段/接口/表结构错一个字就是 bug。memory 污染会让 AI 脑补出"看起来像但不对"的字段。必须严格基于当前 PRD + 代码现状生成。
|
||||
|
||||
**参考**:devflow-claude `plugins/req/commands/_common.md` 同名规则。
|
||||
|
||||
---
|
||||
|
||||
## 变更记录
|
||||
|
||||
| 版本 | 日期 | 变更内容 |
|
||||
|------|------|----------|
|
||||
| V1.0 | 2026-01-26 | 初始版本(原名 req-dev) |
|
||||
| V2.0 | 2026-04-06 | 重构为 req-design:移除架构指南和编码规范,聚焦 API 契约 + 任务拆分 |
|
||||
| V2.1 | 2026-04-16 | 新增 Memory 隔离规则(REQ-20260416-0017) |
|
||||
|
||||
@@ -411,3 +411,34 @@ mcp__ai-proj__export_task_document_to_file
|
||||
- 数据安全分级
|
||||
- 敏感操作审计
|
||||
- 权限最小化原则
|
||||
|
||||
---
|
||||
|
||||
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||
|
||||
**规则:本 skill 涉及模板/文档产出的命令禁止受 auto-memory 影响产出物。**
|
||||
|
||||
### 禁止行为
|
||||
1. 不得根据 memory 中的偏好跳过或合并 PRD 模板章节
|
||||
2. 不得用 memory 里的历史需求/项目内容填充当前 PRD
|
||||
3. 不得根据 memory 反馈调整 PRD 章节顺序、表格列数、标题层级
|
||||
4. 不得读取 `~/.claude/projects/*/memory/` 辅助生成 PRD 正文
|
||||
|
||||
### 允许行为
|
||||
- memory 可影响**交互风格**(提问详略、确认节奏、语气)
|
||||
- memory 可指导**命令选择**(如根据用户习惯推荐先走 req-compare 还是 req-prd)
|
||||
- memory 可影响**非产出文本**(如对话中的说明)
|
||||
|
||||
### Why
|
||||
auto-memory 设计用于跨会话建立用户画像。但 PRD/需求文档是正式产出物,必须由**模板结构 + 当前输入**决定,不能因 memory 中的偏好自作主张调整结构,否则会导致:
|
||||
- 模板章节漂移(用户不知道为什么这次少了一章)
|
||||
- 历史项目内容污染(张冠李戴)
|
||||
- 产出不可复现
|
||||
|
||||
### How to apply
|
||||
执行 `/req prd` / PRD 编写 / 需求描述生成等命令时:
|
||||
- 仅读取:模板文件、用户当前输入、引用的已有需求文档
|
||||
- 不读取:memory 目录下的任何文件
|
||||
- 产出前自检:章节数量和顺序与模板完全一致
|
||||
|
||||
**参考**:devflow-claude 的 `plugins/req/commands/_common.md` 同名规则。
|
||||
|
||||
@@ -184,3 +184,25 @@ mcp__ai-proj__batch_sync_tasks_to_remote(taskIds)
|
||||
发送邮件:`/req notify [REQ-ID] --type <prd|dev|test|deploy|archive>`
|
||||
|
||||
默认收件人:项目邮件组(见项目配置)
|
||||
|
||||
---
|
||||
|
||||
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||
|
||||
**规则:涉及生命周期文档产出(PRD / 设计 / 测试报告 / 部署报告 / 生命周期总结)时,禁止受 auto-memory 影响产出物结构。**
|
||||
|
||||
### 禁止行为
|
||||
1. 不得跳过或合并 5 阶段文档的任一阶段(01-PRD ~ 05-生命周期总结)
|
||||
2. 不得因 memory 里的历史项目跳过章节
|
||||
3. 不得因 memory 中的"精简偏好"缩减文档结构
|
||||
4. 不得读取 `~/.claude/projects/*/memory/` 生成这些文档的正文
|
||||
|
||||
### 允许行为
|
||||
- memory 可影响**交互风格**(如提问详略)
|
||||
- memory 可影响**通知习惯**(如默认 CC 某人)
|
||||
- memory 可影响**触发时机**(如归档时是否自动同步思源)
|
||||
|
||||
### Why
|
||||
需求生命周期文档对应审计和交付物。结构变化会破坏检索、对标、复盘。memory 应调节"怎么聊",不应调节"最终文档长什么样"。
|
||||
|
||||
**参考**:devflow-claude `plugins/req/commands/_common.md` 同名规则,以及本项目 REQ-20260416-0017。
|
||||
|
||||
Reference in New Issue
Block a user