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 机制落地
131 lines
4.1 KiB
Bash
Executable File
131 lines
4.1 KiB
Bash
Executable File
#!/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\` 智能提交"
|