Compare commits
10 Commits
feat/devfl
...
5a45916b2c
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a45916b2c | |||
| bcea648e3c | |||
| e3513f137b | |||
| 011916ceb9 | |||
|
|
48b792fb5a | ||
| 2ab0a61eb9 | |||
| de25f096e7 | |||
| 84d4e35a42 | |||
| e5805cbb51 | |||
| 79c4e55719 |
@@ -23,6 +23,30 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "pm-ask-plugin",
|
||||||
|
"source": "./skills-core/pm-ask-plugin",
|
||||||
|
"description": "基于真实数据的项目问答 /ask。必须引用 MCP/git 真实数据,禁止编造",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "utility",
|
||||||
|
"keywords": [
|
||||||
|
"utility",
|
||||||
|
"tools"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pm-risk-plugin",
|
||||||
|
"source": "./skills-core/pm-risk-plugin",
|
||||||
|
"description": "三维度项目风险扫描:需求层/代码层/流程层。当用户说'/risk'、'风险扫描'、'有什么风险'时自动激活",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "utility",
|
||||||
|
"keywords": [
|
||||||
|
"utility",
|
||||||
|
"tools"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "publish-plugin",
|
"name": "publish-plugin",
|
||||||
"source": "./skills-core/publish-plugin",
|
"source": "./skills-core/publish-plugin",
|
||||||
@@ -47,6 +71,30 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "agent-swarm-plugin",
|
||||||
|
"source": "./skills-dev/agent-swarm-plugin",
|
||||||
|
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "utility",
|
||||||
|
"keywords": [
|
||||||
|
"utility",
|
||||||
|
"tools"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai-chat-plugin",
|
||||||
|
"source": "./skills-dev/ai-chat-plugin",
|
||||||
|
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "utility",
|
||||||
|
"keywords": [
|
||||||
|
"utility",
|
||||||
|
"tools"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "db-migration-plugin",
|
"name": "db-migration-plugin",
|
||||||
"source": "./skills-dev/db-migration-plugin",
|
"source": "./skills-dev/db-migration-plugin",
|
||||||
@@ -59,6 +107,18 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "defect-analysis-plugin",
|
||||||
|
"source": "./skills-dev/defect-analysis-plugin",
|
||||||
|
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "utility",
|
||||||
|
"keywords": [
|
||||||
|
"utility",
|
||||||
|
"tools"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "deploy-rollback-plugin",
|
"name": "deploy-rollback-plugin",
|
||||||
"source": "./skills-dev/deploy-rollback-plugin",
|
"source": "./skills-dev/deploy-rollback-plugin",
|
||||||
@@ -241,6 +301,43 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "executing-plans-plugin",
|
||||||
|
"source": "./skills-dev/executing-plans-plugin",
|
||||||
|
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "utility",
|
||||||
|
"keywords": [
|
||||||
|
"utility",
|
||||||
|
"tools"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "finishing-branch-plugin",
|
||||||
|
"source": "./skills-dev/finishing-branch-plugin",
|
||||||
|
"description": "Use when implementation is complete and all tests pass - verifies and creates PR.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "utility",
|
||||||
|
"keywords": [
|
||||||
|
"utility",
|
||||||
|
"tools"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "frontend-design-plugin",
|
||||||
|
"source": "./skills-dev/frontend-design-plugin",
|
||||||
|
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "development",
|
||||||
|
"keywords": [
|
||||||
|
"development",
|
||||||
|
"coding",
|
||||||
|
"workflow"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pull-request-plugin",
|
"name": "pull-request-plugin",
|
||||||
"source": "./skills-dev/pull-request-plugin",
|
"source": "./skills-dev/pull-request-plugin",
|
||||||
@@ -266,6 +363,19 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "req-audit-plugin",
|
||||||
|
"source": "./skills-req/req-audit-plugin",
|
||||||
|
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "productivity",
|
||||||
|
"keywords": [
|
||||||
|
"project-management",
|
||||||
|
"tasks",
|
||||||
|
"requirements"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "req-compare-plugin",
|
"name": "req-compare-plugin",
|
||||||
"source": "./skills-req/req-compare-plugin",
|
"source": "./skills-req/req-compare-plugin",
|
||||||
@@ -305,6 +415,19 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "req-lookback-plugin",
|
||||||
|
"source": "./skills-req/req-lookback-plugin",
|
||||||
|
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "productivity",
|
||||||
|
"keywords": [
|
||||||
|
"project-management",
|
||||||
|
"tasks",
|
||||||
|
"requirements"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "req-plugin",
|
"name": "req-plugin",
|
||||||
"source": "./skills-req/req-plugin",
|
"source": "./skills-req/req-plugin",
|
||||||
@@ -334,8 +457,8 @@
|
|||||||
{
|
{
|
||||||
"name": "req-prototype-plugin",
|
"name": "req-prototype-plugin",
|
||||||
"source": "./skills-req/req-prototype-plugin",
|
"source": "./skills-req/req-prototype-plugin",
|
||||||
"description": "Stitch 原型生成与迭代。基于 PRD 文档自动生成 UI 原型。",
|
"description": "原型生成与关联。支持 HTML 上传(/req prototype upload,iframe 嵌入详情页)和 Stitch AI 生成两种模式。",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"category": "productivity",
|
"category": "productivity",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"project-management",
|
"project-management",
|
||||||
@@ -357,6 +480,19 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "req-retro-plugin",
|
||||||
|
"source": "./skills-req/req-retro-plugin",
|
||||||
|
"description": "复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "productivity",
|
||||||
|
"keywords": [
|
||||||
|
"project-management",
|
||||||
|
"tasks",
|
||||||
|
"requirements"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "req-review-plugin",
|
"name": "req-review-plugin",
|
||||||
"source": "./skills-req/req-review-plugin",
|
"source": "./skills-req/req-review-plugin",
|
||||||
@@ -573,6 +709,19 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ops-servers-plugin",
|
||||||
|
"source": "./skills-personal/ops-servers-plugin",
|
||||||
|
"description": "企业服务器管理。用于云服务器分组管理、系统监控、备份管理、故障排查。当用户提到云服务器、生产环境、腾讯云、阿里云相关任务时自动激活。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "devops",
|
||||||
|
"keywords": [
|
||||||
|
"devops",
|
||||||
|
"deployment",
|
||||||
|
"operations"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "ops-tools-plugin",
|
"name": "ops-tools-plugin",
|
||||||
"source": "./skills-personal/ops-tools-plugin",
|
"source": "./skills-personal/ops-tools-plugin",
|
||||||
@@ -598,6 +747,19 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "reload-session-plugin",
|
||||||
|
"source": "./skills-personal/reload-session-plugin",
|
||||||
|
"description": "Reload a previously saved Claude session to continue the conversation.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "workflow",
|
||||||
|
"keywords": [
|
||||||
|
"session",
|
||||||
|
"workflow",
|
||||||
|
"productivity"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "req-deploy-plugin",
|
"name": "req-deploy-plugin",
|
||||||
"source": "./skills-personal/req-deploy-plugin",
|
"source": "./skills-personal/req-deploy-plugin",
|
||||||
@@ -610,6 +772,32 @@
|
|||||||
"operations"
|
"operations"
|
||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "save-session-plugin",
|
||||||
|
"source": "./skills-personal/save-session-plugin",
|
||||||
|
"description": "Auto-save Claude session conversation with AI-generated title, summary, and tags in searchable JSON format.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "workflow",
|
||||||
|
"keywords": [
|
||||||
|
"session",
|
||||||
|
"workflow",
|
||||||
|
"productivity"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "search-sessions-plugin",
|
||||||
|
"source": "./skills-personal/search-sessions-plugin",
|
||||||
|
"description": "Search saved Claude sessions by title, tags, date, or content.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "workflow",
|
||||||
|
"keywords": [
|
||||||
|
"session",
|
||||||
|
"workflow",
|
||||||
|
"productivity"
|
||||||
|
],
|
||||||
|
"strict": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
98
hooks/validate-prd.sh
Executable file
98
hooks/validate-prd.sh
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# validate-prd.sh
|
||||||
|
# PostToolUse Hook: PRD 文档写入/编辑后自动校验章节完整性
|
||||||
|
#
|
||||||
|
# 检查标准 PRD 模板的必需章节是否存在。
|
||||||
|
# 借鉴 devflow-claude validate-requirement.sh。
|
||||||
|
# REQ-20260416-0017 P1-14
|
||||||
|
#
|
||||||
|
# 安装方式(可选,加到 ~/.claude/settings.json):
|
||||||
|
# hooks.PostToolUse:
|
||||||
|
# - matcher: "Write|Edit"
|
||||||
|
# hooks:
|
||||||
|
# - type: command
|
||||||
|
# command: "<path>/hooks/validate-prd.sh"
|
||||||
|
# timeout: 5
|
||||||
|
#
|
||||||
|
# 也可由 skill 脚本在 PRD 写完后手动调用:
|
||||||
|
# bash validate-prd.sh /path/to/PRD.md
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ============ 1. 获取文件路径 ============
|
||||||
|
FILE_PATH="$1"
|
||||||
|
|
||||||
|
# 如果没有参数,从 stdin JSON 提取(PostToolUse hook 模式)
|
||||||
|
if [ -z "$FILE_PATH" ] && command -v jq >/dev/null 2>&1; then
|
||||||
|
INPUT=$(cat 2>/dev/null || echo "")
|
||||||
|
if [ -n "$INPUT" ]; then
|
||||||
|
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.content // empty' 2>/dev/null)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 没有文件路径,静默退出
|
||||||
|
if [ -z "$FILE_PATH" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 不是 .md 文件,跳过
|
||||||
|
case "$FILE_PATH" in
|
||||||
|
*.md) ;;
|
||||||
|
*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# 文件不存在,跳过
|
||||||
|
if [ ! -f "$FILE_PATH" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 不含 PRD / 需求 关键词的文件名,跳过
|
||||||
|
BASENAME=$(basename "$FILE_PATH")
|
||||||
|
case "$BASENAME" in
|
||||||
|
*PRD*|*prd*|*需求*|*requirement*|*REQ*) ;;
|
||||||
|
*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ============ 2. 检查章节 ============
|
||||||
|
|
||||||
|
# 标准 PRD 必需章节(来自 req-prd SKILL.md 的模板)
|
||||||
|
REQUIRED_SECTIONS=(
|
||||||
|
"## 1. 概述"
|
||||||
|
"### 1.1 背景"
|
||||||
|
"### 1.2 目标"
|
||||||
|
"### 1.4 客户原始诉求"
|
||||||
|
"## 2. 用户分析"
|
||||||
|
"## 3. 功能需求"
|
||||||
|
"## 4. 交互设计"
|
||||||
|
"## 5. 技术要求"
|
||||||
|
"## 6. 上线计划"
|
||||||
|
"## 7. 风险评估"
|
||||||
|
)
|
||||||
|
|
||||||
|
MISSING=()
|
||||||
|
CONTENT=$(cat "$FILE_PATH")
|
||||||
|
|
||||||
|
for section in "${REQUIRED_SECTIONS[@]}"; do
|
||||||
|
# 模糊匹配:忽略空格差异和标点
|
||||||
|
PATTERN=$(echo "$section" | sed 's/[[:space:]]*//g')
|
||||||
|
CONTENT_CLEAN=$(echo "$CONTENT" | sed 's/[[:space:]]*//g')
|
||||||
|
if ! echo "$CONTENT_CLEAN" | grep -qi "$(echo "$PATTERN" | sed 's/#//g')"; then
|
||||||
|
MISSING+=("$section")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ============ 3. 输出 ============
|
||||||
|
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ PRD 章节检查:${BASENAME}"
|
||||||
|
echo ""
|
||||||
|
echo "缺少 ${#MISSING[@]} 个必需章节:"
|
||||||
|
for m in "${MISSING[@]}"; do
|
||||||
|
echo " ❌ $m"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "💡 请参考 req-prd skill 的 PRD 模板补充缺失章节。"
|
||||||
|
echo " 章节结构不可变:不得新增、删除、合并或重命名模板中的章节。"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
338
install-skills.sh
Executable file
338
install-skills.sh
Executable file
@@ -0,0 +1,338 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# install-skills.sh — Cross-machine Claude skill sync from ai-proj-helper
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./install-skills.sh [options]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --dry-run Preview changes without writing anything
|
||||||
|
# --category <cat> Only install plugins in dir_category=<cat>
|
||||||
|
# Valid values: biz, core, dev, integration, personal, req
|
||||||
|
# --force Overwrite even if local files were modified
|
||||||
|
# --cleanup Remove locally installed skills that are no longer in repo
|
||||||
|
# --list List all available plugins without installing
|
||||||
|
# --help Show this help
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
SKILLS_DIR="${HOME}/.claude/skills"
|
||||||
|
COMMANDS_DIR="${HOME}/.claude/commands"
|
||||||
|
STATE_FILE="${HOME}/.claude/.installed-skills.json"
|
||||||
|
|
||||||
|
DRY_RUN=false
|
||||||
|
CATEGORY_FILTER=""
|
||||||
|
FORCE=false
|
||||||
|
CLEANUP=false
|
||||||
|
LIST_ONLY=false
|
||||||
|
|
||||||
|
# ── Colour helpers ─────────────────────────────────────────────────────────────
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
RESET='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${BLUE}[info]${RESET} $*"; }
|
||||||
|
ok() { echo -e "${GREEN}[ok]${RESET} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[warn]${RESET} $*"; }
|
||||||
|
error() { echo -e "${RED}[error]${RESET} $*" >&2; }
|
||||||
|
dry() { echo -e "${YELLOW}[dry]${RESET} $*"; }
|
||||||
|
|
||||||
|
# ── Argument parsing ───────────────────────────────────────────────────────────
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--dry-run) DRY_RUN=true ;;
|
||||||
|
--force) FORCE=true ;;
|
||||||
|
--cleanup) CLEANUP=true ;;
|
||||||
|
--list) LIST_ONLY=true ;;
|
||||||
|
--category) CATEGORY_FILTER="$2"; shift ;;
|
||||||
|
--help|-h)
|
||||||
|
grep '^#' "$0" | grep -v '!/usr' | sed 's/^# \?//'
|
||||||
|
exit 0 ;;
|
||||||
|
*)
|
||||||
|
error "Unknown argument: $1"
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── State helpers (plain JSON via python3) ─────────────────────────────────────
|
||||||
|
state_get() {
|
||||||
|
# state_get <install_name> -> prints version or empty string
|
||||||
|
local name="$1"
|
||||||
|
if [[ -f "$STATE_FILE" ]]; then
|
||||||
|
python3 -c "
|
||||||
|
import json,sys
|
||||||
|
try:
|
||||||
|
d=json.load(open('$STATE_FILE'))
|
||||||
|
print(d.get('$name',{}).get('version',''))
|
||||||
|
except: pass
|
||||||
|
" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
state_set() {
|
||||||
|
# state_set <install_name> <version> <install_type>
|
||||||
|
local name="$1" ver="$2" itype="$3"
|
||||||
|
python3 -c "
|
||||||
|
import json,os
|
||||||
|
f='$STATE_FILE'
|
||||||
|
d=json.load(open(f)) if os.path.exists(f) else {}
|
||||||
|
d['$name']={'version':'$ver','install_type':'$itype'}
|
||||||
|
json.dump(d,open(f,'w'),indent=2)
|
||||||
|
" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
state_remove() {
|
||||||
|
local name="$1"
|
||||||
|
python3 -c "
|
||||||
|
import json,os
|
||||||
|
f='$STATE_FILE'
|
||||||
|
if not os.path.exists(f): exit()
|
||||||
|
d=json.load(open(f))
|
||||||
|
d.pop('$name',None)
|
||||||
|
json.dump(d,open(f,'w'),indent=2)
|
||||||
|
" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
state_all_names() {
|
||||||
|
if [[ -f "$STATE_FILE" ]]; then
|
||||||
|
python3 -c "
|
||||||
|
import json
|
||||||
|
d=json.load(open('$STATE_FILE'))
|
||||||
|
for k in d: print(k)
|
||||||
|
" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Plugin discovery ───────────────────────────────────────────────────────────
|
||||||
|
find_plugins() {
|
||||||
|
find "$REPO_DIR" -path "*/skills-*/*-plugin/.claude-plugin/plugin.json" | sort
|
||||||
|
}
|
||||||
|
|
||||||
|
read_field() {
|
||||||
|
# read_field <json_file> <field>
|
||||||
|
python3 -c "import json,sys; d=json.load(open('$1')); print(d.get('$2',''))" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve the actual source directory to rsync from.
|
||||||
|
# If skills/ has SKILL.md at the top level, use it directly.
|
||||||
|
# If skills/ has a single subdirectory (e.g. skills/dev-test/SKILL.md), use that subdirectory.
|
||||||
|
resolve_skills_src() {
|
||||||
|
local skills_dir="$1"
|
||||||
|
if [[ -f "$skills_dir/SKILL.md" ]]; then
|
||||||
|
echo "$skills_dir"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
# Find the first subdirectory that contains SKILL.md
|
||||||
|
local sub
|
||||||
|
sub="$(find "$skills_dir" -maxdepth 2 -name 'SKILL.md' | head -1)"
|
||||||
|
if [[ -n "$sub" ]]; then
|
||||||
|
echo "$(dirname "$sub")"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "$skills_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Conflict detection (has local been modified since we installed it?) ────────
|
||||||
|
has_local_modification() {
|
||||||
|
# Returns 0 (true) if local target differs from repo source, 1 if identical or new
|
||||||
|
local install_name="$1" install_type="$2" plugin_skills_dir="$3"
|
||||||
|
|
||||||
|
if [[ "$install_type" == "command" ]]; then
|
||||||
|
local src="$plugin_skills_dir/SKILL.md"
|
||||||
|
local dst="$COMMANDS_DIR/${install_name}.md"
|
||||||
|
[[ -f "$dst" ]] && ! diff -q "$src" "$dst" &>/dev/null && return 0
|
||||||
|
else
|
||||||
|
local dst_dir="$SKILLS_DIR/$install_name"
|
||||||
|
if [[ -d "$dst_dir" ]]; then
|
||||||
|
# Compare each file from source
|
||||||
|
while IFS= read -r -d '' src_file; do
|
||||||
|
local rel="${src_file#$plugin_skills_dir/}"
|
||||||
|
local dst_file="$dst_dir/$rel"
|
||||||
|
if [[ -f "$dst_file" ]] && ! diff -q "$src_file" "$dst_file" &>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < <(find "$plugin_skills_dir" -type f -print0)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Install a single plugin ────────────────────────────────────────────────────
|
||||||
|
install_plugin() {
|
||||||
|
local json_path="$1"
|
||||||
|
local plugin_dir
|
||||||
|
plugin_dir="$(dirname "$(dirname "$json_path")")" # strip /.claude-plugin/plugin.json
|
||||||
|
local skills_dir="$plugin_dir/skills"
|
||||||
|
|
||||||
|
local install_name install_type dir_category version
|
||||||
|
install_name="$(read_field "$json_path" install_name)"
|
||||||
|
install_type="$(read_field "$json_path" install_type)"
|
||||||
|
dir_category="$(read_field "$json_path" dir_category)"
|
||||||
|
version="$(read_field "$json_path" version)"
|
||||||
|
|
||||||
|
# Skip if no install metadata (legacy plugin without our new fields)
|
||||||
|
if [[ -z "$install_name" || -z "$install_type" ]]; then
|
||||||
|
warn "$(basename "$plugin_dir"): missing install_name/install_type, skipping"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Category filter
|
||||||
|
if [[ -n "$CATEGORY_FILTER" && "$dir_category" != "$CATEGORY_FILTER" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify skills directory exists
|
||||||
|
if [[ ! -d "$skills_dir" ]]; then
|
||||||
|
warn "$install_name: no skills/ directory in plugin, skipping"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$LIST_ONLY" == true ]]; then
|
||||||
|
echo " [$dir_category] $install_type:$install_name v$version"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check current installed version
|
||||||
|
local current_version
|
||||||
|
current_version="$(state_get "$install_name")"
|
||||||
|
|
||||||
|
# Skip if up-to-date (same version) and no force
|
||||||
|
if [[ "$current_version" == "$version" && "$FORCE" == false ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Conflict detection: warn if local files were modified
|
||||||
|
if [[ -n "$current_version" && "$FORCE" == false ]]; then
|
||||||
|
if has_local_modification "$install_name" "$install_type" "$skills_dir"; then
|
||||||
|
warn "$install_name: local files were modified — skipping (use --force to overwrite)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Resolve actual source (handles plugins where content sits one level deeper,
|
||||||
|
# e.g. skills/dev-test/SKILL.md instead of skills/SKILL.md)
|
||||||
|
local src_dir
|
||||||
|
src_dir="$(resolve_skills_src "$skills_dir")"
|
||||||
|
|
||||||
|
# Perform install
|
||||||
|
if [[ "$install_type" == "command" ]]; then
|
||||||
|
# Single-file command → ~/.claude/commands/<name>.md
|
||||||
|
local src_md="$src_dir/SKILL.md"
|
||||||
|
if [[ ! -f "$src_md" ]]; then
|
||||||
|
warn "$install_name: SKILL.md not found, skipping"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == true ]]; then
|
||||||
|
dry "$install_name → $COMMANDS_DIR/${install_name}.md"
|
||||||
|
else
|
||||||
|
mkdir -p "$COMMANDS_DIR"
|
||||||
|
cp "$src_md" "$COMMANDS_DIR/${install_name}.md"
|
||||||
|
state_set "$install_name" "$version" "$install_type"
|
||||||
|
ok "$install_name → command (v$version)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
# Skill directory → ~/.claude/skills/<name>/
|
||||||
|
local dst_dir="$SKILLS_DIR/$install_name"
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == true ]]; then
|
||||||
|
dry "$install_name → $dst_dir/"
|
||||||
|
else
|
||||||
|
mkdir -p "$dst_dir"
|
||||||
|
# rsync resolved source (handles nested skills/ structures)
|
||||||
|
rsync -a --delete "$src_dir/" "$dst_dir/"
|
||||||
|
state_set "$install_name" "$version" "$install_type"
|
||||||
|
ok "$install_name → skill (v$version)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Cleanup removed plugins ────────────────────────────────────────────────────
|
||||||
|
cleanup_removed() {
|
||||||
|
if [[ "$CLEANUP" == false ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Checking for removed plugins to clean up..."
|
||||||
|
|
||||||
|
# Collect all install_names still in repo
|
||||||
|
local repo_names=()
|
||||||
|
while IFS= read -r json_path; do
|
||||||
|
local name
|
||||||
|
name="$(read_field "$json_path" install_name)"
|
||||||
|
[[ -n "$name" ]] && repo_names+=("$name")
|
||||||
|
done < <(find_plugins)
|
||||||
|
|
||||||
|
# Check state file for installed plugins no longer in repo
|
||||||
|
while IFS= read -r installed_name; do
|
||||||
|
local found=false
|
||||||
|
for repo_name in "${repo_names[@]}"; do
|
||||||
|
[[ "$repo_name" == "$installed_name" ]] && found=true && break
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$found" == false ]]; then
|
||||||
|
local itype
|
||||||
|
itype="$(python3 -c "import json; d=json.load(open('$STATE_FILE')); print(d.get('$installed_name',{}).get('install_type',''))" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == true ]]; then
|
||||||
|
dry "Would remove: $installed_name ($itype)"
|
||||||
|
else
|
||||||
|
if [[ "$itype" == "command" ]]; then
|
||||||
|
rm -f "$COMMANDS_DIR/${installed_name}.md"
|
||||||
|
else
|
||||||
|
rm -rf "${SKILLS_DIR:?}/$installed_name"
|
||||||
|
fi
|
||||||
|
state_remove "$installed_name"
|
||||||
|
ok "Removed: $installed_name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < <(state_all_names)
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Main ───────────────────────────────────────────────────────────────────────
|
||||||
|
main() {
|
||||||
|
if [[ "$LIST_ONLY" == true ]]; then
|
||||||
|
info "Available plugins in $REPO_DIR:"
|
||||||
|
local count=0
|
||||||
|
while IFS= read -r json_path; do
|
||||||
|
install_plugin "$json_path"
|
||||||
|
((count++)) || true
|
||||||
|
done < <(find_plugins)
|
||||||
|
echo ""
|
||||||
|
info "Total: $count plugins"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Installing Claude skills from: $REPO_DIR"
|
||||||
|
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be written"
|
||||||
|
[[ -n "$CATEGORY_FILTER" ]] && info "Category filter: $CATEGORY_FILTER"
|
||||||
|
|
||||||
|
local installed=0 skipped=0
|
||||||
|
|
||||||
|
while IFS= read -r json_path; do
|
||||||
|
local before
|
||||||
|
before="$(state_all_names | wc -l || true)"
|
||||||
|
install_plugin "$json_path"
|
||||||
|
local after
|
||||||
|
after="$(state_all_names | wc -l || true)"
|
||||||
|
if [[ "$after" -gt "$before" ]] || [[ "$DRY_RUN" == true ]]; then
|
||||||
|
((installed++)) || true
|
||||||
|
fi
|
||||||
|
done < <(find_plugins)
|
||||||
|
|
||||||
|
cleanup_removed
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [[ "$DRY_RUN" == true ]]; then
|
||||||
|
info "Dry run complete. $installed plugins would be installed/updated."
|
||||||
|
else
|
||||||
|
info "Done. $installed plugins installed/updated."
|
||||||
|
info "State saved to: $STATE_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "biz-contract",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "biz"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "biz-ops",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "biz"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "biz-plan",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "biz"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "finance",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "biz"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "ai-proj",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "core"
|
||||||
}
|
}
|
||||||
|
|||||||
11
skills-core/pm-ask-plugin/.claude-plugin/plugin.json
Normal file
11
skills-core/pm-ask-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "pm-ask-plugin",
|
||||||
|
"description": "基于真实数据的项目问答 /ask。必须引用 MCP/git 真实数据,禁止编造",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "pm-ask",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "core"
|
||||||
|
}
|
||||||
126
skills-core/pm-ask-plugin/skills/SKILL.md
Normal file
126
skills-core/pm-ask-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
---
|
||||||
|
name: pm-ask
|
||||||
|
description: 基于 ai-proj MCP + git log 真实数据的项目问答。禁止编造。当用户说"/ask"、"问一下"、"统计一下"、"最近多少"、"谁做了多少"、"本月进度"等项目相关问题时自动激活。
|
||||||
|
---
|
||||||
|
|
||||||
|
# pm-ask Skill — 基于真实数据的项目问答
|
||||||
|
|
||||||
|
借鉴自 devflow-claude `/pm:ask`。源自 REQ-20260416-0017 P1-9。
|
||||||
|
|
||||||
|
## 核心原则(铁律)
|
||||||
|
|
||||||
|
> **必须基于真实数据,禁止编造。**
|
||||||
|
|
||||||
|
- 所有数字必须来自 MCP API / git log / 文件系统的**真实查询**
|
||||||
|
- 必须引用具体 **REQ-XXX / commit-sha / 文件路径**
|
||||||
|
- 数据不足时**明确说明缺失**,不得"大概"、"估计"
|
||||||
|
- **区分事实和推测**:推测必须用 `⚠️ 推测:` 前缀
|
||||||
|
|
||||||
|
## 适用场景
|
||||||
|
|
||||||
|
| 用户问题示例 | 数据源 | 工具 |
|
||||||
|
|-------------|--------|------|
|
||||||
|
| "qiudl 最近一个月提交了多少代码?" | git log | `git log --author=qiudl --since='30 days ago'` |
|
||||||
|
| "本月完成了哪些需求?" | ai-proj MCP | `list_requirements --status=completed` |
|
||||||
|
| "当前进行中的需求有多少?分别是?" | MCP | `list_requirements --status=in_progress` |
|
||||||
|
| "REQ-xxx 拆了哪些任务?完成多少?" | MCP | `get_requirement_tasks` |
|
||||||
|
| "backend/services/user.go 最近谁在改?" | git log | `git log --follow -- file` |
|
||||||
|
| "和上月对比,产出是多了还是少了?" | git log + MCP | 组合查询 |
|
||||||
|
| "生成一封客户更新邮件" | MCP + git | 聚合后结构化输出 |
|
||||||
|
|
||||||
|
## 工作流
|
||||||
|
|
||||||
|
### 1. 意图分析(确定数据源)
|
||||||
|
|
||||||
|
关键词 → 数据源映射:
|
||||||
|
|
||||||
|
| 关键词 | 数据源 |
|
||||||
|
|--------|--------|
|
||||||
|
| 需求 / REQ / 需求状态 | ai-proj MCP `list_requirements` / `find_requirement` |
|
||||||
|
| 任务 / todo / 待办 | MCP `list_tasks` / `find_task` |
|
||||||
|
| 提交 / commit / 代码量 / 贡献 | `git log` |
|
||||||
|
| 文件修改 / 变更 | `git log --follow` / `git blame` |
|
||||||
|
| 进度 / 完成率 | MCP `get_project_stats` / `get_requirement_statistics` |
|
||||||
|
| 周报 / 月报 | 组合:MCP + git log + 时间范围过滤 |
|
||||||
|
| 风险 / 停滞 / 延期 | MCP + 时间戳分析 |
|
||||||
|
|
||||||
|
### 2. 执行查询
|
||||||
|
|
||||||
|
**查询前必须声明**:
|
||||||
|
```
|
||||||
|
📊 正在查询数据源:
|
||||||
|
- ai-proj MCP: list_requirements (status=in_progress)
|
||||||
|
- git log: --author=qiudl --since='2026-04-01'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 生成答案(强约束)
|
||||||
|
|
||||||
|
**结构**:
|
||||||
|
```
|
||||||
|
## 直接答案
|
||||||
|
<一句话结论,附数字>
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
- <查询语句 1> → <结果摘要>
|
||||||
|
- <查询语句 2> → <结果摘要>
|
||||||
|
|
||||||
|
## 细节
|
||||||
|
<表格 / 列表,每条必有 REQ-XXX 或 commit-sha>
|
||||||
|
|
||||||
|
## ⚠️ 数据缺失(如有)
|
||||||
|
<说明哪些数据无法获取>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 禁止事项
|
||||||
|
|
||||||
|
**不允许的答案:**
|
||||||
|
- "大概 XX 个左右"(必须精确数字或说明"无法精确统计")
|
||||||
|
- "主要在做 XX"(必须列具体 REQ)
|
||||||
|
- "应该是 XX"(推测必须 `⚠️ 推测:` 标记)
|
||||||
|
- "最近进度不错"(必须数据支持)
|
||||||
|
|
||||||
|
**违反时自我纠正**:如果生成答案时发现缺少真实数据引用,重新查询,不要猜测。
|
||||||
|
|
||||||
|
## 输出适配
|
||||||
|
|
||||||
|
根据问题类型自动切换输出格式:
|
||||||
|
|
||||||
|
| 问题类型 | 输出格式 |
|
||||||
|
|---------|---------|
|
||||||
|
| 数量统计 | 数字 + 列表 |
|
||||||
|
| 对比分析 | 表格 |
|
||||||
|
| 进度追踪 | 进度条 / 完成率 |
|
||||||
|
| 历史回顾 | 时间线 |
|
||||||
|
| 生成文档(邮件/报告) | 完整结构化文本 |
|
||||||
|
|
||||||
|
## 与其他 skill 的关系
|
||||||
|
|
||||||
|
| 相关 skill | 分工 |
|
||||||
|
|-----------|------|
|
||||||
|
| `ai-proj` | 执行 MCP 调用(本 skill 的底层) |
|
||||||
|
| `req-workflow` | 需求生命周期管理(本 skill 只读查询) |
|
||||||
|
| `dev-review` | 代码评审(本 skill 提供评审背景数据) |
|
||||||
|
|
||||||
|
## 受众适配(借鉴自 devflow report-generator)
|
||||||
|
|
||||||
|
生成报告时按受众调整:
|
||||||
|
|
||||||
|
| 受众 | 侧重 | 禁用 |
|
||||||
|
|------|------|------|
|
||||||
|
| 高层 | 交付物、里程碑、风险 | 技术细节 |
|
||||||
|
| 客户 | 功能价值、上线时间 | 内部术语 |
|
||||||
|
| 内部团队 | 技术细节、Blocker | - |
|
||||||
|
| 新人 | 背景上下文、术语表 | 假设已知 |
|
||||||
|
|
||||||
|
## Memory 使用规则
|
||||||
|
|
||||||
|
本 skill **仅基于当前数据查询**生成答案。memory 可用于:
|
||||||
|
- 记住用户偏好的输出格式(如"喜欢表格不喜欢列表")
|
||||||
|
- 记住常用的过滤条件(如"默认看 qiudl 的提交")
|
||||||
|
|
||||||
|
**禁止**:用 memory 里的历史数据"缓存"事实类答案(这会导致过时数据)。每次问都要重新查。
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- devflow-claude: `plugins/pm/commands/ask.md`
|
||||||
|
- REQ-20260416-0017 P1-9
|
||||||
11
skills-core/pm-risk-plugin/.claude-plugin/plugin.json
Normal file
11
skills-core/pm-risk-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "pm-risk-plugin",
|
||||||
|
"description": "三维度项目风险扫描:需求层/代码层/流程层。当用户说'/risk'、'风险扫描'、'有什么风险'时自动激活",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "pm-risk",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "core"
|
||||||
|
}
|
||||||
154
skills-core/pm-risk-plugin/skills/SKILL.md
Normal file
154
skills-core/pm-risk-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
name: pm-risk
|
||||||
|
description: 三维度项目风险扫描(需求层/代码层/流程层)。当用户说"/risk"、"风险扫描"、"有什么风险"、"哪些需求停滞了"、"分支健康"时自动激活。
|
||||||
|
---
|
||||||
|
|
||||||
|
# pm-risk Skill — 三维度风险扫描
|
||||||
|
|
||||||
|
借鉴自 devflow-claude `/pm:risk`。源自 REQ-20260416-0017 P1-10。
|
||||||
|
|
||||||
|
## 命令
|
||||||
|
|
||||||
|
```
|
||||||
|
/risk [--save]
|
||||||
|
```
|
||||||
|
|
||||||
|
- 不加参数:扫描并展示报告
|
||||||
|
- `--save`:保存到思源笔记
|
||||||
|
|
||||||
|
## 扫描维度
|
||||||
|
|
||||||
|
### 维度 1: 需求层
|
||||||
|
|
||||||
|
通过 ai-proj MCP 查询需求状态:
|
||||||
|
|
||||||
|
| 检测项 | 数据源 | 严重 | 警告 | 提示 |
|
||||||
|
|--------|--------|------|------|------|
|
||||||
|
| 需求停滞 | `list_requirements` + `updated_at` | >14 天无更新 | >7 天无更新 | >3 天无更新 |
|
||||||
|
| 草稿滞留 | `list_requirements --status=draft` | >30 天 | >14 天 | >7 天 |
|
||||||
|
| 开发中无提交 | 需求关联分支 + `git log` | >7 天无 commit | >3 天 | - |
|
||||||
|
| 测试中超期 | `list_requirements --delivery_stage=testing` | >14 天 | >7 天 | - |
|
||||||
|
| 已批准未开发 | `list_requirements --status=approved` | >30 天 | >14 天 | - |
|
||||||
|
|
||||||
|
**查询方式**:
|
||||||
|
```
|
||||||
|
mcp__ai-proj__list_requirements(status=in_progress)
|
||||||
|
→ 遍历每个需求的 updated_at,计算距今天数
|
||||||
|
```
|
||||||
|
|
||||||
|
### 维度 2: 代码层
|
||||||
|
|
||||||
|
通过 git 命令分析:
|
||||||
|
|
||||||
|
| 检测项 | 命令 | 严重 | 警告 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| 未合并分支过久 | `git branch -r --no-merged origin/develop` + `git log -1` | >14 天无 commit | >7 天 |
|
||||||
|
| 频繁修复文件 | `git log --diff-filter=M --name-only` 近 30 天 | 同文件 >5 次 fix commit | >3 次 |
|
||||||
|
| 提交频率下降 | 本周 vs 上周 commit 数 | 下降 >70% | 下降 >50% |
|
||||||
|
| 大文件提交 | `git log --diff-filter=A --name-only` | >10MB 文件 | >5MB |
|
||||||
|
|
||||||
|
**查询方式**:
|
||||||
|
```bash
|
||||||
|
# 未合并分支
|
||||||
|
git for-each-ref --sort=-committerdate --format='%(refname:short) %(committerdate:short)' refs/remotes/origin/ | grep -v 'main\|develop\|HEAD'
|
||||||
|
|
||||||
|
# 频繁修复
|
||||||
|
git log --since='30 days ago' --grep='fix' --diff-filter=M --name-only --pretty=format:'' | sort | uniq -c | sort -rn | head -10
|
||||||
|
|
||||||
|
# 提交频率
|
||||||
|
THIS_WEEK=$(git log --since='7 days ago' --oneline | wc -l)
|
||||||
|
LAST_WEEK=$(git log --since='14 days ago' --until='7 days ago' --oneline | wc -l)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 维度 3: 流程层
|
||||||
|
|
||||||
|
通过 MCP + git 交叉分析:
|
||||||
|
|
||||||
|
| 检测项 | 检测方法 | 级别 |
|
||||||
|
|--------|---------|------|
|
||||||
|
| 跳过评审直接开发 | 需求 status 从 draft 直接跳到 in_progress(没有 approved 记录) | 警告 |
|
||||||
|
| PR 无评审直接合并 | Gitea API 查 PR 无 review approve 就 merge | 警告 |
|
||||||
|
| 测试门禁跳过 | delivery_stage 从 dev 跳到 released(没有 testing) | 严重 |
|
||||||
|
| 直接推 main | `git log --first-parent origin/main` 非 merge commit | 严重 |
|
||||||
|
|
||||||
|
## 风险级别定义
|
||||||
|
|
||||||
|
| 级别 | 图标 | 含义 | 响应 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| 严重 | 🔴 | 立即关注 | 当天处理 |
|
||||||
|
| 警告 | 🟡 | 近期需处理 | 3 天内处理 |
|
||||||
|
| 提示 | 🔵 | 值得留意 | 下次迭代关注 |
|
||||||
|
|
||||||
|
## 输出模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 🔍 风险扫描报告
|
||||||
|
|
||||||
|
**扫描时间**: YYYY-MM-DD HH:MM CST
|
||||||
|
**扫描范围**: 项目 ai-proj
|
||||||
|
|
||||||
|
## 概览
|
||||||
|
|
||||||
|
| 维度 | 🔴 严重 | 🟡 警告 | 🔵 提示 |
|
||||||
|
|------|---------|---------|---------|
|
||||||
|
| 需求层 | X | X | X |
|
||||||
|
| 代码层 | X | X | X |
|
||||||
|
| 流程层 | X | X | X |
|
||||||
|
|
||||||
|
## 🔴 严重风险
|
||||||
|
|
||||||
|
### [S1] REQ-20260410-0001 停滞 18 天
|
||||||
|
- **需求**: REQ-20260410-0001 用户积分管理
|
||||||
|
- **状态**: in_progress
|
||||||
|
- **最后更新**: 2026-04-01
|
||||||
|
- **建议**: 联系负责人确认是否阻塞,必要时降级或暂停
|
||||||
|
|
||||||
|
### [S2] ...
|
||||||
|
|
||||||
|
## 🟡 警告
|
||||||
|
|
||||||
|
### [W1] 分支 feat/xxx 14 天未合并
|
||||||
|
- **分支**: feat/old-feature
|
||||||
|
- **最后 commit**: 2026-04-05
|
||||||
|
- **建议**: 确认是否放弃,清理或合并
|
||||||
|
|
||||||
|
## 🔵 提示
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
## ✅ 一切正常的维度
|
||||||
|
|
||||||
|
- (无严重/警告时显示此段)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**下次扫描建议**: 每周一早会前执行 `/risk`
|
||||||
|
```
|
||||||
|
|
||||||
|
## 无风险时的输出
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ 风险扫描完成 — 一切正常
|
||||||
|
|
||||||
|
扫描了 X 个需求、Y 个分支、Z 条提交,未发现风险项。
|
||||||
|
|
||||||
|
下次扫描建议:下周一
|
||||||
|
```
|
||||||
|
|
||||||
|
## 定期执行建议
|
||||||
|
|
||||||
|
- **手动**:每周一早会前 `/risk`
|
||||||
|
- **自动**:可配合 `/loop 7d /risk --save` 定期扫描并保存到思源笔记
|
||||||
|
|
||||||
|
## 与其他 skill 的关系
|
||||||
|
|
||||||
|
| skill | 协作 |
|
||||||
|
|-------|------|
|
||||||
|
| `pm-ask` | risk 发现问题后,用 `/ask` 深入分析 |
|
||||||
|
| `req-workflow` | risk 发现流程违规后,用 `/req` 修正 |
|
||||||
|
| `ai-proj` | 底层 MCP 数据查询 |
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- devflow-claude: `plugins/pm/commands/risk.md`
|
||||||
|
- REQ-20260416-0017 P1-10
|
||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "publish",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "core"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "agent-browser",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
11
skills-dev/agent-swarm-plugin/.claude-plugin/plugin.json
Normal file
11
skills-dev/agent-swarm-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "agent-swarm-plugin",
|
||||||
|
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "agent-swarm",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
406
skills-dev/agent-swarm-plugin/skills/SKILL.md
Normal file
406
skills-dev/agent-swarm-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
---
|
||||||
|
name: agent-swarm
|
||||||
|
description: Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Agent Swarm - Multi-Agent Orchestration
|
||||||
|
|
||||||
|
基于 OpenAI Swarm 设计模式的多智能体协作系统,用于复杂开发任务的智能分解与协调。
|
||||||
|
|
||||||
|
## 核心概念
|
||||||
|
|
||||||
|
### 1. Agent(智能体)
|
||||||
|
每个 Agent 是具有特定职责的专家:
|
||||||
|
- **Instructions**: Agent 的角色定义和行为准则
|
||||||
|
- **Functions**: Agent 可以调用的工具函数
|
||||||
|
- **Handoffs**: 何时移交给其他 Agent
|
||||||
|
|
||||||
|
### 2. Handoff(任务移交)
|
||||||
|
Agent 之间的控制权转移机制:
|
||||||
|
- 当前 Agent 完成自己的职责
|
||||||
|
- 识别需要其他专长
|
||||||
|
- 移交给最合适的 Agent
|
||||||
|
|
||||||
|
### 3. Context Variables(上下文变量)
|
||||||
|
跨 Agent 共享的状态:
|
||||||
|
- 项目目录
|
||||||
|
- 技术栈信息
|
||||||
|
- 当前进度
|
||||||
|
- 发现的问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 预定义 Agent
|
||||||
|
|
||||||
|
### 1. Architect Agent(架构师)
|
||||||
|
**职责**: 理解需求、技术选型、设计系统架构
|
||||||
|
|
||||||
|
**何时使用**:
|
||||||
|
- 用户描述新功能或系统
|
||||||
|
- 需要技术方案设计
|
||||||
|
- 需要架构评审
|
||||||
|
|
||||||
|
**工具**:
|
||||||
|
- Read codebase
|
||||||
|
- Grep patterns
|
||||||
|
- 设计文档生成
|
||||||
|
|
||||||
|
**Handoff to**:
|
||||||
|
- Coder Agent(开始编码)
|
||||||
|
- Reviewer Agent(评审设计)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Coder Agent(编码者)
|
||||||
|
**职责**: 实现功能、编写代码、修复 bug
|
||||||
|
|
||||||
|
**何时使用**:
|
||||||
|
- 架构师完成设计
|
||||||
|
- 用户提出 bug 修复
|
||||||
|
- 需要代码重构
|
||||||
|
|
||||||
|
**工具**:
|
||||||
|
- Edit files
|
||||||
|
- Write files
|
||||||
|
- Git operations
|
||||||
|
|
||||||
|
**Handoff to**:
|
||||||
|
- Tester Agent(代码完成后)
|
||||||
|
- Architect Agent(遇到设计问题)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Tester Agent(测试员)
|
||||||
|
**职责**: 编写测试、运行测试、验证功能
|
||||||
|
|
||||||
|
**何时使用**:
|
||||||
|
- 代码编写完成
|
||||||
|
- 需要测试覆盖
|
||||||
|
- 验证 bug 修复
|
||||||
|
|
||||||
|
**工具**:
|
||||||
|
- Run tests
|
||||||
|
- Write test cases
|
||||||
|
- Coverage reports
|
||||||
|
|
||||||
|
**Handoff to**:
|
||||||
|
- Deployer Agent(测试通过)
|
||||||
|
- Coder Agent(发现问题)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Deployer Agent(部署员)
|
||||||
|
**职责**: 构建镜像、部署服务、监控上线
|
||||||
|
|
||||||
|
**何时使用**:
|
||||||
|
- 测试全部通过
|
||||||
|
- 需要发布到环境
|
||||||
|
- 需要回滚版本
|
||||||
|
|
||||||
|
**工具**:
|
||||||
|
- Docker build
|
||||||
|
- SSH deployment
|
||||||
|
- Health checks
|
||||||
|
|
||||||
|
**Handoff to**:
|
||||||
|
- Monitor Agent(部署完成)
|
||||||
|
- Coder Agent(部署失败)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Reviewer Agent(评审员)
|
||||||
|
**职责**: 代码审查、文档审查、安全检查
|
||||||
|
|
||||||
|
**何时使用**:
|
||||||
|
- PR 创建后
|
||||||
|
- 重要功能完成
|
||||||
|
- 需要质量把关
|
||||||
|
|
||||||
|
**工具**:
|
||||||
|
- Diff analysis
|
||||||
|
- Security scan
|
||||||
|
- Best practices check
|
||||||
|
|
||||||
|
**Handoff to**:
|
||||||
|
- Coder Agent(需要修改)
|
||||||
|
- Deployer Agent(审查通过)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 基本调用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/swarm start "在 new-ai-proj 中实现任务批量删除功能"
|
||||||
|
```
|
||||||
|
|
||||||
|
**执行流程**:
|
||||||
|
1. **Architect** 分析需求 → 设计 API 和前端交互
|
||||||
|
2. **Coder** 实现后端 API → 实现前端 UI
|
||||||
|
3. **Tester** 编写单元测试 → 运行测试
|
||||||
|
4. **Reviewer** 代码审查 → 安全检查
|
||||||
|
5. **Deployer** 部署到 staging → 验证功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 指定起始 Agent
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/swarm coder "修复 backend/handlers/task_handler.go 的空指针 bug"
|
||||||
|
```
|
||||||
|
|
||||||
|
直接从 Coder Agent 开始,跳过架构设计阶段。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 传递上下文
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/swarm start "优化数据库查询性能" \
|
||||||
|
--context project=/Users/coolbuy-dev/coding/new-ai-proj \
|
||||||
|
--context stack=Go,PostgreSQL,Redis \
|
||||||
|
--context module=backend/services
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 查看执行轨迹
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/swarm trace
|
||||||
|
```
|
||||||
|
|
||||||
|
显示 Agent 调用链:
|
||||||
|
```
|
||||||
|
Architect → analyzed requirements (3 min)
|
||||||
|
↓ handoff: "Design complete, ready for implementation"
|
||||||
|
Coder → implemented 5 files (12 min)
|
||||||
|
↓ handoff: "Code complete, needs testing"
|
||||||
|
Tester → wrote 8 test cases, all passed (5 min)
|
||||||
|
↓ handoff: "Tests passed, ready for review"
|
||||||
|
Reviewer → approved with 2 suggestions (2 min)
|
||||||
|
↓ handoff: "Approved, ready for deployment"
|
||||||
|
Deployer → deployed to staging, health check OK (3 min)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置文件
|
||||||
|
|
||||||
|
### swarm.yaml
|
||||||
|
|
||||||
|
在项目根目录创建 `swarm.yaml` 自定义 Agent 行为:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
agents:
|
||||||
|
architect:
|
||||||
|
instructions: |
|
||||||
|
你是系统架构师,专注于 Go + Vue.js 技术栈。
|
||||||
|
遵循 RESTful API 设计原则。
|
||||||
|
考虑性能、安全性、可维护性。
|
||||||
|
max_turns: 5
|
||||||
|
|
||||||
|
coder:
|
||||||
|
instructions: |
|
||||||
|
你是 Go 后端工程师和 Vue.js 前端工程师。
|
||||||
|
编写清晰、简洁、高性能的代码。
|
||||||
|
遵循项目现有代码风格。
|
||||||
|
tools:
|
||||||
|
- Edit
|
||||||
|
- Write
|
||||||
|
- Bash
|
||||||
|
max_turns: 10
|
||||||
|
|
||||||
|
tester:
|
||||||
|
instructions: |
|
||||||
|
你是测试工程师,编写全面的测试用例。
|
||||||
|
确保边界条件、错误处理、并发安全。
|
||||||
|
tools:
|
||||||
|
- Bash
|
||||||
|
- Write
|
||||||
|
test_command: "go test ./... -v"
|
||||||
|
max_turns: 5
|
||||||
|
|
||||||
|
context_variables:
|
||||||
|
project_root: /Users/coolbuy-dev/coding/new-ai-proj
|
||||||
|
backend_lang: Go 1.21
|
||||||
|
frontend_framework: Vue 3
|
||||||
|
database: PostgreSQL 15
|
||||||
|
deployment_target: staging.ai.pipexerp.com
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 高级功能
|
||||||
|
|
||||||
|
### 1. 自定义 Agent
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
agents:
|
||||||
|
database-optimizer:
|
||||||
|
instructions: |
|
||||||
|
你是数据库性能优化专家。
|
||||||
|
分析慢查询、优化索引、设计缓存策略。
|
||||||
|
functions:
|
||||||
|
- explain_analyze
|
||||||
|
- create_index
|
||||||
|
- cache_design
|
||||||
|
handoff_to:
|
||||||
|
- coder # 实现优化方案
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 条件 Handoff
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
handoff_rules:
|
||||||
|
- from: tester
|
||||||
|
to: coder
|
||||||
|
condition: "test_pass_rate < 90%"
|
||||||
|
message: "测试失败率超过 10%,需要修复"
|
||||||
|
|
||||||
|
- from: tester
|
||||||
|
to: deployer
|
||||||
|
condition: "test_pass_rate == 100%"
|
||||||
|
message: "所有测试通过,可以部署"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 并行 Agent
|
||||||
|
|
||||||
|
对于独立任务,多个 Agent 可以并行工作:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/swarm parallel \
|
||||||
|
"coder: 实现后端 API" \
|
||||||
|
"coder: 实现前端 UI" \
|
||||||
|
"tester: 编写 API 测试"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 与 Remote Coding 集成
|
||||||
|
|
||||||
|
在 OpenClaw 中调用本地 Claude Code 执行 Swarm 工作流:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# OpenClaw 调用 Melbourne Claude Code
|
||||||
|
ssh melbourne "cd /Users/coolbuy-dev/coding/new-ai-proj && \
|
||||||
|
/opt/homebrew/bin/claude --dangerously-skip-permissions \
|
||||||
|
-p '/swarm start 实现任务批量删除功能'"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 实际案例
|
||||||
|
|
||||||
|
### 案例 1: 新功能开发
|
||||||
|
|
||||||
|
**任务**: "为 AI-Proj 实现需求批量导出功能"
|
||||||
|
|
||||||
|
**执行过程**:
|
||||||
|
1. **Architect**:
|
||||||
|
- 分析需求:导出格式(Excel/PDF)、筛选条件、数据脱敏
|
||||||
|
- 设计 API: `POST /api/v1/requirements/export`
|
||||||
|
- 设计前端:导出按钮、进度条、下载链接
|
||||||
|
|
||||||
|
2. **Coder**:
|
||||||
|
- 后端实现 export service
|
||||||
|
- 前端实现导出 UI 组件
|
||||||
|
- 集成 file download 功能
|
||||||
|
|
||||||
|
3. **Tester**:
|
||||||
|
- 测试大量数据导出(1000+ 需求)
|
||||||
|
- 测试并发导出
|
||||||
|
- 测试下载失败重试
|
||||||
|
|
||||||
|
4. **Reviewer**:
|
||||||
|
- 检查文件大小限制
|
||||||
|
- 检查内存泄漏风险
|
||||||
|
- 检查数据权限控制
|
||||||
|
|
||||||
|
5. **Deployer**:
|
||||||
|
- 部署到 staging
|
||||||
|
- 验证导出功能
|
||||||
|
- 监控资源使用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 案例 2: Bug 修复
|
||||||
|
|
||||||
|
**任务**: "修复任务详情页加载缓慢问题"
|
||||||
|
|
||||||
|
**执行过程**:
|
||||||
|
1. **Architect**:
|
||||||
|
- 分析性能瓶颈:N+1 查询问题
|
||||||
|
- 设计优化方案:使用 JOIN 和预加载
|
||||||
|
|
||||||
|
2. **Coder**:
|
||||||
|
- 优化数据库查询
|
||||||
|
- 添加 Redis 缓存
|
||||||
|
- 更新前端数据获取逻辑
|
||||||
|
|
||||||
|
3. **Tester**:
|
||||||
|
- 性能测试:加载时间从 3s → 300ms
|
||||||
|
- 并发测试:100 用户同时访问
|
||||||
|
- 缓存一致性测试
|
||||||
|
|
||||||
|
4. **Deployer**:
|
||||||
|
- 灰度发布到 10% 用户
|
||||||
|
- 监控性能指标
|
||||||
|
- 全量发布
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
1. **明确任务范围**: 复杂任务交给 Swarm,简单任务直接执行
|
||||||
|
2. **合理设置 max_turns**: 避免 Agent 陷入死循环
|
||||||
|
3. **记录 Handoff 原因**: 便于追溯和调试
|
||||||
|
4. **定期审查轨迹**: 优化 Agent 协作流程
|
||||||
|
5. **利用 Context Variables**: 避免重复传递信息
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
| 问题 | 原因 | 解决方案 |
|
||||||
|
|------|------|----------|
|
||||||
|
| Agent 一直循环 | max_turns 设置过大 | 降低 max_turns,添加明确的 handoff 条件 |
|
||||||
|
| Handoff 失败 | 目标 Agent 未定义 | 检查 swarm.yaml 配置 |
|
||||||
|
| 上下文丢失 | Context Variables 未传递 | 在 handoff 时显式传递 context |
|
||||||
|
| 执行太慢 | 串行执行可并行任务 | 使用 `/swarm parallel` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 与其他 Skills 集成
|
||||||
|
|
||||||
|
- **dev-coding**: Coder Agent 使用 dev-coding 的编码规范
|
||||||
|
- **dev-test**: Tester Agent 使用 dev-test 的测试策略
|
||||||
|
- **ops-tools**: Deployer Agent 使用 ops-tools 进行部署
|
||||||
|
- **ai-proj**: 所有 Agent 使用 ai-proj MCP 进行任务同步
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 命令速查
|
||||||
|
|
||||||
|
| 命令 | 功能 |
|
||||||
|
|------|------|
|
||||||
|
| `/swarm start <task>` | 启动 Swarm 工作流(从 Architect 开始) |
|
||||||
|
| `/swarm <agent> <task>` | 从指定 Agent 开始 |
|
||||||
|
| `/swarm parallel <tasks>` | 并行执行多个任务 |
|
||||||
|
| `/swarm trace` | 查看执行轨迹 |
|
||||||
|
| `/swarm config` | 显示当前配置 |
|
||||||
|
| `/swarm agents` | 列出所有可用 Agent |
|
||||||
|
| `/swarm stop` | 终止当前 Swarm 执行 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考资料
|
||||||
|
|
||||||
|
- [OpenAI Swarm 文档](https://github.com/openai/swarm)
|
||||||
|
- [Multi-Agent Systems 设计模式](https://arxiv.org/abs/2308.00352)
|
||||||
|
- [Claude Code Skills 文档](https://docs.anthropic.com/claude-code/skills)
|
||||||
11
skills-dev/ai-chat-plugin/.claude-plugin/plugin.json
Normal file
11
skills-dev/ai-chat-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "ai-chat-plugin",
|
||||||
|
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "ai-chat",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
537
skills-dev/ai-chat-plugin/skills/SKILL.md
Normal file
537
skills-dev/ai-chat-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,537 @@
|
|||||||
|
---
|
||||||
|
name: ai-chat
|
||||||
|
description: AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。
|
||||||
|
arguments: <subcommand> [args]
|
||||||
|
---
|
||||||
|
|
||||||
|
# AI Chat Skill
|
||||||
|
|
||||||
|
测试和管理 Coolbuy PaaS AI Chat 服务的 Claude Code skill。
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
| 命令 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `/ai-chat send <message>` | 发送消息到 AI Chat,实时显示工具调用 + AI 回复 |
|
||||||
|
| `/ai-chat env [local\|staging]` | 切换/查看目标环境(默认 local) |
|
||||||
|
| `/ai-chat tools [category]` | 列出当前环境已注册的工具 |
|
||||||
|
| `/ai-chat config` | 查看 AI 配置(Provider、工具开关等) |
|
||||||
|
| `/ai-chat history` | 显示本次会话的历史消息 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Config
|
||||||
|
|
||||||
|
两套环境,通过 `/ai-chat env` 切换:
|
||||||
|
|
||||||
|
| 环境 | Auth URL | AI URL | 登录账号 |
|
||||||
|
|------|----------|--------|----------|
|
||||||
|
| **local** (默认) | `http://localhost:7089` | `http://localhost:7092` | lining_admin / admin123 |
|
||||||
|
| **staging** | `http://39.105.150.219:7089` | `http://39.105.150.219:7092` | lining_admin / admin123 |
|
||||||
|
|
||||||
|
### 状态文件
|
||||||
|
|
||||||
|
环境状态保存在 `/tmp/ai-chat-state.json`,格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"env": "local",
|
||||||
|
"token": "eyJ...",
|
||||||
|
"token_env": "local",
|
||||||
|
"history": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### /ai-chat env
|
||||||
|
|
||||||
|
**切换或查看当前环境。**
|
||||||
|
|
||||||
|
用法:
|
||||||
|
- `/ai-chat env` — 显示当前环境
|
||||||
|
- `/ai-chat env local` — 切换到本地环境
|
||||||
|
- `/ai-chat env staging` — 切换到 staging 环境
|
||||||
|
|
||||||
|
实现步骤:
|
||||||
|
|
||||||
|
1. 读取 `/tmp/ai-chat-state.json`(不存在则默认 `{"env":"local","history":[]}`)
|
||||||
|
2. 如果提供了参数,更新 `env` 字段并清空 `token`(环境变了 token 失效)
|
||||||
|
3. 写回状态文件
|
||||||
|
4. 输出当前环境信息表格
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### /ai-chat send
|
||||||
|
|
||||||
|
**发送消息到 AI Chat 并实时显示流式响应。**
|
||||||
|
|
||||||
|
用法:`/ai-chat send <message>`
|
||||||
|
|
||||||
|
实现步骤:
|
||||||
|
|
||||||
|
#### Step 1: 读取状态
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 读取状态文件
|
||||||
|
cat /tmp/ai-chat-state.json 2>/dev/null || echo '{"env":"local","history":[]}'
|
||||||
|
```
|
||||||
|
|
||||||
|
确定环境变量:
|
||||||
|
- **local**: `AUTH_URL=http://localhost:7089`, `AI_URL=http://localhost:7092`
|
||||||
|
- **staging**: `AUTH_URL=http://39.105.150.219:7089`, `AI_URL=http://39.105.150.219:7092`
|
||||||
|
|
||||||
|
#### Step 2: 获取 Token
|
||||||
|
|
||||||
|
如果状态文件中没有 token 或 `token_env` 与当前 `env` 不匹配,执行登录:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST "$AUTH_URL/api/v1/auth/login" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"username":"lining_admin","password":"admin123"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
从响应中提取 `access_token`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 响应格式
|
||||||
|
# {"access_token":"eyJ...","refresh_token":"...","token_type":"Bearer","expires_in":7200,"user_info":{...}}
|
||||||
|
```
|
||||||
|
|
||||||
|
用 `python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])"` 提取 token。
|
||||||
|
|
||||||
|
将 token 和 token_env 保存到状态文件。
|
||||||
|
|
||||||
|
#### Step 3: 构造请求并发送 SSE 流
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -N -X POST "$AI_URL/api/v1/ai/chat/stream" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"message\":\"$MSG\",\"history\":$HISTORY}" 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
**重要**: `history` 字段传入之前的会话历史(从状态文件读取),实现多轮对话。
|
||||||
|
|
||||||
|
#### Step 4: 解析 SSE 事件
|
||||||
|
|
||||||
|
用 Python 脚本解析 SSE 流(比 bash while read 更可靠):
|
||||||
|
|
||||||
|
```python
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""解析 AI Chat SSE 流并格式化输出"""
|
||||||
|
import sys, json
|
||||||
|
|
||||||
|
full_content = ""
|
||||||
|
tool_calls = []
|
||||||
|
|
||||||
|
for line in sys.stdin:
|
||||||
|
line = line.strip()
|
||||||
|
if not line.startswith("data:"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
data_str = line[5:].strip()
|
||||||
|
if not data_str:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
event = json.loads(data_str)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
evt_type = event.get("type", "")
|
||||||
|
|
||||||
|
if evt_type == "content":
|
||||||
|
chunk = event.get("content", "")
|
||||||
|
full_content += chunk
|
||||||
|
# 实时输出内容片段
|
||||||
|
sys.stdout.write(chunk)
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
elif evt_type == "tool_call":
|
||||||
|
tc = event.get("tool_call", {})
|
||||||
|
tool_name = tc.get("name", "unknown")
|
||||||
|
tool_args = tc.get("arguments", {})
|
||||||
|
tool_id = tc.get("id", "")
|
||||||
|
tool_calls.append({"id": tool_id, "name": tool_name})
|
||||||
|
# 输出工具调用标记
|
||||||
|
args_str = json.dumps(tool_args, ensure_ascii=False)
|
||||||
|
if len(args_str) > 200:
|
||||||
|
args_str = args_str[:200] + "..."
|
||||||
|
print(f"\n🔧 Tool Call: {tool_name}", file=sys.stderr)
|
||||||
|
print(f" Args: {args_str}", file=sys.stderr)
|
||||||
|
|
||||||
|
elif evt_type == "tool_result":
|
||||||
|
tr = event.get("tool_result", {})
|
||||||
|
tool_name = tr.get("name", "unknown")
|
||||||
|
content = tr.get("content", "")
|
||||||
|
is_error = tr.get("is_error", False)
|
||||||
|
# 截断长结果
|
||||||
|
if len(content) > 500:
|
||||||
|
content = content[:500] + f"... ({len(content)} chars total)"
|
||||||
|
status = "❌ Error" if is_error else "✅ Result"
|
||||||
|
print(f" {status} [{tool_name}]: {content}", file=sys.stderr)
|
||||||
|
|
||||||
|
elif evt_type == "done":
|
||||||
|
usage = event.get("usage") or {}
|
||||||
|
prompt_t = usage.get("prompt_tokens", 0)
|
||||||
|
completion_t = usage.get("completion_tokens", 0)
|
||||||
|
total_t = usage.get("total_tokens", 0)
|
||||||
|
print(f"\n\n--- Done ---", file=sys.stderr)
|
||||||
|
if total_t > 0:
|
||||||
|
print(f"Tokens: {prompt_t} prompt + {completion_t} completion = {total_t} total", file=sys.stderr)
|
||||||
|
if tool_calls:
|
||||||
|
print(f"Tool calls: {len(tool_calls)} ({', '.join(tc['name'] for tc in tool_calls)})", file=sys.stderr)
|
||||||
|
|
||||||
|
elif evt_type == "error":
|
||||||
|
err = event.get("error", "unknown error")
|
||||||
|
print(f"\n❌ Error: {err}", file=sys.stderr)
|
||||||
|
|
||||||
|
# 输出换行
|
||||||
|
print()
|
||||||
|
# 将 full_content 输出到 fd 3 用于状态更新(如果 fd 3 打开)
|
||||||
|
try:
|
||||||
|
with open("/tmp/ai-chat-response.txt", "w") as f:
|
||||||
|
f.write(full_content)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 5: 更新会话历史
|
||||||
|
|
||||||
|
发送完成后,将用户消息和 AI 回复追加到状态文件的 `history` 数组中:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"role": "user", "content": "<用户消息>"},
|
||||||
|
{"role": "assistant", "content": "<AI 完整回复>"}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 完整 bash 执行流程
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
MSG="$1"
|
||||||
|
STATE_FILE="/tmp/ai-chat-state.json"
|
||||||
|
|
||||||
|
# 1. 读取状态
|
||||||
|
if [ -f "$STATE_FILE" ]; then
|
||||||
|
STATE=$(cat "$STATE_FILE")
|
||||||
|
else
|
||||||
|
STATE='{"env":"local","history":[]}'
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENV=$(echo "$STATE" | python3 -c "import json,sys; print(json.load(sys.stdin).get('env','local'))")
|
||||||
|
TOKEN=$(echo "$STATE" | python3 -c "import json,sys; print(json.load(sys.stdin).get('token',''))")
|
||||||
|
TOKEN_ENV=$(echo "$STATE" | python3 -c "import json,sys; print(json.load(sys.stdin).get('token_env',''))")
|
||||||
|
HISTORY=$(echo "$STATE" | python3 -c "import json,sys; print(json.dumps(json.load(sys.stdin).get('history',[])))")
|
||||||
|
|
||||||
|
# 2. 确定 URL
|
||||||
|
if [ "$ENV" = "staging" ]; then
|
||||||
|
AUTH_URL="http://39.105.150.219:7089"
|
||||||
|
AI_URL="http://39.105.150.219:7092"
|
||||||
|
else
|
||||||
|
AUTH_URL="http://localhost:7089"
|
||||||
|
AI_URL="http://localhost:7092"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. 获取 token(如果需要)
|
||||||
|
if [ -z "$TOKEN" ] || [ "$TOKEN_ENV" != "$ENV" ]; then
|
||||||
|
echo "🔐 Logging in to $ENV environment..."
|
||||||
|
LOGIN_RESP=$(curl -s -X POST "$AUTH_URL/api/v1/auth/login" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"username":"lining_admin","password":"admin123"}')
|
||||||
|
|
||||||
|
TOKEN=$(echo "$LOGIN_RESP" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('access_token',d.get('data',{}).get('access_token','')))")
|
||||||
|
|
||||||
|
if [ -z "$TOKEN" ]; then
|
||||||
|
echo "❌ Login failed: $LOGIN_RESP"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ Login successful"
|
||||||
|
|
||||||
|
# 更新状态中的 token
|
||||||
|
STATE=$(echo "$STATE" | python3 -c "
|
||||||
|
import json,sys
|
||||||
|
s=json.load(sys.stdin)
|
||||||
|
s['token']='$TOKEN'
|
||||||
|
s['token_env']='$ENV'
|
||||||
|
print(json.dumps(s,ensure_ascii=False))
|
||||||
|
")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. 发送 SSE 请求并解析
|
||||||
|
echo ""
|
||||||
|
echo "📤 Sending to $ENV: $MSG"
|
||||||
|
echo "---"
|
||||||
|
|
||||||
|
# 转义消息中的特殊字符
|
||||||
|
MSG_JSON=$(python3 -c "import json; print(json.dumps('$MSG'))")
|
||||||
|
|
||||||
|
curl -s -N -X POST "$AI_URL/api/v1/ai/chat/stream" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"message\":$MSG_JSON,\"history\":$HISTORY}" 2>&1 | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
|
||||||
|
full_content = ''
|
||||||
|
tool_calls = []
|
||||||
|
|
||||||
|
for line in sys.stdin:
|
||||||
|
line = line.strip()
|
||||||
|
if not line.startswith('data:'):
|
||||||
|
continue
|
||||||
|
data_str = line[5:].strip()
|
||||||
|
if not data_str:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
event = json.loads(data_str)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
evt_type = event.get('type', '')
|
||||||
|
|
||||||
|
if evt_type == 'content':
|
||||||
|
chunk = event.get('content', '')
|
||||||
|
full_content += chunk
|
||||||
|
sys.stdout.write(chunk)
|
||||||
|
sys.stdout.flush()
|
||||||
|
elif evt_type == 'tool_call':
|
||||||
|
tc = event.get('tool_call', {})
|
||||||
|
tool_name = tc.get('name', 'unknown')
|
||||||
|
tool_args = tc.get('arguments', {})
|
||||||
|
tool_calls.append({'name': tool_name})
|
||||||
|
args_str = json.dumps(tool_args, ensure_ascii=False)
|
||||||
|
if len(args_str) > 200:
|
||||||
|
args_str = args_str[:200] + '...'
|
||||||
|
print(f'\n🔧 Tool Call: {tool_name}', file=sys.stderr)
|
||||||
|
print(f' Args: {args_str}', file=sys.stderr)
|
||||||
|
elif evt_type == 'tool_result':
|
||||||
|
tr = event.get('tool_result', {})
|
||||||
|
tool_name = tr.get('name', 'unknown')
|
||||||
|
content = tr.get('content', '')
|
||||||
|
is_error = tr.get('is_error', False)
|
||||||
|
if len(content) > 500:
|
||||||
|
content = content[:500] + f'... ({len(content)} chars total)'
|
||||||
|
status = '❌' if is_error else '✅'
|
||||||
|
print(f' {status} [{tool_name}]: {content}', file=sys.stderr)
|
||||||
|
elif evt_type == 'done':
|
||||||
|
usage = event.get('usage') or {}
|
||||||
|
pt = usage.get('prompt_tokens', 0)
|
||||||
|
ct = usage.get('completion_tokens', 0)
|
||||||
|
tt = usage.get('total_tokens', 0)
|
||||||
|
print(f'\n\n--- Done ---', file=sys.stderr)
|
||||||
|
if tt > 0:
|
||||||
|
print(f'Tokens: {pt} prompt + {ct} completion = {tt} total', file=sys.stderr)
|
||||||
|
if tool_calls:
|
||||||
|
names = ', '.join(tc['name'] for tc in tool_calls)
|
||||||
|
print(f'Tool calls: {len(tool_calls)} ({names})', file=sys.stderr)
|
||||||
|
elif evt_type == 'error':
|
||||||
|
err = event.get('error', 'unknown error')
|
||||||
|
print(f'\n❌ Error: {err}', file=sys.stderr)
|
||||||
|
|
||||||
|
print()
|
||||||
|
with open('/tmp/ai-chat-response.txt', 'w') as f:
|
||||||
|
f.write(full_content)
|
||||||
|
"
|
||||||
|
|
||||||
|
# 5. 更新历史
|
||||||
|
RESPONSE=$(cat /tmp/ai-chat-response.txt 2>/dev/null || echo "")
|
||||||
|
python3 -c "
|
||||||
|
import json
|
||||||
|
state_file = '$STATE_FILE'
|
||||||
|
try:
|
||||||
|
with open(state_file) as f:
|
||||||
|
state = json.load(f)
|
||||||
|
except:
|
||||||
|
state = {'env': 'local', 'history': []}
|
||||||
|
|
||||||
|
msg = $MSG_JSON
|
||||||
|
resp = '''$RESPONSE'''
|
||||||
|
|
||||||
|
state['history'].append({'role': 'user', 'content': msg})
|
||||||
|
if resp:
|
||||||
|
state['history'].append({'role': 'assistant', 'content': resp})
|
||||||
|
state['token'] = '$TOKEN'
|
||||||
|
state['token_env'] = '$ENV'
|
||||||
|
|
||||||
|
with open(state_file, 'w') as f:
|
||||||
|
json.dump(state, f, ensure_ascii=False, indent=2)
|
||||||
|
"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "💬 History: $(echo "$STATE" | python3 -c "import json,sys; h=json.load(sys.stdin).get('history',[]); print(len(h)//2 + 1)") messages in session"
|
||||||
|
```
|
||||||
|
|
||||||
|
**重要注意事项**:
|
||||||
|
- 上面的脚本是逻辑参考,**不要**原样执行。Claude Code 应按步骤逐一执行 bash 命令。
|
||||||
|
- 消息中的引号和特殊字符需要用 python3 json.dumps 转义。
|
||||||
|
- 如果 token 过期(401 响应),自动重新登录。
|
||||||
|
- SSE 超时设置 `--max-time 120`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### /ai-chat tools
|
||||||
|
|
||||||
|
**列出当前环境已注册的工具。**
|
||||||
|
|
||||||
|
用法:
|
||||||
|
- `/ai-chat tools` — 列出所有工具
|
||||||
|
- `/ai-chat tools <category>` — 按分类过滤
|
||||||
|
|
||||||
|
实现步骤:
|
||||||
|
|
||||||
|
1. 读取状态获取环境和 token(必要时先登录)
|
||||||
|
2. 发送请求:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 列出所有工具
|
||||||
|
curl -s "$AI_URL/api/v1/ai/tools" \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
|
||||||
|
# 按分类过滤
|
||||||
|
curl -s "$AI_URL/api/v1/ai/tools?category=order" \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 响应格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "list_orders",
|
||||||
|
"description": "查询订单列表",
|
||||||
|
"category": "order",
|
||||||
|
"enabled": true,
|
||||||
|
"parameters": {...}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 按 category 分组,输出表格:
|
||||||
|
|
||||||
|
```
|
||||||
|
📋 AI Tools (142 total)
|
||||||
|
|
||||||
|
order (15 tools)
|
||||||
|
├── list_orders 查询订单列表
|
||||||
|
├── get_order_detail 获取订单详情
|
||||||
|
└── ...
|
||||||
|
|
||||||
|
product (12 tools)
|
||||||
|
├── list_products 查询商品列表
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
已知工具分类:order, product, sku, inventory, task, brand, requirement, customer, dashboard, distribution, finance, discount, channel, approval, organization, feature_gap
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### /ai-chat config
|
||||||
|
|
||||||
|
**查看 AI 服务配置。**
|
||||||
|
|
||||||
|
实现步骤:
|
||||||
|
|
||||||
|
1. 根据当前环境读取对应配置文件:
|
||||||
|
- **local**: 读取 `ai-service/api/etc/ai-api-local.yaml`
|
||||||
|
- **staging**: SSH 到 staging 读取 `/opt/coolbuy/configs/ai-api.yaml`
|
||||||
|
|
||||||
|
2. 显示关键配置项:
|
||||||
|
- AI Provider 和 Model
|
||||||
|
- 各工具分类的开关状态
|
||||||
|
- API 端口
|
||||||
|
|
||||||
|
3. 输出格式:
|
||||||
|
|
||||||
|
```
|
||||||
|
⚙️ AI Config (local)
|
||||||
|
|
||||||
|
Provider: deepseek
|
||||||
|
Model: deepseek-chat
|
||||||
|
Port: 7092
|
||||||
|
|
||||||
|
Tool Categories:
|
||||||
|
✅ order ✅ product ✅ sku
|
||||||
|
✅ inventory ✅ task ✅ brand
|
||||||
|
✅ requirement ✅ customer ✅ dashboard
|
||||||
|
✅ distribution ✅ finance ✅ discount
|
||||||
|
✅ channel ✅ approval ✅ organization
|
||||||
|
✅ feature_gap
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### /ai-chat history
|
||||||
|
|
||||||
|
**显示当前会话的历史消息。**
|
||||||
|
|
||||||
|
实现步骤:
|
||||||
|
|
||||||
|
1. 读取 `/tmp/ai-chat-state.json` 的 `history` 数组
|
||||||
|
2. 按时间顺序显示:
|
||||||
|
|
||||||
|
```
|
||||||
|
💬 Chat History (3 messages)
|
||||||
|
|
||||||
|
[1] 👤 User: 你好
|
||||||
|
🤖 AI: 你好!我是 AI 助手...
|
||||||
|
|
||||||
|
[2] 👤 User: 搜索组织架构找到大客户部
|
||||||
|
🤖 AI: 我来帮你搜索... (used: search_organizations)
|
||||||
|
|
||||||
|
[3] 👤 User: 最近的订单
|
||||||
|
🤖 AI: 以下是最近的订单列表...
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 如果需要清空历史:`/ai-chat history clear`
|
||||||
|
- 删除状态文件中的 history 数组,重置为空
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SSE Event Format Reference
|
||||||
|
|
||||||
|
AI Chat SSE 流使用 `event: message` + `data: {json}` 格式:
|
||||||
|
|
||||||
|
| type | 数据字段 | 说明 |
|
||||||
|
|------|---------|------|
|
||||||
|
| `content` | `content: "<text>"` | 增量文本内容 |
|
||||||
|
| `tool_call` | `tool_call: {id, name, arguments}` | AI 请求调用工具 |
|
||||||
|
| `tool_result` | `tool_result: {tool_call_id, name, content, is_error}` | 工具执行结果 |
|
||||||
|
| `done` | `usage: {prompt_tokens, completion_tokens, total_tokens}` (可能为 null), `finish_reason` | 流结束 |
|
||||||
|
| `error` | `error: "<message>"` | 错误 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Token 过期 (401)
|
||||||
|
如果请求返回 401,删除缓存 token 重新登录:
|
||||||
|
```bash
|
||||||
|
# 清除 token 强制重新登录
|
||||||
|
python3 -c "
|
||||||
|
import json
|
||||||
|
with open('/tmp/ai-chat-state.json') as f: s=json.load(f)
|
||||||
|
s['token']=''
|
||||||
|
with open('/tmp/ai-chat-state.json','w') as f: json.dump(s,f)
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 连接失败
|
||||||
|
- **local**: 确认本地服务已启动 (`./scripts/start_dev.sh`)
|
||||||
|
- **staging**: 确认 staging 服务运行中 (`ssh coolbuy-staging "docker ps | grep ai-service"`)
|
||||||
|
|
||||||
|
### SSE 流中断
|
||||||
|
- 检查 AI 服务日志
|
||||||
|
- local: 查看终端输出
|
||||||
|
- staging: `ssh coolbuy-staging "docker logs coolbuy-ai-service --tail 50"`
|
||||||
|
|
||||||
|
### 消息中包含特殊字符
|
||||||
|
务必用 `python3 -c "import json; print(json.dumps(msg))"` 转义消息内容,避免 JSON 解析失败。
|
||||||
@@ -2,5 +2,10 @@
|
|||||||
"name": "db-migration-plugin",
|
"name": "db-migration-plugin",
|
||||||
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时激活。",
|
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时激活。",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": { "name": "qiudl" }
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "db-migration",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
11
skills-dev/defect-analysis-plugin/.claude-plugin/plugin.json
Normal file
11
skills-dev/defect-analysis-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "defect-analysis-plugin",
|
||||||
|
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "defect-analysis",
|
||||||
|
"install_type": "command",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
159
skills-dev/defect-analysis-plugin/skills/SKILL.md
Normal file
159
skills-dev/defect-analysis-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
---
|
||||||
|
name: defect-analysis
|
||||||
|
description: 系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 设计缺陷分析 Skill(通用版)
|
||||||
|
|
||||||
|
你是资深架构审计师。对给定的需求方案或代码实现,执行系统性的多维度缺陷检查,**反复迭代直到收敛**(连续一轮无新发现即停止)。
|
||||||
|
|
||||||
|
## 检查维度(按严重度排序)
|
||||||
|
|
||||||
|
### 1. 致命级:架构不可行
|
||||||
|
- **异步/同步冲突**:异步操作被当作同步使用?长时间操作阻塞了请求?
|
||||||
|
- **框架限制**:v-html 无法绑定事件、WebSocket/SSE 超时、API 轮数限制
|
||||||
|
- **数据格式不匹配**:前后端约定的 ID 格式/字段名/序列化方式不一致?
|
||||||
|
- **循环依赖**:模块 A 内部调 B,B 又依赖 A 的结果?嵌套调用超时?
|
||||||
|
|
||||||
|
### 2. 高级:运行时崩溃
|
||||||
|
- **资源生命周期**:DB session/连接/文件句柄在回调中过期?
|
||||||
|
- **并发冲突**:多个异步操作同时修改共享状态?用户操作和自动流程冲突?
|
||||||
|
- **超时/死锁**:链式调用累计超时?轮询无限等待?重试风暴?
|
||||||
|
- **内存泄漏**:大数据未释放?事件监听器未清理?闭包持有旧引用?
|
||||||
|
|
||||||
|
### 3. 中级:数据错误
|
||||||
|
- **状态覆盖**:多次回调覆盖同一变量?最后一次覆盖前面的?
|
||||||
|
- **上下文丢失**:对话/会话截断导致关键信息丢失?
|
||||||
|
- **参数传递断裂**:A 组件的输出无法完整传递给 B 组件?
|
||||||
|
- **类型不安全**:JSON.parse 可能失败?nullable 字段未处理?双重编码?
|
||||||
|
- **初始化缺失**:变量未赋初值?首次使用时为 undefined/NaN?
|
||||||
|
|
||||||
|
### 4. 低级:体验/维护问题
|
||||||
|
- **重复触发**:watcher/callback 多次触发同一操作?
|
||||||
|
- **维护成本**:硬编码路径/行号/ID 需要手动同步?
|
||||||
|
- **XSS/注入**:用户输入或外部输出被直接渲染为 HTML?
|
||||||
|
- **状态清理**:组件卸载/页面切换时未清理进行中的请求/定时器?
|
||||||
|
|
||||||
|
## 检查流程
|
||||||
|
|
||||||
|
1. **读取方案描述**(需求文档或代码)
|
||||||
|
2. **画数据流图**(从用户操作 → 前端 → API → 后端 → DB/外部服务 → 返回)
|
||||||
|
3. **沿数据流逐节点检查**:每个节点问 5 个问题:
|
||||||
|
- 输入从哪来?可能为 null/异常吗?
|
||||||
|
- 输出给谁?接收方能处理所有情况吗?
|
||||||
|
- 耗时多久?会超时吗?
|
||||||
|
- 资源(session/连接/监听器)何时释放?
|
||||||
|
- 并发执行 N 次会怎样?
|
||||||
|
4. **检查边界**:
|
||||||
|
- 前后端交界(API 格式/认证/超时/序列化)
|
||||||
|
- 同步/异步交界(await/callback/轮询/SSE)
|
||||||
|
- 组件生命周期交界(mount/unmount/路由切换)
|
||||||
|
- AI/LLM 输出交界(结构化 vs 自由文本,幻觉风险,token 限制)
|
||||||
|
5. **从用户旅程检查**:
|
||||||
|
- 首次使用(服务未就绪?数据为空?)
|
||||||
|
- 正常使用(N 次重复操作后状态累积?)
|
||||||
|
- 异常使用(断网/超时/并发/快速切换)
|
||||||
|
- 边界数据(空列表/超大数据/特殊字符)
|
||||||
|
|
||||||
|
## 迭代收敛规则
|
||||||
|
|
||||||
|
- 每轮检查一个维度,输出发现的缺陷列表
|
||||||
|
- 如果某轮发现 0 个新缺陷 → **收敛,停止**
|
||||||
|
- 如果 5 轮后仍有新发现 → 继续,最多 10 轮
|
||||||
|
- 每个缺陷标注严重度和轮次
|
||||||
|
|
||||||
|
## 输出格式
|
||||||
|
|
||||||
|
对每个缺陷:
|
||||||
|
|
||||||
|
```
|
||||||
|
### 缺陷 #N: {标题}({致命/高/中/低})
|
||||||
|
|
||||||
|
**问题**:{一句话描述}
|
||||||
|
**场景**:{触发条件}
|
||||||
|
**后果**:{不修复会怎样}
|
||||||
|
**解决**:{具体方案}
|
||||||
|
**验收**:- [ ] {如何确认已修复}
|
||||||
|
```
|
||||||
|
|
||||||
|
最后输出汇总表:
|
||||||
|
|
||||||
|
```
|
||||||
|
| 轮次 | 维度 | 缺陷数 | 关键发现 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | 架构 | N | ... |
|
||||||
|
| ... | ... | ... | ... |
|
||||||
|
| K | 收敛 | 0 | 无新发现 |
|
||||||
|
```
|
||||||
|
|
||||||
|
## 端到端验证方法论
|
||||||
|
|
||||||
|
设计方案发现的缺陷可能在实际运行时不存在,反之亦然。对关键功能执行以下分层验证:
|
||||||
|
|
||||||
|
### 层 1:单元验证(后端隔离测试)
|
||||||
|
直接调用目标函数,绕过 API/前端,确认核心逻辑可用:
|
||||||
|
```python
|
||||||
|
# 示例:验证 AI 工具是否正常返回数据
|
||||||
|
docker exec app python3 -c "
|
||||||
|
import asyncio, json
|
||||||
|
from app.services.ai_tools import ai_tool_registry
|
||||||
|
from app.models.base import async_session_factory
|
||||||
|
async def test():
|
||||||
|
async with async_session_factory() as db:
|
||||||
|
result = await ai_tool_registry.execute('tool_name', {args}, user_id=1, db=db)
|
||||||
|
print(json.loads(result))
|
||||||
|
asyncio.run(test())
|
||||||
|
"
|
||||||
|
```
|
||||||
|
**如果这层失败**:代码逻辑错误或依赖缺失。
|
||||||
|
|
||||||
|
### 层 2:AI 行为验证(LLM 是否正确调用工具)
|
||||||
|
直接调用 AI 非流式接口,验证 LLM 是否输出了预期的工具调用标签:
|
||||||
|
```python
|
||||||
|
# 示例:验证 DeepSeek 是否输出 [TOOL_CALL]
|
||||||
|
result = await ai_gateway.chat([
|
||||||
|
{"role": "system", "content": SYSTEM_PROMPT},
|
||||||
|
{"role": "user", "content": "分析回测 LB-xxx"}
|
||||||
|
])
|
||||||
|
print("[TOOL_CALL] found:", "[TOOL_CALL]" in result["content"])
|
||||||
|
```
|
||||||
|
**如果这层失败**:SYSTEM_PROMPT 不够强,LLM 不遵循指令。加"必须"/"绝不能"等强制词。
|
||||||
|
|
||||||
|
### 层 3:SSE 流式验证(前后端数据管道)
|
||||||
|
用 curl 模拟前端 SSE 请求,检查事件流格式:
|
||||||
|
```bash
|
||||||
|
curl -N "http://localhost:8000/api/ai/chat" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-d '{"message":"测试","model":"v3"}' | head -20
|
||||||
|
```
|
||||||
|
检查是否有 `type: "tool_call"` 和 `type: "tool_result"` 事件。
|
||||||
|
**如果这层失败**:SSE 流解析/工具执行/事件格式问题。
|
||||||
|
|
||||||
|
### 层 4:前端渲染验证(浏览器实际效果)
|
||||||
|
打开 F12 → Network → 找到 SSE 请求 → EventStream 选项卡:
|
||||||
|
- 有 `tool_call` 事件?→ 后端正常
|
||||||
|
- 有 `tool_result` 事件?→ 工具执行正常
|
||||||
|
- 页面渲染了结果?→ 前端正常
|
||||||
|
**如果这层失败**:前端缓存(Cmd+Shift+R)、v-html 渲染、事件委托问题。
|
||||||
|
|
||||||
|
### 层 5:部署验证(CI/CD + 远端环境)
|
||||||
|
```bash
|
||||||
|
# 检查 CI 绿否
|
||||||
|
gh run list --limit 1 --branch main
|
||||||
|
# 检查远端容器是否加载了新代码
|
||||||
|
ssh server "docker exec app grep 'key_function' /app/path/to/file.py"
|
||||||
|
# 检查远端日志
|
||||||
|
ssh server "docker logs app 2>&1 | tail -20"
|
||||||
|
```
|
||||||
|
**如果这层失败**:PR 未合并、CI 失败、Docker 缓存旧镜像、.env 缺配置。
|
||||||
|
|
||||||
|
### 常见的"设计没问题但实际不工作"的原因
|
||||||
|
|
||||||
|
| 症状 | 通常原因 | 排查方法 |
|
||||||
|
|---|---|---|
|
||||||
|
| AI 不调用工具 | SYSTEM_PROMPT 用"可以"而非"必须" | 层 2 验证 |
|
||||||
|
| 工具返回空 | DB 中无数据 / 权限隔离 user_id 不匹配 | 层 1 验证 |
|
||||||
|
| 前端无反应 | 浏览器缓存旧 JS / SSE 事件未解析 | 层 4 + Cmd+Shift+R |
|
||||||
|
| 远端不生效 | PR 未合并 / Docker 用了旧镜像 | 层 5 验证 |
|
||||||
|
| 数据格式错 | 双重 JSON 编码 / 字段名不一致 | 层 3 验证 |
|
||||||
|
| 按钮点不了 | v-html 无法绑 Vue 事件 | 层 4 + 事件委托 |
|
||||||
@@ -2,5 +2,10 @@
|
|||||||
"name": "deploy-rollback-plugin",
|
"name": "deploy-rollback-plugin",
|
||||||
"description": "回滚方案插件。部署后发现问题时的回滚策略、数据修复、灰度回退。挂载在 deploy 阶段。",
|
"description": "回滚方案插件。部署后发现问题时的回滚策略、数据修复、灰度回退。挂载在 deploy 阶段。",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": { "name": "qiudl" }
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "deploy-rollback",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,11 @@
|
|||||||
{"name":"dev-android-plugin","description":"Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
|
{
|
||||||
|
"name": "dev-android-plugin",
|
||||||
|
"description": "Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。按需加载。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "dev-android",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "dev-arch",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "dev-cicd",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "dev-coding",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "dev-commit",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,48 @@ feat/REQ-xxx-i42 + commit → commit message 自动加 "closes #42"
|
|||||||
确认提交?(y/n)
|
确认提交?(y/n)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Issue 集成规范(REQ-20260416-0017 P1-13)
|
||||||
|
|
||||||
|
借鉴 devflow-claude 的 issue 关联机制。
|
||||||
|
|
||||||
|
### 分支名 `-iN` 后缀
|
||||||
|
|
||||||
|
当任务/需求来自 Gitea/GitHub issue 时,分支名末尾追加 `-iN`:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat/REQ-20260416-0017-user-points-i12 ← issue #12
|
||||||
|
fix/login-token-expired-i5 ← issue #5
|
||||||
|
```
|
||||||
|
|
||||||
|
**规则**:
|
||||||
|
- `-iN` 仅当 issue 关联存在时追加
|
||||||
|
- `N` 为纯数字(不带 `#`)
|
||||||
|
- 位于分支名最末尾
|
||||||
|
|
||||||
|
### commit message 自动追加 `closes #N`
|
||||||
|
|
||||||
|
当分支名含 `-iN` 后缀时,commit message 末尾自动追加 `closes #N`:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(user): 实现积分规则管理 (REQ-20260416-0017) closes #12
|
||||||
|
fix(auth): 修复 token 过期未刷新 closes #5
|
||||||
|
```
|
||||||
|
|
||||||
|
**效果**:PR 合并后 Gitea/GitHub 自动关闭关联 issue。
|
||||||
|
|
||||||
|
### Issue 编号读取优先级
|
||||||
|
|
||||||
|
| 优先级 | 来源 | 说明 |
|
||||||
|
|-------|------|------|
|
||||||
|
| 1 | `--from-issue=#N` 参数 | 用户显式指定 |
|
||||||
|
| 2 | 分支名 `-iN` 后缀 | 自动解析 `(-i(\d+))$` |
|
||||||
|
| 3 | MCP 需求文档关联的 issue | (预留) |
|
||||||
|
|
||||||
|
### 不触发的情况
|
||||||
|
|
||||||
|
- 分支名不含 `-iN` → 不追加 `closes`
|
||||||
|
- 用户显式说"不关联 issue" → 跳过
|
||||||
|
|
||||||
## 与 ai-proj 集成
|
## 与 ai-proj 集成
|
||||||
|
|
||||||
- **查询当前需求**:通过 MCP `mcp__ai-proj__find_requirement` 或 `list_requirements` 找 user 进行中的
|
- **查询当前需求**:通过 MCP `mcp__ai-proj__find_requirement` 或 `list_requirements` 找 user 进行中的
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "dev-deploy",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "dev-integration",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,11 @@
|
|||||||
{"name":"dev-ios-plugin","description":"iOS 开发插件。Swift/SwiftUI + MVVM 架构、TestFlight 部署、Xcode 构建。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
|
{
|
||||||
|
"name": "dev-ios-plugin",
|
||||||
|
"description": "iOS 开发插件。Swift/SwiftUI + MVVM 架构、TestFlight 部署、Xcode 构建。按需加载。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "dev-ios",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,11 @@
|
|||||||
{"name":"dev-mcp-plugin","description":"MCP Bridge 开发插件。TypeScript MCP 服务开发、Token 管理、HTTP 客户端模式。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
|
{
|
||||||
|
"name": "dev-mcp-plugin",
|
||||||
|
"description": "MCP Bridge 开发插件。TypeScript MCP 服务开发、Token 管理、HTTP 客户端模式。按需加载。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "dev-mcp",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,11 @@
|
|||||||
{"name":"dev-pda-plugin","description":"PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先模式。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
|
{
|
||||||
|
"name": "dev-pda-plugin",
|
||||||
|
"description": "PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先模式。按需加载。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "dev-pda",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "dev-review",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,10 @@
|
|||||||
"name": "dev-scaffold-plugin",
|
"name": "dev-scaffold-plugin",
|
||||||
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架(Model/Repository/Service/Handler/Route)。挂载在 dev 阶段。",
|
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架(Model/Repository/Service/Handler/Route)。挂载在 dev 阶段。",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": { "name": "qiudl" }
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "dev-scaffold",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "dev-test",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
11
skills-dev/executing-plans-plugin/.claude-plugin/plugin.json
Normal file
11
skills-dev/executing-plans-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "executing-plans-plugin",
|
||||||
|
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "executing-plans",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
96
skills-dev/executing-plans-plugin/skills/SKILL.md
Normal file
96
skills-dev/executing-plans-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
name: executing-plans
|
||||||
|
description: Use when you have a written implementation plan to execute in a separate session with review checkpoints
|
||||||
|
---
|
||||||
|
|
||||||
|
# Executing Plans
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Load plan, review critically, create branch, execute tasks in batches, report for review between batches.
|
||||||
|
|
||||||
|
**Core principle:** Batch execution with checkpoints for architect review.
|
||||||
|
|
||||||
|
**Announce at start:** "I'm using the executing-plans skill to implement this plan."
|
||||||
|
|
||||||
|
## The Process
|
||||||
|
|
||||||
|
### Step 1: Load and Review Plan
|
||||||
|
1. Read plan file
|
||||||
|
2. Review critically - identify any questions or concerns about the plan
|
||||||
|
3. If concerns: Raise them with your human partner before starting
|
||||||
|
4. If no concerns: Proceed to branch setup
|
||||||
|
|
||||||
|
### Step 2: Setup Branch
|
||||||
|
**Before any implementation, ensure proper branch isolation.**
|
||||||
|
|
||||||
|
1. Check if already on a feature branch for this task
|
||||||
|
2. If not, use `/pr start` to create one:
|
||||||
|
```bash
|
||||||
|
/pr start <type> <REQ-id> <name>
|
||||||
|
# Example: /pr start feature REQ-123 user-login
|
||||||
|
```
|
||||||
|
3. If no REQ-id available, ask user or create branch manually:
|
||||||
|
```bash
|
||||||
|
git fetch origin
|
||||||
|
git checkout -b <type>/<descriptive-name> origin/main
|
||||||
|
```
|
||||||
|
4. Confirm branch is ready before proceeding
|
||||||
|
|
||||||
|
**Branch types:** `feature`, `fix`, `refactor`
|
||||||
|
|
||||||
|
### Step 3: Create Tasks and Execute Batch
|
||||||
|
**Default: First 3 tasks**
|
||||||
|
|
||||||
|
1. Create TodoWrite tasks from plan
|
||||||
|
2. For each task in batch:
|
||||||
|
- Mark as in_progress
|
||||||
|
- Follow each step exactly (plan has bite-sized steps)
|
||||||
|
- Run verifications as specified
|
||||||
|
- Mark as completed
|
||||||
|
|
||||||
|
### Step 4: Report
|
||||||
|
When batch complete:
|
||||||
|
- Show what was implemented
|
||||||
|
- Show verification output
|
||||||
|
- Say: "Ready for feedback."
|
||||||
|
|
||||||
|
### Step 5: Continue
|
||||||
|
Based on feedback:
|
||||||
|
- Apply changes if needed
|
||||||
|
- Execute next batch
|
||||||
|
- Repeat until complete
|
||||||
|
|
||||||
|
### Step 6: Complete Development
|
||||||
|
|
||||||
|
After all tasks complete and verified:
|
||||||
|
- Announce: "I'm using the finishing-a-development-branch skill to complete this work."
|
||||||
|
- **REQUIRED SUB-SKILL:** Use superpowers:finishing-a-development-branch
|
||||||
|
- Follow that skill to verify tests, present options, execute choice
|
||||||
|
|
||||||
|
## When to Stop and Ask for Help
|
||||||
|
|
||||||
|
**STOP executing immediately when:**
|
||||||
|
- Hit a blocker mid-batch (missing dependency, test fails, instruction unclear)
|
||||||
|
- Plan has critical gaps preventing starting
|
||||||
|
- You don't understand an instruction
|
||||||
|
- Verification fails repeatedly
|
||||||
|
|
||||||
|
**Ask for clarification rather than guessing.**
|
||||||
|
|
||||||
|
## When to Revisit Earlier Steps
|
||||||
|
|
||||||
|
**Return to Review (Step 1) when:**
|
||||||
|
- Partner updates the plan based on your feedback
|
||||||
|
- Fundamental approach needs rethinking
|
||||||
|
|
||||||
|
**Don't force through blockers** - stop and ask.
|
||||||
|
|
||||||
|
## Remember
|
||||||
|
- Review plan critically first
|
||||||
|
- **Create feature branch before implementation**
|
||||||
|
- Follow plan steps exactly
|
||||||
|
- Don't skip verifications
|
||||||
|
- Reference skills when plan says to
|
||||||
|
- Between batches: just report and wait
|
||||||
|
- Stop when blocked, don't guess
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "finishing-branch-plugin",
|
||||||
|
"description": "Use when implementation is complete and all tests pass - verifies and creates PR.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "finishing-a-development-branch",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
104
skills-dev/finishing-branch-plugin/skills/SKILL.md
Normal file
104
skills-dev/finishing-branch-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
---
|
||||||
|
name: finishing-a-development-branch
|
||||||
|
description: Use when implementation is complete and all tests pass - verifies and creates PR
|
||||||
|
---
|
||||||
|
|
||||||
|
# Finishing a Development Branch
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Verify tests pass, then push and create PR.
|
||||||
|
|
||||||
|
**Core principle:** Verify tests → Create PR → Done.
|
||||||
|
|
||||||
|
**Announce at start:** "I'm using the finishing-a-development-branch skill to complete this work."
|
||||||
|
|
||||||
|
## The Process
|
||||||
|
|
||||||
|
### Step 1: Verify Tests
|
||||||
|
|
||||||
|
**Before creating PR, verify tests pass:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run project's test suite
|
||||||
|
npm test / cargo test / pytest / go test ./... / mvn test
|
||||||
|
```
|
||||||
|
|
||||||
|
**If tests fail:**
|
||||||
|
```
|
||||||
|
Tests failing (<N> failures). Must fix before completing:
|
||||||
|
|
||||||
|
[Show failures]
|
||||||
|
|
||||||
|
Cannot proceed with PR until tests pass.
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop. Fix tests first.
|
||||||
|
|
||||||
|
**If tests pass:** Continue to Step 2.
|
||||||
|
|
||||||
|
### Step 2: Push and Create PR
|
||||||
|
|
||||||
|
Use the `/pr create` command which will:
|
||||||
|
1. **Check for existing PR first** - avoids duplicates
|
||||||
|
2. If PR exists: Report existing PR URL and skip
|
||||||
|
3. If no PR: Analyze commits, generate title/description, push, create PR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/pr create
|
||||||
|
```
|
||||||
|
|
||||||
|
**Duplicate prevention:** The `/pr create` command checks for existing open PRs on the current branch before creating a new one.
|
||||||
|
|
||||||
|
Report the PR URL when complete (whether existing or newly created).
|
||||||
|
|
||||||
|
### Step 3: Cleanup Worktree (if applicable)
|
||||||
|
|
||||||
|
Check if working in a worktree:
|
||||||
|
```bash
|
||||||
|
git worktree list | grep $(git branch --show-current)
|
||||||
|
```
|
||||||
|
|
||||||
|
If yes, ask user:
|
||||||
|
```
|
||||||
|
Worktree at <path>. Remove it now? (y/n)
|
||||||
|
```
|
||||||
|
|
||||||
|
If confirmed:
|
||||||
|
```bash
|
||||||
|
git worktree remove <worktree-path>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```
|
||||||
|
Tests Pass?
|
||||||
|
↓ yes
|
||||||
|
/pr create
|
||||||
|
↓
|
||||||
|
PR URL returned
|
||||||
|
↓
|
||||||
|
Cleanup worktree (optional)
|
||||||
|
↓
|
||||||
|
Done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Red Flags
|
||||||
|
|
||||||
|
**Never:**
|
||||||
|
- Create PR with failing tests
|
||||||
|
- Skip test verification
|
||||||
|
- Force-push without explicit request
|
||||||
|
|
||||||
|
**Always:**
|
||||||
|
- Verify tests before creating PR
|
||||||
|
- Use `/pr create` for consistent PR format
|
||||||
|
- Report the PR URL
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
**Called by:**
|
||||||
|
- **executing-plans** (Step 6) - After all batches complete
|
||||||
|
|
||||||
|
**Uses:**
|
||||||
|
- **/pr create** - For pushing and PR creation
|
||||||
11
skills-dev/frontend-design-plugin/.claude-plugin/plugin.json
Normal file
11
skills-dev/frontend-design-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend-design-plugin",
|
||||||
|
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "frontend-design",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
|
}
|
||||||
695
skills-dev/frontend-design-plugin/skills/SKILL.md
Normal file
695
skills-dev/frontend-design-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,695 @@
|
|||||||
|
---
|
||||||
|
name: frontend-design
|
||||||
|
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
|
||||||
|
arguments: [component|page|storybook] <description>
|
||||||
|
---
|
||||||
|
|
||||||
|
# Frontend Design 前端设计技能
|
||||||
|
|
||||||
|
创建高质量、有设计感的前端界面和组件,支持 Storybook 组件开发。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 命令格式
|
||||||
|
|
||||||
|
| 命令 | 功能 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| `/frontend-design component <描述>` | 创建 React/Vue 组件 | `/frontend-design component 产品卡片` |
|
||||||
|
| `/frontend-design page <描述>` | 创建完整页面 | `/frontend-design page 登录页` |
|
||||||
|
| `/frontend-design storybook <描述>` | 创建带 Storybook 的组件 | `/frontend-design storybook 按钮组件` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 设计原则
|
||||||
|
|
||||||
|
### 1. 设计思维先行
|
||||||
|
|
||||||
|
在编码前,明确以下问题:
|
||||||
|
|
||||||
|
- **目的**:这个界面解决什么问题?谁在使用?
|
||||||
|
- **调性**:选择一个明确的美学方向
|
||||||
|
- **差异化**:什么让这个设计令人难忘?
|
||||||
|
|
||||||
|
### 2. 美学方向选择
|
||||||
|
|
||||||
|
| 风格 | 特点 | 适用场景 |
|
||||||
|
|------|------|----------|
|
||||||
|
| 极简主义 | 大量留白、精炼元素 | 工具类、专业平台 |
|
||||||
|
| 现代商务 | 清晰层次、专业配色 | 企业官网、B2B |
|
||||||
|
| 活力年轻 | 鲜艳色彩、动感动画 | 消费品、社交 |
|
||||||
|
| 奢华精致 | 深色调、金属质感 | 高端品牌、金融 |
|
||||||
|
| 自然有机 | 柔和曲线、自然色系 | 健康、环保 |
|
||||||
|
| 复古怀旧 | 经典字体、做旧质感 | 文化、艺术 |
|
||||||
|
| 未来科技 | 渐变、玻璃拟态 | 科技、创新 |
|
||||||
|
|
||||||
|
### 3. 避免的设计陷阱
|
||||||
|
|
||||||
|
**禁止使用**:
|
||||||
|
- 过度使用的字体:Inter、Roboto、Arial
|
||||||
|
- 陈词滥调的配色:紫色渐变白底
|
||||||
|
- 千篇一律的布局
|
||||||
|
- 缺乏个性的通用组件
|
||||||
|
|
||||||
|
**应该追求**:
|
||||||
|
- 独特的字体组合
|
||||||
|
- 有意图的配色方案
|
||||||
|
- 打破常规的布局
|
||||||
|
- 有记忆点的细节
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Storybook 组件开发
|
||||||
|
|
||||||
|
### 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── components/
|
||||||
|
│ ├── Button/
|
||||||
|
│ │ ├── Button.tsx
|
||||||
|
│ │ ├── Button.stories.tsx
|
||||||
|
│ │ ├── Button.module.css
|
||||||
|
│ │ └── index.ts
|
||||||
|
│ ├── Card/
|
||||||
|
│ │ ├── Card.tsx
|
||||||
|
│ │ ├── Card.stories.tsx
|
||||||
|
│ │ ├── Card.module.css
|
||||||
|
│ │ └── index.ts
|
||||||
|
│ └── index.ts
|
||||||
|
├── styles/
|
||||||
|
│ ├── variables.css
|
||||||
|
│ ├── typography.css
|
||||||
|
│ └── animations.css
|
||||||
|
└── .storybook/
|
||||||
|
├── main.ts
|
||||||
|
└── preview.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### 组件模板
|
||||||
|
|
||||||
|
#### 1. 组件文件 (Component.tsx)
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import styles from './Component.module.css';
|
||||||
|
|
||||||
|
export interface ComponentProps {
|
||||||
|
/** 组件变体 */
|
||||||
|
variant?: 'primary' | 'secondary' | 'outline';
|
||||||
|
/** 尺寸 */
|
||||||
|
size?: 'sm' | 'md' | 'lg';
|
||||||
|
/** 是否禁用 */
|
||||||
|
disabled?: boolean;
|
||||||
|
/** 子元素 */
|
||||||
|
children: React.ReactNode;
|
||||||
|
/** 点击事件 */
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Component: React.FC<ComponentProps> = ({
|
||||||
|
variant = 'primary',
|
||||||
|
size = 'md',
|
||||||
|
disabled = false,
|
||||||
|
children,
|
||||||
|
onClick,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${styles.component} ${styles[variant]} ${styles[size]}`}
|
||||||
|
data-disabled={disabled}
|
||||||
|
onClick={disabled ? undefined : onClick}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Storybook Stories (Component.stories.tsx)
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { Component } from './Component';
|
||||||
|
|
||||||
|
const meta: Meta<typeof Component> = {
|
||||||
|
title: 'Components/Component',
|
||||||
|
component: Component,
|
||||||
|
tags: ['autodocs'],
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: '组件描述文档',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
variant: {
|
||||||
|
control: 'select',
|
||||||
|
options: ['primary', 'secondary', 'outline'],
|
||||||
|
description: '组件变体样式',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
control: 'radio',
|
||||||
|
options: ['sm', 'md', 'lg'],
|
||||||
|
description: '组件尺寸',
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: '是否禁用',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof Component>;
|
||||||
|
|
||||||
|
/** 默认状态 */
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
children: '默认组件',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 主要变体 */
|
||||||
|
export const Primary: Story = {
|
||||||
|
args: {
|
||||||
|
variant: 'primary',
|
||||||
|
children: '主要按钮',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 次要变体 */
|
||||||
|
export const Secondary: Story = {
|
||||||
|
args: {
|
||||||
|
variant: 'secondary',
|
||||||
|
children: '次要按钮',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 不同尺寸 */
|
||||||
|
export const Sizes: Story = {
|
||||||
|
render: () => (
|
||||||
|
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
||||||
|
<Component size="sm">小号</Component>
|
||||||
|
<Component size="md">中号</Component>
|
||||||
|
<Component size="lg">大号</Component>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 禁用状态 */
|
||||||
|
export const Disabled: Story = {
|
||||||
|
args: {
|
||||||
|
disabled: true,
|
||||||
|
children: '禁用状态',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 样式文件 (Component.module.css)
|
||||||
|
|
||||||
|
```css
|
||||||
|
.component {
|
||||||
|
/* 基础样式 */
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 变体 */
|
||||||
|
.primary {
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary:hover {
|
||||||
|
background: var(--color-primary-dark);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px var(--color-primary-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
background: var(--color-secondary);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline {
|
||||||
|
background: transparent;
|
||||||
|
border: 2px solid var(--color-border);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 尺寸 */
|
||||||
|
.sm {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md {
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg {
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态 */
|
||||||
|
[data-disabled="true"] {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 设计系统变量
|
||||||
|
|
||||||
|
### CSS 变量模板
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
/* 颜色 */
|
||||||
|
--color-primary: #0066ff;
|
||||||
|
--color-primary-dark: #0052cc;
|
||||||
|
--color-primary-light: #4d94ff;
|
||||||
|
--color-primary-shadow: rgba(0, 102, 255, 0.25);
|
||||||
|
|
||||||
|
--color-secondary: #f0f4f8;
|
||||||
|
--color-accent: #ff6b35;
|
||||||
|
|
||||||
|
--color-text: #1a1a2e;
|
||||||
|
--color-text-muted: #64748b;
|
||||||
|
--color-text-inverse: #ffffff;
|
||||||
|
|
||||||
|
--color-background: #ffffff;
|
||||||
|
--color-surface: #f8fafc;
|
||||||
|
--color-border: #e2e8f0;
|
||||||
|
|
||||||
|
--color-success: #10b981;
|
||||||
|
--color-warning: #f59e0b;
|
||||||
|
--color-error: #ef4444;
|
||||||
|
|
||||||
|
/* 字体 */
|
||||||
|
--font-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
|
||||||
|
--font-display: 'Clash Display', var(--font-sans);
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
|
||||||
|
/* 字号 */
|
||||||
|
--text-xs: 0.75rem;
|
||||||
|
--text-sm: 0.875rem;
|
||||||
|
--text-base: 1rem;
|
||||||
|
--text-lg: 1.125rem;
|
||||||
|
--text-xl: 1.25rem;
|
||||||
|
--text-2xl: 1.5rem;
|
||||||
|
--text-3xl: 2rem;
|
||||||
|
--text-4xl: 2.5rem;
|
||||||
|
|
||||||
|
/* 间距 */
|
||||||
|
--space-1: 0.25rem;
|
||||||
|
--space-2: 0.5rem;
|
||||||
|
--space-3: 0.75rem;
|
||||||
|
--space-4: 1rem;
|
||||||
|
--space-6: 1.5rem;
|
||||||
|
--space-8: 2rem;
|
||||||
|
--space-12: 3rem;
|
||||||
|
--space-16: 4rem;
|
||||||
|
|
||||||
|
/* 圆角 */
|
||||||
|
--radius-sm: 0.25rem;
|
||||||
|
--radius-md: 0.5rem;
|
||||||
|
--radius-lg: 1rem;
|
||||||
|
--radius-xl: 1.5rem;
|
||||||
|
--radius-full: 9999px;
|
||||||
|
|
||||||
|
/* 阴影 */
|
||||||
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||||
|
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||||
|
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
/* 动画 */
|
||||||
|
--duration-fast: 150ms;
|
||||||
|
--duration-normal: 300ms;
|
||||||
|
--duration-slow: 500ms;
|
||||||
|
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色主题 */
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--color-text: #f1f5f9;
|
||||||
|
--color-text-muted: #94a3b8;
|
||||||
|
--color-background: #0f172a;
|
||||||
|
--color-surface: #1e293b;
|
||||||
|
--color-border: #334155;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常用组件示例
|
||||||
|
|
||||||
|
### 1. 产品卡片 (ProductCard)
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// ProductCard.tsx
|
||||||
|
import React from 'react';
|
||||||
|
import styles from './ProductCard.module.css';
|
||||||
|
|
||||||
|
export interface ProductCardProps {
|
||||||
|
image: string;
|
||||||
|
title: string;
|
||||||
|
location: string;
|
||||||
|
rating: number;
|
||||||
|
reviewCount: number;
|
||||||
|
price: number;
|
||||||
|
originalPrice?: number;
|
||||||
|
tags?: string[];
|
||||||
|
onAddToCart?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProductCard: React.FC<ProductCardProps> = ({
|
||||||
|
image,
|
||||||
|
title,
|
||||||
|
location,
|
||||||
|
rating,
|
||||||
|
reviewCount,
|
||||||
|
price,
|
||||||
|
originalPrice,
|
||||||
|
tags = [],
|
||||||
|
onAddToCart,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<article className={styles.card}>
|
||||||
|
<div className={styles.imageWrapper}>
|
||||||
|
<img src={image} alt={title} className={styles.image} />
|
||||||
|
{tags.length > 0 && (
|
||||||
|
<div className={styles.tags}>
|
||||||
|
{tags.map((tag) => (
|
||||||
|
<span key={tag} className={styles.tag} data-tag={tag}>
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.content}>
|
||||||
|
<h3 className={styles.title}>{title}</h3>
|
||||||
|
<p className={styles.location}>📍 {location}</p>
|
||||||
|
|
||||||
|
<div className={styles.rating}>
|
||||||
|
<span className={styles.stars}>⭐ {rating.toFixed(1)}</span>
|
||||||
|
<span className={styles.reviewCount}>({reviewCount}条评价)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.priceRow}>
|
||||||
|
<div className={styles.price}>
|
||||||
|
<span className={styles.currency}>¥</span>
|
||||||
|
<span className={styles.amount}>{price}</span>
|
||||||
|
<span className={styles.suffix}>起</span>
|
||||||
|
</div>
|
||||||
|
{originalPrice && (
|
||||||
|
<span className={styles.originalPrice}>¥{originalPrice}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className={styles.addButton} onClick={onAddToCart}>
|
||||||
|
加入购物车
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// ProductCard.stories.tsx
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { ProductCard } from './ProductCard';
|
||||||
|
|
||||||
|
const meta: Meta<typeof ProductCard> = {
|
||||||
|
title: 'Components/ProductCard',
|
||||||
|
component: ProductCard,
|
||||||
|
tags: ['autodocs'],
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
backgrounds: {
|
||||||
|
default: 'light',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof ProductCard>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
image: 'https://images.unsplash.com/photo-1494947665470-20322015e3a8',
|
||||||
|
title: '袋鼠岛一日游',
|
||||||
|
location: '阿德莱德出发',
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 126,
|
||||||
|
price: 389,
|
||||||
|
tags: ['热卖', '含午餐'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithDiscount: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
originalPrice: 499,
|
||||||
|
tags: ['特惠', '限时'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Grid: Story = {
|
||||||
|
render: () => (
|
||||||
|
<div style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(3, 300px)',
|
||||||
|
gap: '1.5rem'
|
||||||
|
}}>
|
||||||
|
<ProductCard
|
||||||
|
image="https://images.unsplash.com/photo-1494947665470-20322015e3a8"
|
||||||
|
title="袋鼠岛一日游"
|
||||||
|
location="阿德莱德出发"
|
||||||
|
rating={4.8}
|
||||||
|
reviewCount={126}
|
||||||
|
price={389}
|
||||||
|
tags={['热卖']}
|
||||||
|
/>
|
||||||
|
<ProductCard
|
||||||
|
image="https://images.unsplash.com/photo-1506905925346-21bda4d32df4"
|
||||||
|
title="巴罗莎谷酒庄之旅"
|
||||||
|
location="阿德莱德出发"
|
||||||
|
rating={4.9}
|
||||||
|
reviewCount={89}
|
||||||
|
price={299}
|
||||||
|
originalPrice={399}
|
||||||
|
tags={['特惠', '含品酒']}
|
||||||
|
/>
|
||||||
|
<ProductCard
|
||||||
|
image="https://images.unsplash.com/photo-1540202403-b7abd6747a18"
|
||||||
|
title="海豚巡航体验"
|
||||||
|
location="格雷尔海滩"
|
||||||
|
rating={4.7}
|
||||||
|
reviewCount={234}
|
||||||
|
price={159}
|
||||||
|
tags={['亲子']}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 按钮组件 (Button)
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Button.tsx
|
||||||
|
import React from 'react';
|
||||||
|
import styles from './Button.module.css';
|
||||||
|
|
||||||
|
export interface ButtonProps {
|
||||||
|
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
|
||||||
|
size?: 'sm' | 'md' | 'lg';
|
||||||
|
fullWidth?: boolean;
|
||||||
|
loading?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
leftIcon?: React.ReactNode;
|
||||||
|
rightIcon?: React.ReactNode;
|
||||||
|
children: React.ReactNode;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Button: React.FC<ButtonProps> = ({
|
||||||
|
variant = 'primary',
|
||||||
|
size = 'md',
|
||||||
|
fullWidth = false,
|
||||||
|
loading = false,
|
||||||
|
disabled = false,
|
||||||
|
leftIcon,
|
||||||
|
rightIcon,
|
||||||
|
children,
|
||||||
|
onClick,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`
|
||||||
|
${styles.button}
|
||||||
|
${styles[variant]}
|
||||||
|
${styles[size]}
|
||||||
|
${fullWidth ? styles.fullWidth : ''}
|
||||||
|
`}
|
||||||
|
disabled={disabled || loading}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<span className={styles.spinner} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{leftIcon && <span className={styles.icon}>{leftIcon}</span>}
|
||||||
|
<span>{children}</span>
|
||||||
|
{rightIcon && <span className={styles.icon}>{rightIcon}</span>}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Storybook 配置
|
||||||
|
|
||||||
|
### .storybook/main.ts
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { StorybookConfig } from '@storybook/react-vite';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||||
|
addons: [
|
||||||
|
'@storybook/addon-links',
|
||||||
|
'@storybook/addon-essentials',
|
||||||
|
'@storybook/addon-interactions',
|
||||||
|
'@storybook/addon-a11y',
|
||||||
|
],
|
||||||
|
framework: {
|
||||||
|
name: '@storybook/react-vite',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
docs: {
|
||||||
|
autodocs: 'tag',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
### .storybook/preview.ts
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { Preview } from '@storybook/react';
|
||||||
|
import '../src/styles/variables.css';
|
||||||
|
import '../src/styles/typography.css';
|
||||||
|
|
||||||
|
const preview: Preview = {
|
||||||
|
parameters: {
|
||||||
|
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
backgrounds: {
|
||||||
|
default: 'light',
|
||||||
|
values: [
|
||||||
|
{ name: 'light', value: '#ffffff' },
|
||||||
|
{ name: 'gray', value: '#f8fafc' },
|
||||||
|
{ name: 'dark', value: '#0f172a' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
globalTypes: {
|
||||||
|
theme: {
|
||||||
|
description: 'Global theme for components',
|
||||||
|
defaultValue: 'light',
|
||||||
|
toolbar: {
|
||||||
|
title: 'Theme',
|
||||||
|
icon: 'circlehollow',
|
||||||
|
items: ['light', 'dark'],
|
||||||
|
dynamicTitle: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default preview;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速启动命令
|
||||||
|
|
||||||
|
### 创建新组件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建组件目录
|
||||||
|
mkdir -p src/components/ComponentName
|
||||||
|
|
||||||
|
# 创建文件
|
||||||
|
touch src/components/ComponentName/{ComponentName.tsx,ComponentName.stories.tsx,ComponentName.module.css,index.ts}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 安装 Storybook
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 初始化 Storybook
|
||||||
|
npx storybook@latest init
|
||||||
|
|
||||||
|
# 安装额外插件
|
||||||
|
npm install -D @storybook/addon-a11y @storybook/addon-interactions
|
||||||
|
|
||||||
|
# 启动 Storybook
|
||||||
|
npm run storybook
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 设计检查清单
|
||||||
|
|
||||||
|
### 组件质量检查
|
||||||
|
|
||||||
|
- [ ] Props 接口定义完整,带 JSDoc 注释
|
||||||
|
- [ ] 支持必要的变体(variant)和尺寸(size)
|
||||||
|
- [ ] 处理禁用和加载状态
|
||||||
|
- [ ] 支持自定义 className
|
||||||
|
- [ ] 键盘可访问性
|
||||||
|
- [ ] 屏幕阅读器友好
|
||||||
|
|
||||||
|
### Storybook 质量检查
|
||||||
|
|
||||||
|
- [ ] 所有变体都有对应 Story
|
||||||
|
- [ ] argTypes 配置完整
|
||||||
|
- [ ] 包含组件文档描述
|
||||||
|
- [ ] 交互状态可测试
|
||||||
|
- [ ] 响应式展示
|
||||||
|
|
||||||
|
### 视觉质量检查
|
||||||
|
|
||||||
|
- [ ] 字体选择有特色
|
||||||
|
- [ ] 配色方案协调
|
||||||
|
- [ ] 动画流畅自然
|
||||||
|
- [ ] 间距一致
|
||||||
|
- [ ] 暗色主题支持
|
||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "pull-request",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "review-checklist",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "dev"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "data-excel",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "integration"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,8 @@
|
|||||||
"name": "doubao-voice",
|
"name": "doubao-voice",
|
||||||
"path": "./skills/SKILL.md"
|
"path": "./skills/SKILL.md"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"install_name": "doubao-voice",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "integration"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "feishu-bitable",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "integration"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "feishu-docx",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "integration"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "feishu",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "integration"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "siyuan",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "integration"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,128 +5,35 @@ description: 思源笔记 API 集成。通过自然语言创建、编辑、搜
|
|||||||
|
|
||||||
# 思源笔记 API 集成 Skill
|
# 思源笔记 API 集成 Skill
|
||||||
|
|
||||||
## 服务配置
|
## 环境变量配置(必须)
|
||||||
|
|
||||||
### 阿里云生产环境 (推荐)
|
本 skill 通过环境变量读取思源笔记连接信息,不同用户/组织配置不同的值。
|
||||||
|
|
||||||
| 配置项 | 值 |
|
**在 `~/.claude/settings.json` 中配置:**
|
||||||
|--------|-----|
|
|
||||||
| 服务地址 | `https://siyuan.pipexerp.com` (HTTPS) |
|
|
||||||
| HTTP访问 | `http://47.93.23.182` (重定向到HTTPS) |
|
|
||||||
| SSH 别名 | `siyuan` |
|
|
||||||
| 访问码 | `SiYuan@2026` |
|
|
||||||
| 版本 | **3.1.11** (2026-02-16 升级) |
|
|
||||||
| 部署位置 | 阿里云 Ubuntu 24.04 (Docker) |
|
|
||||||
| 容器名称 | `siyuan` |
|
|
||||||
| 镜像 | `b3log/siyuan:v3.1.11` |
|
|
||||||
| 数据目录 | `/opt/siyuan/workspace` |
|
|
||||||
| 反向代理 | Caddy (HTTPS + SSL证书) |
|
|
||||||
| SSL证书 | Let's Encrypt (自动续期) |
|
|
||||||
|
|
||||||
### Tailscale 内网环境 (备用)
|
```json
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"SIYUAN_URL": "${SIYUAN_URL}",
|
||||||
|
"SIYUAN_TOKEN": "your-api-token-here"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
| 配置项 | 值 |
|
或在项目级 `.claude/settings.json` 中配置(优先级更高)。
|
||||||
|--------|-----|
|
|
||||||
| 服务地址 | `http://47.93.23.182` (Tailscale) |
|
**检查配置是否就绪:** 执行任何思源操作前,先确认环境变量已设置:
|
||||||
| 局域网地址 | `http://192.168.1.50:6806` |
|
|
||||||
| API Token | `nfnycjb1g8vbexb2` |
|
```bash
|
||||||
| 版本 | 3.1.5 |
|
echo "URL: ${SIYUAN_URL:-未配置}"
|
||||||
| 部署位置 | 飞牛OS (Docker) |
|
echo "TOKEN: ${SIYUAN_TOKEN:+已配置}"
|
||||||
|
```
|
||||||
|
|
||||||
|
如果未配置,提示用户在 `~/.claude/settings.json` 的 `env` 中添加 `SIYUAN_URL` 和 `SIYUAN_TOKEN`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 服务器管理 (阿里云)
|
## 🔐 保存<E4BF9D><E5AD98><EFBFBD>禁:敏感<E6958F><E6849F>息脱敏
|
||||||
|
|
||||||
### SSH 连接
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 快捷连接
|
|
||||||
ssh siyuan
|
|
||||||
|
|
||||||
# 完整连接命令
|
|
||||||
ssh -i /Users/donglinlai/Downloads/siyuan.pem root@47.93.23.182
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker 管理
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 查看容器状态
|
|
||||||
ssh siyuan "docker ps | grep siyuan"
|
|
||||||
|
|
||||||
# 查看日志
|
|
||||||
ssh siyuan "docker logs -f siyuan --tail 100"
|
|
||||||
|
|
||||||
# 重启服务
|
|
||||||
ssh siyuan "docker restart siyuan"
|
|
||||||
|
|
||||||
# 停止服务
|
|
||||||
ssh siyuan "docker stop siyuan"
|
|
||||||
|
|
||||||
# 启动服务
|
|
||||||
ssh siyuan "docker start siyuan"
|
|
||||||
|
|
||||||
# 升级到特定版本(推荐)
|
|
||||||
ssh siyuan "docker stop siyuan && docker rm siyuan"
|
|
||||||
ssh siyuan "docker pull b3log/siyuan:v3.1.11"
|
|
||||||
ssh siyuan "docker run -d --name siyuan --restart=always \
|
|
||||||
-p 127.0.0.1:6806:6806 \
|
|
||||||
-v /opt/siyuan/workspace:/siyuan/workspace \
|
|
||||||
-e LANG=en_US.UTF-8 \
|
|
||||||
b3log/siyuan:v3.1.11 \
|
|
||||||
--workspace=/siyuan/workspace \
|
|
||||||
--accessAuthCode=SiYuan@2026"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 数据备份与恢复
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 查看数据目录大小
|
|
||||||
ssh siyuan "du -sh /opt/siyuan/workspace"
|
|
||||||
|
|
||||||
# 备份数据到服务器
|
|
||||||
ssh siyuan "tar -czf /root/siyuan-backup-\$(date +%Y%m%d).tar.gz /opt/siyuan/workspace"
|
|
||||||
|
|
||||||
# 下载备份到本地
|
|
||||||
scp siyuan:/root/siyuan-backup-*.tar.gz ~/Downloads/
|
|
||||||
|
|
||||||
# 恢复数据(先停止容器)
|
|
||||||
ssh siyuan "docker stop siyuan && \
|
|
||||||
tar -xzf /root/siyuan-backup-YYYYMMDD.tar.gz -C / && \
|
|
||||||
docker start siyuan"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 访问测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 测试 HTTPS 访问
|
|
||||||
curl -I https://siyuan.pipexerp.com
|
|
||||||
|
|
||||||
# 测试认证 API
|
|
||||||
curl -s -X POST https://siyuan.pipexerp.com/api/system/version | jq .
|
|
||||||
|
|
||||||
# 浏览器访问
|
|
||||||
open https://siyuan.pipexerp.com
|
|
||||||
```
|
|
||||||
|
|
||||||
### Caddy 反向代理管理
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 查看 Caddy 状态
|
|
||||||
ssh siyuan "systemctl status caddy"
|
|
||||||
|
|
||||||
# 重启 Caddy
|
|
||||||
ssh siyuan "systemctl restart caddy"
|
|
||||||
|
|
||||||
# 查看 Caddy 配置
|
|
||||||
ssh siyuan "cat /etc/caddy/Caddyfile"
|
|
||||||
|
|
||||||
# 查看 SSL 证书
|
|
||||||
ssh siyuan "journalctl -u caddy | grep certificate"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔐 保存门禁:敏感信息脱敏
|
|
||||||
|
|
||||||
**保存任何内容到思源笔记前,必须先完成脱敏处理。**
|
**保存任何内容到思源笔记前,必须先完成脱敏处理。**
|
||||||
|
|
||||||
@@ -201,8 +108,8 @@ api.upsert_doc(notebook_id, "/文档路径", content)
|
|||||||
所有 API 请求使用 POST 方法,需携带 Token:
|
所有 API 请求使用 POST 方法,需携带 Token:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/xxx \
|
curl -X POST ${SIYUAN_URL}/api/xxx \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"param": "value"}'
|
-d '{"param": "value"}'
|
||||||
```
|
```
|
||||||
@@ -239,8 +146,8 @@ curl -X POST https://siyuan.pipexerp.com/api/xxx \
|
|||||||
#### 列出所有笔记本
|
#### 列出所有笔记本
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/notebook/lsNotebooks \
|
curl -X POST ${SIYUAN_URL}/api/notebook/lsNotebooks \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{}'
|
-d '{}'
|
||||||
```
|
```
|
||||||
@@ -248,8 +155,8 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/lsNotebooks \
|
|||||||
#### 创建笔记本
|
#### 创建笔记本
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/notebook/createNotebook \
|
curl -X POST ${SIYUAN_URL}/api/notebook/createNotebook \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"name": "笔记本名称"}'
|
-d '{"name": "笔记本名称"}'
|
||||||
```
|
```
|
||||||
@@ -257,8 +164,8 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/createNotebook \
|
|||||||
#### 删除笔记本
|
#### 删除笔记本
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/notebook/removeNotebook \
|
curl -X POST ${SIYUAN_URL}/api/notebook/removeNotebook \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"notebook": "笔记本ID"}'
|
-d '{"notebook": "笔记本ID"}'
|
||||||
```
|
```
|
||||||
@@ -276,22 +183,22 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/removeNotebook \
|
|||||||
```bash
|
```bash
|
||||||
# 1. 先查询是否存在(按路径精确匹配)
|
# 1. 先查询是否存在(按路径精确匹配)
|
||||||
DOC_PATH="/网络管理/家庭Tailscale网络"
|
DOC_PATH="/网络管理/家庭Tailscale网络"
|
||||||
EXISTING=$(curl -s -X POST https://siyuan.pipexerp.com/api/query/sql \
|
EXISTING=$(curl -s -X POST ${SIYUAN_URL}/api/query/sql \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"stmt\": \"SELECT id FROM blocks WHERE type='d' AND hpath='${DOC_PATH}' LIMIT 1\"}" \
|
-d "{\"stmt\": \"SELECT id FROM blocks WHERE type='d' AND hpath='${DOC_PATH}' LIMIT 1\"}" \
|
||||||
| jq -r '.data[0].id // empty')
|
| jq -r '.data[0].id // empty')
|
||||||
|
|
||||||
if [ -n "$EXISTING" ]; then
|
if [ -n "$EXISTING" ]; then
|
||||||
# 2a. 存在则更新
|
# 2a. 存在则更新
|
||||||
curl -s -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
|
curl -s -X POST ${SIYUAN_URL}/api/block/updateBlock \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"id\": \"${EXISTING}\", \"dataType\": \"markdown\", \"data\": \"# 更新的内容\"}"
|
-d "{\"id\": \"${EXISTING}\", \"dataType\": \"markdown\", \"data\": \"# 更新的内容\"}"
|
||||||
else
|
else
|
||||||
# 2b. 不存在则创建
|
# 2b. 不存在则创建
|
||||||
curl -s -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
|
curl -s -X POST ${SIYUAN_URL}/api/filetree/createDocWithMd \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"notebook": "笔记本ID", "path": "/网络管理/家庭Tailscale网络", "markdown": "# 新内容"}'
|
-d '{"notebook": "笔记本ID", "path": "/网络管理/家庭Tailscale网络", "markdown": "# 新内容"}'
|
||||||
fi
|
fi
|
||||||
@@ -300,8 +207,8 @@ fi
|
|||||||
#### 创建文档 (仅新建时使用)
|
#### 创建文档 (仅新建时使用)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
|
curl -X POST ${SIYUAN_URL}/api/filetree/createDocWithMd \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"notebook": "笔记本ID",
|
"notebook": "笔记本ID",
|
||||||
@@ -313,8 +220,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
|
|||||||
#### 获取文档内容
|
#### 获取文档内容
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/filetree/getDoc \
|
curl -X POST ${SIYUAN_URL}/api/filetree/getDoc \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"id": "文档ID"}'
|
-d '{"id": "文档ID"}'
|
||||||
```
|
```
|
||||||
@@ -322,8 +229,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/getDoc \
|
|||||||
#### 删除文档
|
#### 删除文档
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/filetree/removeDoc \
|
curl -X POST ${SIYUAN_URL}/api/filetree/removeDoc \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"notebook": "笔记本ID", "path": "/文档路径"}'
|
-d '{"notebook": "笔记本ID", "path": "/文档路径"}'
|
||||||
```
|
```
|
||||||
@@ -331,8 +238,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/removeDoc \
|
|||||||
#### 重命名文档
|
#### 重命名文档
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/filetree/renameDoc \
|
curl -X POST ${SIYUAN_URL}/api/filetree/renameDoc \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"notebook": "笔记本ID", "path": "/旧路径", "title": "新标题"}'
|
-d '{"notebook": "笔记本ID", "path": "/旧路径", "title": "新标题"}'
|
||||||
```
|
```
|
||||||
@@ -344,8 +251,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/renameDoc \
|
|||||||
#### 插入块
|
#### 插入块
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/block/insertBlock \
|
curl -X POST ${SIYUAN_URL}/api/block/insertBlock \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"dataType": "markdown",
|
"dataType": "markdown",
|
||||||
@@ -357,8 +264,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/insertBlock \
|
|||||||
#### 更新块
|
#### 更新块
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
|
curl -X POST ${SIYUAN_URL}/api/block/updateBlock \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"dataType": "markdown",
|
"dataType": "markdown",
|
||||||
@@ -370,8 +277,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
|
|||||||
#### 删除块
|
#### 删除块
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/block/deleteBlock \
|
curl -X POST ${SIYUAN_URL}/api/block/deleteBlock \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"id": "块ID"}'
|
-d '{"id": "块ID"}'
|
||||||
```
|
```
|
||||||
@@ -383,8 +290,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/deleteBlock \
|
|||||||
#### 全文搜索
|
#### 全文搜索
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/search/fullTextSearchBlock \
|
curl -X POST ${SIYUAN_URL}/api/search/fullTextSearchBlock \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"query": "搜索关键词",
|
"query": "搜索关键词",
|
||||||
@@ -395,8 +302,8 @@ curl -X POST https://siyuan.pipexerp.com/api/search/fullTextSearchBlock \
|
|||||||
#### SQL 查询
|
#### SQL 查询
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/query/sql \
|
curl -X POST ${SIYUAN_URL}/api/query/sql \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"stmt": "SELECT * FROM blocks WHERE content LIKE '\''%关键词%'\'' LIMIT 10"
|
"stmt": "SELECT * FROM blocks WHERE content LIKE '\''%关键词%'\'' LIMIT 10"
|
||||||
@@ -410,8 +317,8 @@ curl -X POST https://siyuan.pipexerp.com/api/query/sql \
|
|||||||
#### 导出 Markdown
|
#### 导出 Markdown
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST https://siyuan.pipexerp.com/api/export/exportMdContent \
|
curl -X POST ${SIYUAN_URL}/api/export/exportMdContent \
|
||||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"id": "文档ID"}'
|
-d '{"id": "文档ID"}'
|
||||||
```
|
```
|
||||||
@@ -421,13 +328,16 @@ curl -X POST https://siyuan.pipexerp.com/api/export/exportMdContent \
|
|||||||
## Python 封装
|
## Python 封装
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
import os
|
||||||
import requests
|
import requests
|
||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any
|
||||||
|
|
||||||
class SiYuanAPI:
|
class SiYuanAPI:
|
||||||
"""思源笔记 API 封装"""
|
"""思源笔记 API 封装"""
|
||||||
|
|
||||||
def __init__(self, base_url: str = "https://siyuan.pipexerp.com", token: str = "nfnycjb1g8vbexb2"):
|
def __init__(self, base_url: str = None, token: str = None):
|
||||||
|
base_url = base_url or os.environ.get("SIYUAN_URL", "")
|
||||||
|
token = token or os.environ.get("SIYUAN_TOKEN", "")
|
||||||
self.base_url = base_url
|
self.base_url = base_url
|
||||||
self.headers = {
|
self.headers = {
|
||||||
"Authorization": f"Token {token}",
|
"Authorization": f"Token {token}",
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "siyuan-to-feishu",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "integration"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "wecom",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "integration"
|
||||||
}
|
}
|
||||||
|
|||||||
11
skills-req/req-audit-plugin/.claude-plugin/plugin.json
Normal file
11
skills-req/req-audit-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "req-audit-plugin",
|
||||||
|
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "req-audit",
|
||||||
|
"install_type": "command",
|
||||||
|
"dir_category": "req"
|
||||||
|
}
|
||||||
105
skills-req/req-audit-plugin/skills/SKILL.md
Normal file
105
skills-req/req-audit-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
---
|
||||||
|
name: req-audit
|
||||||
|
description: 部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 部署后审计 (audit)
|
||||||
|
|
||||||
|
对本次部署执行三维度审计:运行时行为、代码缺陷、设计偏移。
|
||||||
|
|
||||||
|
## 执行流程
|
||||||
|
|
||||||
|
### 2a. 运行时检查
|
||||||
|
|
||||||
|
检查部署后是否有新增错误。
|
||||||
|
|
||||||
|
**优先 SSH**:
|
||||||
|
```bash
|
||||||
|
ssh -o ConnectTimeout=3 ${EC2_USER}@${EC2_HOST} \
|
||||||
|
"docker logs ${APP_CONTAINER} --since 10m 2>&1 | grep -i 'error\|panic\|fatal\|traceback'"
|
||||||
|
```
|
||||||
|
|
||||||
|
**降级 1:CI 日志**:
|
||||||
|
```bash
|
||||||
|
RUN_ID=$(gh run list --repo ${OWNER}/${REPO} --limit 1 --json databaseId -q '.[0].databaseId')
|
||||||
|
gh run view ${RUN_ID} --repo ${OWNER}/${REPO} --log 2>&1 | grep -i 'error\|panic\|fatal'
|
||||||
|
```
|
||||||
|
|
||||||
|
**降级 2:N/A + 警告**:
|
||||||
|
```
|
||||||
|
⚠️ 无法获取运行时日志(SSH 不可达 + CI 日志无异常信息),2a 标记为 N/A。
|
||||||
|
运行时问题可能未被发现,建议手动检查服务器日志。
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2b. 静态分析
|
||||||
|
|
||||||
|
调用现有 `/defect-analysis` command,传入变更文件:
|
||||||
|
|
||||||
|
```
|
||||||
|
对以下变更文件执行缺陷分析:
|
||||||
|
{变更文件列表}
|
||||||
|
|
||||||
|
重点关注:运行时行为(不是合并前 CR 的重复,而是部署后复查)
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:缺陷清单(按致命/高/中/低分级)
|
||||||
|
|
||||||
|
### 2c. 设计偏移检测
|
||||||
|
|
||||||
|
1. 读取需求的 PRD 文档(linkRole=prd 的任务文档)
|
||||||
|
2. 读取本次变更的源码
|
||||||
|
3. AI 对比分析:
|
||||||
|
|
||||||
|
```
|
||||||
|
请对比以下 PRD 功能点和实际代码实现:
|
||||||
|
|
||||||
|
PRD 功能点:
|
||||||
|
{从 PRD 提取的功能清单}
|
||||||
|
|
||||||
|
实际代码变更:
|
||||||
|
{变更文件的关键逻辑}
|
||||||
|
|
||||||
|
检查:
|
||||||
|
- 遗漏的功能(PRD 有但代码没实现)
|
||||||
|
- 多做的功能(代码有但 PRD 没提)
|
||||||
|
- 实现方式与 PRD 描述不一致
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:偏移项列表
|
||||||
|
|
||||||
|
### 合并报告
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 部署后审计报告
|
||||||
|
|
||||||
|
### 2a 运行时检查
|
||||||
|
| 检查项 | 结果 | 详情 |
|
||||||
|
| 新增错误 | ✅ 无 / ❌ 有 N 条 | ... |
|
||||||
|
|
||||||
|
### 2b 静态分析
|
||||||
|
| 缺陷 | 严重度 | 描述 |
|
||||||
|
(来自 defect-analysis 输出)
|
||||||
|
|
||||||
|
### 2c 设计偏移
|
||||||
|
| 偏移项 | 类型 | 说明 |
|
||||||
|
|
||||||
|
### 结论
|
||||||
|
- 致命/高级缺陷: N 个 → {阻断/通过}
|
||||||
|
- 中/低级缺陷: N 个 → 已创建 backlog
|
||||||
|
- 设计偏移: N 项 → {建议处理方式}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 缺陷分级处理
|
||||||
|
|
||||||
|
| 级别 | 处理 |
|
||||||
|
|------|------|
|
||||||
|
| 致命 | 阻断归档 + 回滚建议 + `ai-proj task create` 创建修复任务并关联需求 |
|
||||||
|
| 高级 | 阻断归档 + 回滚建议 + 创建修复任务 |
|
||||||
|
| 中级 | 警告不阻断 + 创建 backlog 任务 |
|
||||||
|
| 低级 | 记录到报告,不创建任务 |
|
||||||
|
|
||||||
|
## 任务关联
|
||||||
|
|
||||||
|
- linkRole: `code_review`
|
||||||
|
- 任务标题: `【审计】部署后审计: {需求标题}`
|
||||||
|
- 报告附加到任务文档
|
||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req-compare",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req-design",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,8 @@
|
|||||||
"deprecated": true,
|
"deprecated": true,
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req-dev",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
11
skills-req/req-lookback-plugin/.claude-plugin/plugin.json
Normal file
11
skills-req/req-lookback-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "req-lookback-plugin",
|
||||||
|
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "req-lookback",
|
||||||
|
"install_type": "command",
|
||||||
|
"dir_category": "req"
|
||||||
|
}
|
||||||
87
skills-req/req-lookback-plugin/skills/SKILL.md
Normal file
87
skills-req/req-lookback-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
name: req-lookback
|
||||||
|
description: 回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 回归测试 (lookback)
|
||||||
|
|
||||||
|
对本次部署变更的功能执行自动化验证。
|
||||||
|
|
||||||
|
## 执行流程
|
||||||
|
|
||||||
|
### 1. 获取变更文件列表
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 优先从最近合并的 PR 获取(覆盖 merge 多 commit 场景)
|
||||||
|
gh pr list --state merged --base main --limit 1 --json number,files
|
||||||
|
# 回退:git diff
|
||||||
|
git diff HEAD~1..HEAD --name-only
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 自动发现验证项(动态推断)
|
||||||
|
|
||||||
|
读取变更文件,按类型推断需要验证什么:
|
||||||
|
|
||||||
|
| 文件类型 | 推断方式 | 验证命令 |
|
||||||
|
|----------|---------|---------|
|
||||||
|
| `*.vue` / `*.tsx` | grep router 配置,提取对应路由路径 | `curl -sf -o /dev/null -w "%{http_code}" ${SERVER}${ROUTE}` |
|
||||||
|
| `app/api/*.py` | grep 路由装饰器 `@router.get/post`,提取 API 路径 | `curl -sf -o /dev/null -w "%{http_code}" ${SERVER}/api${PATH}` |
|
||||||
|
| `app/services/*.py` | 找到引用该 service 的 api 文件,提取关联 API | 同上 |
|
||||||
|
| `nginx.conf` | 提取 location 块 | `curl` 各路径检查状态码 |
|
||||||
|
| `docker-compose.yml` | 提取服务列表 | `docker ps` 检查容器状态 |
|
||||||
|
| `alembic/*.py` | 提取表名 | `psql -c "\d table_name"` |
|
||||||
|
| `*.md` / `*.txt` / `SKILL.md` | 纯文档 | **N/A(自动通过)** |
|
||||||
|
| `*.css` / `*.scss` | 纯样式 | 轻量验证:页面可达即可 |
|
||||||
|
|
||||||
|
### 3. 执行验证
|
||||||
|
|
||||||
|
**远端模式**(SSH 可达时):
|
||||||
|
```bash
|
||||||
|
# 3 秒超时检测
|
||||||
|
ssh -o ConnectTimeout=3 ${EC2_USER}@${EC2_HOST} "echo ok" 2>/dev/null
|
||||||
|
# 成功 → 执行完整验证(docker ps + curl + docker logs)
|
||||||
|
```
|
||||||
|
|
||||||
|
**降级模式**(SSH 不可达时):
|
||||||
|
```bash
|
||||||
|
# 检查最近 CI run 是否成功
|
||||||
|
gh run list --repo ${OWNER}/${REPO} --limit 1 --json conclusion
|
||||||
|
# conclusion=success → PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 输出报告
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 回归测试报告
|
||||||
|
|
||||||
|
### 变更范围
|
||||||
|
| 文件 | 类型 | 推断的验证项 |
|
||||||
|
|
||||||
|
### 测试结果
|
||||||
|
| 测试项 | 方式 | 预期 | 实际 | 状态 |
|
||||||
|
|
||||||
|
### 结论: ✅ PASS / ❌ FAIL / N/A
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 失败处理
|
||||||
|
|
||||||
|
```
|
||||||
|
⚠️ 回归测试失败:{失败项描述}
|
||||||
|
|
||||||
|
建议操作:
|
||||||
|
1. [回滚] git revert HEAD~1 + /req deploy(推荐,影响最小)
|
||||||
|
2. [修复] 创建 hotfix 分支修复后重新部署
|
||||||
|
3. [忽略] 标记为已知问题,继续归档
|
||||||
|
|
||||||
|
输入 1/2/3:
|
||||||
|
```
|
||||||
|
|
||||||
|
- 选 1 → 执行 `git revert`,提示用户运行 `/req deploy`
|
||||||
|
- 选 2 → 创建 hotfix 任务(ai-proj create_task),阻断归档
|
||||||
|
- 选 3 → 记录到报告,继续
|
||||||
|
|
||||||
|
## 任务关联
|
||||||
|
|
||||||
|
- linkRole: `test`
|
||||||
|
- 任务标题: `【回归】回归测试: {需求标题}`
|
||||||
|
- 报告附加到任务文档
|
||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req-prd",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,20 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
|||||||
- `req-compare` — 对比式 PRD 编写(系统平移/竞品借鉴时激活)
|
- `req-compare` — 对比式 PRD 编写(系统平移/竞品借鉴时激活)
|
||||||
- `req-prototype` — UI 原型生成
|
- `req-prototype` — UI 原型生成
|
||||||
|
|
||||||
|
## 客户原话原则(REQ-20260416-0017 P1-8)
|
||||||
|
|
||||||
|
**编写 PRD 时必须包含「客户原始诉求」章节(模板 1.4),保留客户/业务方原话,不做 AI 加工。**
|
||||||
|
|
||||||
|
**为什么**:
|
||||||
|
- 产品经理转述会失真(借鉴自 devflow-claude 的"客户场景"设计)
|
||||||
|
- 后续争议追溯时有据可查
|
||||||
|
- 团队成员看到原话能建立同理心
|
||||||
|
|
||||||
|
**填写规范**:
|
||||||
|
- 原话用 Markdown `> 引用块` 包裹,区分于 AI 加工内容
|
||||||
|
- 标注提出人、时间、出处(会议/邮件/聊天)
|
||||||
|
- 特殊约束(时间/合规/预算)必须保留
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 对比式 PRD 编写
|
## 对比式 PRD 编写
|
||||||
@@ -50,6 +64,20 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
|||||||
|------|--------|--------|----------|
|
|------|--------|--------|----------|
|
||||||
| ... | ... | ... | ... |
|
| ... | ... | ... | ... |
|
||||||
|
|
||||||
|
### 1.4 客户原始诉求 ⭐ 强制保留
|
||||||
|
|
||||||
|
> **重要**:记录客户/业务方提出需求时的**原始描述**,**不做 AI 加工、不总结、不转述**。
|
||||||
|
> 保留原话是为了后续溯源"我们当初为什么做这个"有据可查,避免产品经理转述失真。
|
||||||
|
|
||||||
|
- **场景1**(提出人:xxx / 时间:yyyy-mm-dd):
|
||||||
|
> "原始描述引用..."
|
||||||
|
- **场景2**:
|
||||||
|
> "原始描述引用..."
|
||||||
|
|
||||||
|
**补充信息**(可选):
|
||||||
|
- 会议/邮件/聊天记录链接
|
||||||
|
- 客户特殊约束(如"必须在 Q2 前上线")
|
||||||
|
|
||||||
## 2. 用户分析
|
## 2. 用户分析
|
||||||
### 2.1 目标用户
|
### 2.1 目标用户
|
||||||
[用户画像描述]
|
[用户画像描述]
|
||||||
@@ -108,12 +136,49 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
|||||||
### 6.2 灰度策略
|
### 6.2 灰度策略
|
||||||
[灰度发布计划]
|
[灰度发布计划]
|
||||||
|
|
||||||
## 7. 风险评估
|
## 7. 验收标准 ⭐ 强制包含 VP 三件套
|
||||||
|
|
||||||
|
> **规则(源自 REQ-20260421-0002)**:每条 AC 必须附带 VP-Data / VP-Steps / VP-Pass,缺一项评审不通过。
|
||||||
|
|
||||||
|
### AC1: [验收条件标题]
|
||||||
|
|
||||||
|
**目标**:[一句话描述期望结果]
|
||||||
|
|
||||||
|
**VP-Data(前置测试数据)**:
|
||||||
|
- 环境:localhost / production(二选一,明确注明)
|
||||||
|
- 数据:[字段、值、状态,例如:需求状态=approved,关联任务 3 个,task_project_id 非空]
|
||||||
|
- 建数据方式:[curl localhost:8080/... 或 MCP 工具,禁止混用]
|
||||||
|
|
||||||
|
**VP-Steps(验证步骤)**:
|
||||||
|
1. [工具 + 操作,例如:agent-browser open http://localhost:3000/xxx]
|
||||||
|
2. [检查指标,例如:eval `document.querySelector('.xxx').textContent`]
|
||||||
|
3. [确认值,例如:返回值包含"期望字符串"]
|
||||||
|
|
||||||
|
**VP-Pass(通过判定)**:
|
||||||
|
- ✅ [具体期望值,例如:eval 返回数组长度 = 3]
|
||||||
|
- ✅ [第二个判定条件]
|
||||||
|
- ❌ [明确的不通过条件,例如:仅靠代码分析得出结论 = 不通过]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### AC2: [第二条验收条件]
|
||||||
|
|
||||||
|
**目标**:...
|
||||||
|
|
||||||
|
**VP-Data**:...
|
||||||
|
|
||||||
|
**VP-Steps**:...
|
||||||
|
|
||||||
|
**VP-Pass**:
|
||||||
|
- ✅ ...
|
||||||
|
- ❌ ...
|
||||||
|
|
||||||
|
## 8. 风险评估
|
||||||
| 风险 | 影响 | 概率 | 应对措施 |
|
| 风险 | 影响 | 概率 | 应对措施 |
|
||||||
|------|------|------|----------|
|
|------|------|------|----------|
|
||||||
| ... | 高/中/低 | 高/中/低 | ... |
|
| ... | 高/中/低 | 高/中/低 | ... |
|
||||||
|
|
||||||
## 8. 附录
|
## 9. 附录
|
||||||
- 相关文档链接
|
- 相关文档链接
|
||||||
- 参考资料
|
- 参考资料
|
||||||
```
|
```
|
||||||
@@ -144,6 +209,81 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 需求粒度判断(REQ-20260416-0017 P1-12)
|
||||||
|
|
||||||
|
**创建需求前,AI 必须先对标题做粒度预判。** 借鉴 devflow-claude `/req:split`。
|
||||||
|
|
||||||
|
### 核心问题
|
||||||
|
|
||||||
|
> **"这个需求完成后,用户能感知到一个完整的功能变化吗?"**
|
||||||
|
> - 能 → 粒度合适
|
||||||
|
> - 不能(太大或太小)→ 需调整
|
||||||
|
|
||||||
|
### 粒度参考表
|
||||||
|
|
||||||
|
| 标题示例 | 粒度 | 建议 |
|
||||||
|
|---------|------|------|
|
||||||
|
| "用户积分系统"(含规则+查询+兑换+排行) | 太大 | 拆为 4 个需求 |
|
||||||
|
| "用户积分-积分规则管理"(含 CRUD+校验) | 合适 | 直接创建 |
|
||||||
|
| "用户积分-新增积分接口"(仅一个 API) | 太小 | 合并到功能级需求,或用任务(task) |
|
||||||
|
| "用户积分-新增 model 层"(按技术层拆) | 错误 | 按功能拆,不按技术层拆 |
|
||||||
|
|
||||||
|
### AI 自动检测规则
|
||||||
|
|
||||||
|
**标题过宽信号**(建议拆分):
|
||||||
|
- 含"系统"/"模块"/"平台"/"管理"等宏观词
|
||||||
|
- 描述中功能点 > 5 个
|
||||||
|
- 预估涉及文件 > 15 个
|
||||||
|
|
||||||
|
**标题过窄信号**(建议合并或改 task):
|
||||||
|
- 含"新增XX接口"/"修改XX字段"/"加一个按钮"
|
||||||
|
- 单个 CRUD 操作
|
||||||
|
- 预估涉及文件 ≤ 2 个
|
||||||
|
|
||||||
|
**错误拆分信号**(按技术层拆了):
|
||||||
|
- 标题含"model 层"/"service 层"/"handler 层"/"前端样式"
|
||||||
|
- 同一业务被拆为"后端接口"和"前端页面"两个独立需求
|
||||||
|
|
||||||
|
### 执行时机
|
||||||
|
|
||||||
|
1. **创建需求时**(`/req new` 或 `create_requirement`):检查标题,给出建议
|
||||||
|
2. **编辑需求时**:功能清单超 8 项时提醒"是否应拆分"
|
||||||
|
3. **独立评估**:用 `/req split <标题>` 预判粒度
|
||||||
|
|
||||||
|
### 三种输出
|
||||||
|
|
||||||
|
1. **粒度合适** → 正常创建
|
||||||
|
2. **建议拆分** → 列出子功能建议,用户确认后批量创建
|
||||||
|
3. **建议改 task/QUICK** → 提示"这个用任务更合适"
|
||||||
|
|
||||||
|
### 已有需求扩展功能的决策
|
||||||
|
|
||||||
|
> **"去掉这个新功能点,原需求还能独立交付吗?"**
|
||||||
|
> - 能 → 新建需求
|
||||||
|
> - 不能 → 修改原需求(`/req edit`)
|
||||||
|
|
||||||
|
| 场景 | 建议 |
|
||||||
|
|------|------|
|
||||||
|
| 新功能是原需求的自然延伸 | 修改原需求 |
|
||||||
|
| 新功能可独立上线 | 新建需求 |
|
||||||
|
| 原需求已完成/归档 | 必须新建 |
|
||||||
|
| 原需求开发中,新增会影响已有代码 | 新建(防范围蔓延) |
|
||||||
|
|
||||||
|
### 前后端拆分规则
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ 正确:
|
||||||
|
REQ-001 用户积分规则管理-后端(含 CRUD 全部接口)
|
||||||
|
REQ-002 用户积分规则管理-前端(含 CRUD 全部页面)
|
||||||
|
|
||||||
|
❌ 错误:
|
||||||
|
REQ-001 用户积分规则-新增接口
|
||||||
|
REQ-002 用户积分规则-查询接口
|
||||||
|
REQ-003 用户积分规则-修改接口
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 用户故事编写
|
## 用户故事编写
|
||||||
|
|
||||||
### 标准格式
|
### 标准格式
|
||||||
@@ -343,7 +483,7 @@ mcp__ai-proj__export_task_document_to_file
|
|||||||
- [ ] 背景与目标明确
|
- [ ] 背景与目标明确
|
||||||
- [ ] 用户群体定义清晰
|
- [ ] 用户群体定义清晰
|
||||||
- [ ] 功能需求完整
|
- [ ] 功能需求完整
|
||||||
- [ ] 验收标准可测试
|
- [ ] 验收标准可测试(每条 AC 附带 VP-Data / VP-Steps / VP-Pass)
|
||||||
- [ ] 异常情况已考虑
|
- [ ] 异常情况已考虑
|
||||||
- [ ] 性能要求已定义
|
- [ ] 性能要求已定义
|
||||||
- [ ] 上线计划合理
|
- [ ] 上线计划合理
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "req-prototype-plugin",
|
"name": "req-prototype-plugin",
|
||||||
"description": "Stitch 原型生成与迭代。基于 PRD 文档自动生成 UI 原型。",
|
"description": "原型生成与关联。支持 HTML 上传(/req prototype upload,iframe 嵌入详情页)和 Stitch AI 生成两种模式。",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req-prototype",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
---
|
---
|
||||||
name: req-prototype
|
name: req-prototype
|
||||||
description: Stitch 原型生成与迭代。基于 PRD 文档自动生成 UI 原型,支持编辑和变体生成。当执行 /req prototype 或需要生成界面原型时使用。
|
description: 原型生成与关联。支持两种模式:(1) Stitch AI 基于 PRD 自动生成 UI 原型截图;(2) AI 编写 HTML 原型并上传关联到需求详情页 iframe。当执行 /req prototype 或需要生成/上传界面原型时使用。
|
||||||
arguments: [REQ-ID] [--device desktop|mobile|tablet] [--model pro|flash] [--prompt "..."]
|
arguments: [REQ-ID] [upload|edit|variant] [--device desktop|mobile|tablet] [--model pro|flash] [--note "..."] [--prompt "..."]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Stitch 原型设计 Skill (req-prototype)
|
# 原型设计 Skill (req-prototype)
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
基于 PRD 文档自动生成 Stitch UI 原型,插入在 PRD 编写完成后、submit 评审前。
|
支持两种原型工作流:
|
||||||
|
|
||||||
**核心流程**:读取 PRD → 提取 UI 描述 → 转英文 prompt → 调用 Stitch API → 截图回填 PRD
|
| 模式 | 命令 | 适用场景 | 输出 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| **HTML 上传** | `/req prototype upload` | 快速展示、评审用静态原型 | iframe 嵌入需求详情页 |
|
||||||
|
| **Stitch AI** | `/req prototype` | 精细 UI 设计、多屏交互 | 截图回填 PRD 文档 |
|
||||||
|
|
||||||
## 前置条件
|
## 前置条件
|
||||||
|
|
||||||
@@ -18,13 +21,132 @@ arguments: [REQ-ID] [--device desktop|mobile|tablet] [--model pro|flash] [--prom
|
|||||||
|
|
||||||
| 检查项 | 方式 | 失败处理 |
|
| 检查项 | 方式 | 失败处理 |
|
||||||
|--------|------|----------|
|
|--------|------|----------|
|
||||||
| 需求存在 | `ai-proj req get --id <id>` | 报错:需求不存在 |
|
| 需求存在 | `mcp__ai-proj__get_requirement` | 报错:需求不存在 |
|
||||||
| PRD 文档存在 | `ai-proj req tasks --id <id>` 找 linkRole=prd 任务 + 检查文档 | 报错:请先执行 req-prd 编写 PRD |
|
| 后端运行中(upload 模式)| `curl http://localhost:8080/api/v1/health` | 报错:后端未启动 |
|
||||||
| PRD 包含 UI 描述 | 检查 PRD 中「功能需求」「交互设计」「界面原型」章节 | 警告:PRD 未包含 UI 相关描述,建议先补充 |
|
| PRD 文档存在(Stitch 模式)| 找 linkRole=prd 任务 + 检查文档 | 报错:请先执行 req-prd |
|
||||||
|
|
||||||
## 子命令
|
## 子命令
|
||||||
|
|
||||||
### 1. `/req prototype [REQ-ID]` — 生成原型
|
### 0. `/req prototype upload [REQ-ID] [--note "版本说明"]` — 上传 HTML 原型(**推荐**)
|
||||||
|
|
||||||
|
**适用场景**:快速为需求关联一个带样式的 HTML 原型,直接在需求详情页以 iframe 展示,供评审人预览交互流程。
|
||||||
|
|
||||||
|
**执行流程**:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 获取需求信息(mcp__ai-proj__get_requirement)
|
||||||
|
2. 读取 PRD 或需求描述,提炼 UI 关键信息
|
||||||
|
3. AI 编写带完整样式的 HTML 原型文件(见设计规范)
|
||||||
|
4. 保存到 /tmp/proto_<req_id>_<timestamp>.html
|
||||||
|
5. 获取本地 JWT token(登录 API)
|
||||||
|
6. 上传到后端(multipart POST)
|
||||||
|
7. 确认上传成功,输出预览 URL
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 5-6 执行方式**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 5. 获取 token(本地开发环境)
|
||||||
|
TOKEN=$(curl -s http://localhost:8080/api/v1/auth/login \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"username":"qiudl","password":"Admin@2026~"}' \
|
||||||
|
| python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["access_token"])')
|
||||||
|
|
||||||
|
# 6. 上传 HTML 原型并关联到需求
|
||||||
|
curl -s -X POST "http://localhost:8080/api/v1/requirements/<REQ_DB_ID>/prototype" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-F "file=@/tmp/proto_<req_id>_<timestamp>.html;type=text/html" \
|
||||||
|
-F "version_note=<--note 的值或空>"
|
||||||
|
```
|
||||||
|
|
||||||
|
**成功响应**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"url": "/api/v1/uploads/prototypes/proto_xxx.html",
|
||||||
|
"version_note": "...",
|
||||||
|
"uploaded_at": "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**效果**:需求详情页(`/requirements/<id>` 或 `/platform/requirements/<id>`)自动出现「原型预览」卡片,iframe 加载上传的 HTML。
|
||||||
|
|
||||||
|
**参数**:
|
||||||
|
|
||||||
|
| 参数 | 默认值 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| `--note` | 空 | 版本说明,如"初稿 v1"、"评审修改版" |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### HTML 原型设计规范
|
||||||
|
|
||||||
|
AI 生成的 HTML 原型必须满足以下要求:
|
||||||
|
|
||||||
|
**结构要求**:
|
||||||
|
- 完整独立的 HTML 文件(含 `<!DOCTYPE html>` + `<head>` + `<body>`)
|
||||||
|
- 所有样式内联在 `<style>` 标签中,不依赖外部 CDN
|
||||||
|
- 不使用 JavaScript 框架(纯 HTML+CSS,可用少量原生 JS)
|
||||||
|
- 适合在 600px 高度的 iframe 中展示
|
||||||
|
|
||||||
|
**视觉要求**:
|
||||||
|
- 与需求功能高度对应,体现核心交互流程
|
||||||
|
- 包含真实感数据(非"xxx"占位符)
|
||||||
|
- 顶部导航栏 / 侧边栏与项目风格一致(深色 header,现代 SaaS 风格)
|
||||||
|
- 底部加注释标注条(固定定位,说明版本和需求号)
|
||||||
|
|
||||||
|
**内容要求**:
|
||||||
|
- 覆盖需求描述中的核心功能点
|
||||||
|
- 展示关键数据状态(列表、表单、卡片等)
|
||||||
|
- 按钮/操作有视觉反馈样式(hover 色等)
|
||||||
|
|
||||||
|
**模板参考**(顶部 topbar + 侧边栏 + 主内容区):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>[需求标题] - 原型</title>
|
||||||
|
<style>
|
||||||
|
/* reset + layout */
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
background: #f0f2f5; color: #1a1a2e; min-height: 100vh; }
|
||||||
|
.topbar { background: #1e3a5f; color: #fff; padding: 0 24px; height: 52px;
|
||||||
|
display: flex; align-items: center; justify-content: space-between; }
|
||||||
|
.layout { display: flex; height: calc(100vh - 52px); }
|
||||||
|
.sidebar { width: 220px; background: #fff; border-right: 1px solid #e5e7eb;
|
||||||
|
padding: 16px 0; overflow-y: auto; }
|
||||||
|
.main { flex: 1; overflow-y: auto; padding: 24px; padding-bottom: 48px; }
|
||||||
|
/* 底部原型标注条 */
|
||||||
|
.annotation { position: fixed; bottom: 0; left: 0; right: 0;
|
||||||
|
background: rgba(30,58,95,.92); color: #fff;
|
||||||
|
padding: 6px 24px; font-size: 12px;
|
||||||
|
display: flex; justify-content: space-between; }
|
||||||
|
.annotation strong { color: #93c5fd; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="topbar"><!-- 导航 --></div>
|
||||||
|
<div class="layout">
|
||||||
|
<aside class="sidebar"><!-- 侧边栏 --></aside>
|
||||||
|
<main class="main"><!-- 主内容 --></main>
|
||||||
|
</div>
|
||||||
|
<div class="annotation">
|
||||||
|
<span>🎨 <strong>原型预览</strong> · [REQ-ID] — [需求标题]</span>
|
||||||
|
<span style="color:#93c5fd;">v1.0 · [日期] · 仅供评审参考</span>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1. `/req prototype [REQ-ID]` — Stitch AI 生成原型
|
||||||
|
|
||||||
**流程**:
|
**流程**:
|
||||||
|
|
||||||
@@ -154,6 +276,36 @@ generated_at: "<timestamp>"
|
|||||||
|
|
||||||
## 异常处理
|
## 异常处理
|
||||||
|
|
||||||
|
### Upload 模式
|
||||||
|
|
||||||
|
| 异常 | 处理 |
|
||||||
|
|------|------|
|
||||||
|
| 后端 500 / `column prototype_urls does not exist` | 需执行数据库迁移:`psql -U <owner> -d <db> -c "ALTER TABLE requirements ADD COLUMN IF NOT EXISTS prototype_urls JSONB DEFAULT '[]';"` |
|
||||||
|
| token 获取失败(401)| 检查用户名密码,或改用生产环境 token |
|
||||||
|
| 需求 ID 不存在(404)| 确认使用数据库自增 ID,而非 display_id(REQ-xxx) |
|
||||||
|
| HTML 文件超过 5MB | 精简样式或拆分多版本上传 |
|
||||||
|
| iframe 不显示 | 检查 `prototype_urls` 字段是否非空:`mcp__ai-proj__get_requirement` 确认 |
|
||||||
|
|
||||||
|
### 原型展示规则
|
||||||
|
|
||||||
|
**原型必须用 iframe 展示**(不得用截图、图片嵌入或内联 HTML 方式替代)。
|
||||||
|
|
||||||
|
- PRD 文档的「4.2 界面原型」章节:使用 `<iframe>` 标签嵌入原型 URL,而非 `` 图片
|
||||||
|
- 需求详情页原型预览卡片:后端渲染 iframe,前端不得将 `prototype_urls` 内容渲染为 `<img>`
|
||||||
|
- 典型正确写法(PRD 文档内):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<iframe src="/api/v1/uploads/prototypes/proto_xxx.html"
|
||||||
|
width="100%" height="600" frameborder="0"
|
||||||
|
style="border-radius:8px;border:1px solid #e5e7eb;">
|
||||||
|
</iframe>
|
||||||
|
```
|
||||||
|
|
||||||
|
> 背景:REQ-20260420-0031 反馈原型图用图片方式展示,无法交互预览,改为 iframe 后可正常使用。
|
||||||
|
|
||||||
|
|
||||||
|
### Stitch 模式
|
||||||
|
|
||||||
| 异常 | 处理 |
|
| 异常 | 处理 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 需求无 PRD 文档 | 报错:`请先使用 req-prd 技能编写 PRD 文档` |
|
| 需求无 PRD 文档 | 报错:`请先使用 req-prd 技能编写 PRD 文档` |
|
||||||
@@ -162,3 +314,15 @@ generated_at: "<timestamp>"
|
|||||||
| Stitch API 返回错误 | 展示错误信息,建议调整 prompt 或更换模型 |
|
| Stitch API 返回错误 | 展示错误信息,建议调整 prompt 或更换模型 |
|
||||||
| PRD 无「4.2 界面原型」章节 | 在「## 4. 交互设计」末尾自动追加该章节 |
|
| PRD 无「4.2 界面原型」章节 | 在「## 4. 交互设计」末尾自动追加该章节 |
|
||||||
| 已有原型元数据 | 询问:`已存在原型,是否覆盖?` |
|
| 已有原型元数据 | 询问:`已存在原型,是否覆盖?` |
|
||||||
|
|
||||||
|
## 版本管理
|
||||||
|
|
||||||
|
每次 `/req prototype upload` 都会追加一个新版本(自增 `version` 字段),需求详情页支持版本切换下拉框。多个版本并存时,最新版本默认展示。
|
||||||
|
|
||||||
|
可多次上传来迭代原型:
|
||||||
|
|
||||||
|
```
|
||||||
|
v1 → 初稿(评审前)
|
||||||
|
v2 → 评审修改版
|
||||||
|
v3 → 开发对齐版
|
||||||
|
```
|
||||||
|
|||||||
@@ -2,5 +2,10 @@
|
|||||||
"name": "req-research-plugin",
|
"name": "req-research-plugin",
|
||||||
"description": "需求调研插件。代码审计、数据库分析、现有功能调研。挂载在 analysis 阶段,需要深度调研时激活。",
|
"description": "需求调研插件。代码审计、数据库分析、现有功能调研。挂载在 analysis 阶段,需要深度调研时激活。",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": { "name": "qiudl" }
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "req-research",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
11
skills-req/req-retro-plugin/.claude-plugin/plugin.json
Normal file
11
skills-req/req-retro-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "req-retro-plugin",
|
||||||
|
"description": "复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "qiudl"
|
||||||
|
},
|
||||||
|
"install_name": "req-retro",
|
||||||
|
"install_type": "command",
|
||||||
|
"dir_category": "req"
|
||||||
|
}
|
||||||
150
skills-req/req-retro-plugin/skills/SKILL.md
Normal file
150
skills-req/req-retro-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
---
|
||||||
|
name: req-retro
|
||||||
|
description: 复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 复盘总结 (retro)
|
||||||
|
|
||||||
|
自动采集需求全生命周期数据,计算质量评分,识别跨需求模式,沉淀经验。
|
||||||
|
|
||||||
|
## 执行流程
|
||||||
|
|
||||||
|
### 1. 数据采集
|
||||||
|
|
||||||
|
| 数据 | 来源 | 方式 |
|
||||||
|
|------|------|------|
|
||||||
|
| 各阶段时间 | ai-proj | `mcp__ai-proj__get_requirement_history` |
|
||||||
|
| 任务完成情况 | ai-proj | `mcp__ai-proj__get_requirement_tasks` |
|
||||||
|
| CR 发现数 | CR 任务文档 | 从【代码评审】任务文档中提取缺陷计数 |
|
||||||
|
| 测试结果 | test 任务文档 | 从【测试】任务文档中提取通过/失败数 |
|
||||||
|
| audit 缺陷数 | 本次 audit 结果 | 从 audit 报告中提取 |
|
||||||
|
| git 统计 | git | `git log --stat` 提交数、变更行数 |
|
||||||
|
|
||||||
|
### 2. 质量评分(Quality Score)
|
||||||
|
|
||||||
|
```
|
||||||
|
QS = lookback_pass_rate × 0.3
|
||||||
|
+ audit_defect_score × 0.3
|
||||||
|
+ cr_density_score × 0.2
|
||||||
|
+ test_pass_rate × 0.2
|
||||||
|
|
||||||
|
audit_defect_score:
|
||||||
|
0 缺陷 = 100
|
||||||
|
每个低级 -5
|
||||||
|
每个中级 -20
|
||||||
|
每个高级 -60
|
||||||
|
致命 = 0
|
||||||
|
|
||||||
|
cr_density_score:
|
||||||
|
100 - (发现数 / 变更行数 × 1000)
|
||||||
|
下限 0,上限 100
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 历史趋势对比
|
||||||
|
|
||||||
|
读取 `memory/retro_metrics.md` 的明细数据:
|
||||||
|
|
||||||
|
```
|
||||||
|
本次 vs 近 10 次平均:
|
||||||
|
总耗时: 5h 44min vs avg 8h 12min (↓ 30% 改善)
|
||||||
|
质量分: 92 vs avg 85 (↑ 8% 提升)
|
||||||
|
缺陷数: 2 vs avg 3.2 (↓ 37% 改善)
|
||||||
|
变更行: 279 vs avg 450 (↓ 38%)
|
||||||
|
```
|
||||||
|
|
||||||
|
首次执行时显示:"首次复盘,无历史数据可对比。"
|
||||||
|
|
||||||
|
### 4. 跨需求模式识别
|
||||||
|
|
||||||
|
读取 retro_metrics.md 全部明细,AI 分析三类模式:
|
||||||
|
|
||||||
|
**耗时模式**:
|
||||||
|
- 按需求类型(前端/后端/混合/技能)分组统计平均耗时
|
||||||
|
- 识别异常:某次耗时是同类平均的 2 倍以上 → 标注原因
|
||||||
|
|
||||||
|
**缺陷热区**:
|
||||||
|
- 统计各文件在多次需求中的 audit 发现频率
|
||||||
|
- 同一文件 3 次以上出现 → 标记为热区
|
||||||
|
|
||||||
|
**改进验证**:
|
||||||
|
- 对比引入某流程/技能前后的质量分趋势
|
||||||
|
- 例:"引入 defect-analysis 后 5 次需求平均质量分从 72 → 88"
|
||||||
|
|
||||||
|
### 5. 自动更新技能
|
||||||
|
|
||||||
|
当模式识别发现高频缺陷类型时:
|
||||||
|
|
||||||
|
```
|
||||||
|
AI: "近 5 次需求中 3 次 audit 发现了 {缺陷类型}。
|
||||||
|
建议在 dev-review 的 CR 检查清单中增加:'{新检查项}'。"
|
||||||
|
|
||||||
|
用户确认?(y/n)
|
||||||
|
```
|
||||||
|
|
||||||
|
用户确认 → 自动在 `~/.claude/skills/dev-review/SKILL.md` 检查清单末尾追加该检查项。
|
||||||
|
|
||||||
|
### 6. 更新 retro_metrics.md
|
||||||
|
|
||||||
|
追加一行到明细:
|
||||||
|
```
|
||||||
|
| REQ-xxx | 2026-04-18 | 5h44m | 92 | 2 | 279 | 3 | frontend |
|
||||||
|
```
|
||||||
|
|
||||||
|
更新汇总:重新计算近 10 次平均值和趋势箭头(↑↓→)。
|
||||||
|
|
||||||
|
超过 30 条明细 → 最早的移入 `retro_metrics_archive.md`。
|
||||||
|
|
||||||
|
### 7. 生成文档
|
||||||
|
|
||||||
|
输出 `04-生命周期总结.md`,附加到【复盘】任务:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 生命周期总结 - REQ-xxx
|
||||||
|
|
||||||
|
## 质量评分: 92/100
|
||||||
|
|
||||||
|
## 时间线
|
||||||
|
| 阶段 | 进入时间 | 耗时 |
|
||||||
|
|------|---------|------|
|
||||||
|
| 创建 | 04-18 11:14 | - |
|
||||||
|
| PRD | 04-18 11:18 | 4min |
|
||||||
|
| 评审 | 04-18 11:19 | 1min |
|
||||||
|
| 开发 | 04-18 12:00 | 41min |
|
||||||
|
| 部署 | 04-18 16:58 | 4h58min |
|
||||||
|
| 总耗时 | | 5h 44min |
|
||||||
|
|
||||||
|
## 质量指标
|
||||||
|
| 指标 | 本次 | 近10次均 | 对比 |
|
||||||
|
|------|------|---------|------|
|
||||||
|
| 质量分 | 92 | 85 | ↑ |
|
||||||
|
| audit 缺陷 | 2(低) | 3.2 | ↓ |
|
||||||
|
| CR 发现 | 0 | 1.5 | ↓ |
|
||||||
|
| 测试通过率 | 100% | 95% | ↑ |
|
||||||
|
|
||||||
|
## git 统计
|
||||||
|
| 提交数 | 变更文件 | +行 | -行 |
|
||||||
|
|--------|---------|-----|-----|
|
||||||
|
| 3 | 1 | 279 | 2 |
|
||||||
|
|
||||||
|
## 模式识别
|
||||||
|
- {耗时/热区/改进 分析结果}
|
||||||
|
|
||||||
|
## 经验教训(写入 memory)
|
||||||
|
- {AI 提取的非显然教训}
|
||||||
|
|
||||||
|
## 技能更新
|
||||||
|
- {已更新/无更新}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. 写入 memory
|
||||||
|
|
||||||
|
将有价值的经验写入 memory:
|
||||||
|
- 类型:`feedback` 或 `project`
|
||||||
|
- 只写非显然的、可指导未来工作的教训
|
||||||
|
- 用户确认后写入
|
||||||
|
|
||||||
|
## 任务关联
|
||||||
|
|
||||||
|
- linkRole: `documentation`
|
||||||
|
- 任务标题: `【复盘】生命周期总结: {需求标题}`
|
||||||
|
- 04-生命周期总结.md 附加到任务文档
|
||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req-review",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ PRD 提交 → 结构检查 → 清晰度评估 → 技术可行性 → 数据
|
|||||||
| 功能描述 | ✓ | 详细功能需求 |
|
| 功能描述 | ✓ | 详细功能需求 |
|
||||||
| 数据模型 | ✓ | 数据库表结构 |
|
| 数据模型 | ✓ | 数据库表结构 |
|
||||||
| API 设计 | ✓ | RESTful 接口 |
|
| API 设计 | ✓ | RESTful 接口 |
|
||||||
| 验收标准 | ✓ | 验收条件 |
|
| 验收标准 | ✓ | 验收条件(每条 AC 必须附带 VP 三件套,见下方) |
|
||||||
|
| VP 三件套 | ✓ | 每条 AC 后必须有 VP-Data / VP-Steps / VP-Pass 三个子节 |
|
||||||
| 用户故事 | ○ | As a... I want... |
|
| 用户故事 | ○ | As a... I want... |
|
||||||
| 页面原型 | ○ | 如有 Stitch 原型则必审:布局合理性、与 PRD 描述一致性 |
|
| 页面原型 | ○ | 如有 Stitch 原型则必审:布局合理性、与 PRD 描述一致性 |
|
||||||
| 非功能需求 | ○ | 性能、安全 |
|
| 非功能需求 | ○ | 性能、安全 |
|
||||||
@@ -112,6 +113,19 @@ PRD 提交 → 结构检查 → 清晰度评估 → 技术可行性 → 数据
|
|||||||
【驳回人】xxx 【时间】2026-xx-xx
|
【驳回人】xxx 【时间】2026-xx-xx
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## VP 三件套强制检查(源自 REQ-20260421-0002)
|
||||||
|
|
||||||
|
**每条 AC 必须附带完整的 Verification Protocol,缺一项即驳回。**
|
||||||
|
|
||||||
|
| 检查项 | 要求 | 驳回条件 |
|
||||||
|
|--------|------|----------|
|
||||||
|
| VP-Data | 前置测试数据规格(环境/字段值/状态缺一不可) | 未注明在哪个环境建数据 |
|
||||||
|
| VP-Steps | 可重复执行的验证步骤(工具 + 操作 + 检查指标) | 步骤无法被第三方重复执行 |
|
||||||
|
| VP-Pass | 明确判定标准(含 ✅ 通过条件 + ❌ 不通过条件) | 只写"效果正确"之类模糊描述 |
|
||||||
|
| 环境隔离 | localhost 测试数据用 curl,生产数据用 MCP | 混用环境 |
|
||||||
|
|
||||||
|
**评审结论规则**:VP 三件套任何一项缺失 → 驳回,注明「AC N 缺少 VP-XXX,退回补充」。
|
||||||
|
|
||||||
## 常见驳回原因
|
## 常见驳回原因
|
||||||
|
|
||||||
| 类别 | 问题 | 建议 |
|
| 类别 | 问题 | 建议 |
|
||||||
@@ -121,4 +135,4 @@ PRD 提交 → 结构检查 → 清晰度评估 → 技术可行性 → 数据
|
|||||||
| 边界不清 | 缺异常处理 | 补充边界条件 |
|
| 边界不清 | 缺异常处理 | 补充边界条件 |
|
||||||
| 设计缺陷 | 模型/API 不合理 | 重新设计 |
|
| 设计缺陷 | 模型/API 不合理 | 重新设计 |
|
||||||
| 范围过大 | 难以实现 | 拆分为多需求 |
|
| 范围过大 | 难以实现 | 拆分为多需求 |
|
||||||
| 验收不明 | 缺验收标准 | 补充验收条件 |
|
| 验收不明 | 缺 AC 或 VP 三件套 | 补充验收条件和 VP |
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req-test-gate",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
}
|
},
|
||||||
|
"install_name": "req-workflow",
|
||||||
|
"install_type": "skill",
|
||||||
|
"dir_category": "req"
|
||||||
}
|
}
|
||||||
|
|||||||
119
update-plugin-meta.py
Normal file
119
update-plugin-meta.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Update all plugin.json files with install_name, install_type, and dir_category fields.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Mapping: plugin_dir_name -> (install_name, install_type, dir_category)
|
||||||
|
# install_type: "skill" -> ~/.claude/skills/<install_name>/
|
||||||
|
# "command" -> ~/.claude/commands/<install_name>.md
|
||||||
|
PLUGIN_MAP = {
|
||||||
|
# skills-biz
|
||||||
|
"biz-contract-plugin": ("biz-contract", "skill", "biz"),
|
||||||
|
"biz-ops-plugin": ("biz-ops", "skill", "biz"),
|
||||||
|
"biz-plan-plugin": ("biz-plan", "skill", "biz"),
|
||||||
|
"finance-plugin": ("finance", "skill", "biz"),
|
||||||
|
|
||||||
|
# skills-core
|
||||||
|
"ai-proj-plugin": ("ai-proj", "skill", "core"),
|
||||||
|
"pm-ask-plugin": ("pm-ask", "skill", "core"),
|
||||||
|
"pm-risk-plugin": ("pm-risk", "skill", "core"),
|
||||||
|
"publish-plugin": ("publish", "skill", "core"),
|
||||||
|
|
||||||
|
# skills-dev
|
||||||
|
"agent-browser-plugin": ("agent-browser", "skill", "dev"),
|
||||||
|
"agent-swarm-plugin": ("agent-swarm", "skill", "dev"),
|
||||||
|
"ai-chat-plugin": ("ai-chat", "skill", "dev"),
|
||||||
|
"db-migration-plugin": ("db-migration", "skill", "dev"),
|
||||||
|
"defect-analysis-plugin": ("defect-analysis", "command", "dev"),
|
||||||
|
"deploy-rollback-plugin": ("deploy-rollback", "skill", "dev"),
|
||||||
|
"dev-android-plugin": ("dev-android", "skill", "dev"),
|
||||||
|
"dev-arch-plugin": ("dev-arch", "skill", "dev"),
|
||||||
|
"dev-cicd-plugin": ("dev-cicd", "skill", "dev"),
|
||||||
|
"dev-coding-plugin": ("dev-coding", "skill", "dev"),
|
||||||
|
"dev-commit-plugin": ("dev-commit", "skill", "dev"),
|
||||||
|
"dev-deploy-plugin": ("dev-deploy", "skill", "dev"),
|
||||||
|
"dev-integration-plugin": ("dev-integration", "skill", "dev"),
|
||||||
|
"dev-ios-plugin": ("dev-ios", "skill", "dev"),
|
||||||
|
"dev-mcp-plugin": ("dev-mcp", "skill", "dev"),
|
||||||
|
"dev-pda-plugin": ("dev-pda", "skill", "dev"),
|
||||||
|
"dev-review-plugin": ("dev-review", "skill", "dev"),
|
||||||
|
"dev-scaffold-plugin": ("dev-scaffold", "skill", "dev"),
|
||||||
|
"dev-test-plugin": ("dev-test", "skill", "dev"),
|
||||||
|
"executing-plans-plugin": ("executing-plans", "skill", "dev"),
|
||||||
|
"finishing-branch-plugin": ("finishing-a-development-branch","skill", "dev"), # name mismatch!
|
||||||
|
"frontend-design-plugin": ("frontend-design", "skill", "dev"),
|
||||||
|
"pull-request-plugin": ("pull-request", "skill", "dev"),
|
||||||
|
"review-checklist-plugin": ("review-checklist", "skill", "dev"),
|
||||||
|
|
||||||
|
# skills-integration
|
||||||
|
"data-excel-plugin": ("data-excel", "skill", "integration"),
|
||||||
|
"doubao-voice-plugin": ("doubao-voice", "skill", "integration"),
|
||||||
|
"feishu-bitable-plugin": ("feishu-bitable", "skill", "integration"),
|
||||||
|
"feishu-docx-plugin": ("feishu-docx", "skill", "integration"),
|
||||||
|
"feishu-plugin": ("feishu", "skill", "integration"),
|
||||||
|
"siyuan-plugin": ("siyuan", "skill", "integration"),
|
||||||
|
"siyuan-to-feishu-plugin": ("siyuan-to-feishu", "skill", "integration"),
|
||||||
|
"wecom-plugin": ("wecom", "skill", "integration"),
|
||||||
|
|
||||||
|
# skills-personal
|
||||||
|
"gitea-plugin": ("gitea", "skill", "personal"),
|
||||||
|
"openclaw-plugin": ("openclaw", "skill", "personal"),
|
||||||
|
"ops-servers-plugin": ("ops-servers", "skill", "personal"),
|
||||||
|
"ops-tools-plugin": ("ops-tools", "skill", "personal"),
|
||||||
|
"qiudl-personal-plugin": ("qiudl-personal", "skill", "personal"),
|
||||||
|
"reload-session-plugin": ("reload-session", "skill", "personal"),
|
||||||
|
"req-deploy-plugin": ("req-deploy", "skill", "personal"),
|
||||||
|
"save-session-plugin": ("save-session", "skill", "personal"),
|
||||||
|
"search-sessions-plugin": ("search-sessions", "skill", "personal"),
|
||||||
|
|
||||||
|
# skills-req
|
||||||
|
"req-audit-plugin": ("req-audit", "command", "req"),
|
||||||
|
"req-compare-plugin": ("req-compare", "skill", "req"),
|
||||||
|
"req-design-plugin": ("req-design", "skill", "req"),
|
||||||
|
"req-dev-plugin": ("req-dev", "skill", "req"),
|
||||||
|
"req-lookback-plugin": ("req-lookback", "command", "req"),
|
||||||
|
"req-plugin": ("req", "skill", "req"),
|
||||||
|
"req-prd-plugin": ("req-prd", "skill", "req"),
|
||||||
|
"req-prototype-plugin": ("req-prototype", "skill", "req"),
|
||||||
|
"req-research-plugin": ("req-research", "skill", "req"),
|
||||||
|
"req-retro-plugin": ("req-retro", "command", "req"),
|
||||||
|
"req-review-plugin": ("req-review", "skill", "req"),
|
||||||
|
"req-test-gate-plugin": ("req-test-gate", "skill", "req"),
|
||||||
|
"req-workflow-plugin": ("req-workflow", "skill", "req"),
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
updated = 0
|
||||||
|
skipped = 0
|
||||||
|
missing = []
|
||||||
|
|
||||||
|
for plugin_name, (install_name, install_type, dir_category) in PLUGIN_MAP.items():
|
||||||
|
# Find the plugin directory
|
||||||
|
found = False
|
||||||
|
for cat_dir in os.listdir(BASE):
|
||||||
|
if not cat_dir.startswith("skills-"):
|
||||||
|
continue
|
||||||
|
plugin_dir = os.path.join(BASE, cat_dir, plugin_name)
|
||||||
|
if os.path.isdir(plugin_dir):
|
||||||
|
json_path = os.path.join(plugin_dir, ".claude-plugin", "plugin.json")
|
||||||
|
if os.path.exists(json_path):
|
||||||
|
with open(json_path) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
data["install_name"] = install_name
|
||||||
|
data["install_type"] = install_type
|
||||||
|
data["dir_category"] = dir_category
|
||||||
|
with open(json_path, "w") as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
f.write("\n")
|
||||||
|
print(f" ✓ {plugin_name} → {install_type}:{install_name}")
|
||||||
|
updated += 1
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
missing.append(plugin_name)
|
||||||
|
|
||||||
|
print(f"\nUpdated: {updated}, Missing plugin dirs: {len(missing)}")
|
||||||
|
if missing:
|
||||||
|
print("Missing:", missing)
|
||||||
Reference in New Issue
Block a user