feat(sync): add install-skills.sh + install metadata to all 62 plugins
- Add install_name, install_type, dir_category fields to all 62 plugin.json files to resolve name-mapping and skill-vs-command routing issues - Add install-skills.sh: idempotent cross-machine skill sync script - Routes skill→~/.claude/skills/<name>/, command→~/.claude/commands/<name>.md - rsync full skills/ directory (preserves multi-file skills like dev-test, req-deploy) - State file ~/.claude/.installed-skills.json tracks installed versions - Conflict detection: warns before overwriting locally modified files - --dry-run, --category, --force, --cleanup, --list flags - Add 9 new plugins migrated from local ~/.claude (agent-swarm, ai-chat, defect-analysis, executing-plans, finishing-branch, frontend-design, req-audit, req-lookback, req-retro) - Add update-plugin-meta.py helper used to bulk-update plugin.json - Fix siyuan SKILL.md: remove hardcoded server credentials, use env vars Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -71,6 +71,30 @@
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-dev/db-migration-plugin",
|
||||
@@ -83,6 +107,18 @@
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-dev/deploy-rollback-plugin",
|
||||
@@ -265,6 +301,43 @@
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-dev/pull-request-plugin",
|
||||
@@ -290,6 +363,19 @@
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-req/req-compare-plugin",
|
||||
@@ -329,6 +415,19 @@
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-req/req-plugin",
|
||||
@@ -381,6 +480,19 @@
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-req/req-review-plugin",
|
||||
@@ -597,6 +709,19 @@
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-personal/ops-tools-plugin",
|
||||
@@ -622,6 +747,19 @@
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-personal/req-deploy-plugin",
|
||||
@@ -634,6 +772,32 @@
|
||||
"operations"
|
||||
],
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
314
install-skills.sh
Executable file
314
install-skills.sh
Executable file
@@ -0,0 +1,314 @@
|
||||
#!/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
|
||||
}
|
||||
|
||||
# ── 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
|
||||
|
||||
# Perform install
|
||||
if [[ "$install_type" == "command" ]]; then
|
||||
# Single-file command → ~/.claude/commands/<name>.md
|
||||
local src_md="$skills_dir/SKILL.md"
|
||||
if [[ ! -f "$src_md" ]]; then
|
||||
warn "$install_name: skills/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: copy entire skills/ directory content preserving structure
|
||||
rsync -a --delete "$skills_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",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "biz-contract",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "biz-ops",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "biz-plan",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "finance",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "2.0.1",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "ai-proj",
|
||||
"install_type": "skill",
|
||||
"dir_category": "core"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "pm-ask",
|
||||
"install_type": "skill",
|
||||
"dir_category": "core"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "pm-risk",
|
||||
"install_type": "skill",
|
||||
"dir_category": "core"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "publish",
|
||||
"install_type": "skill",
|
||||
"dir_category": "core"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"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",
|
||||
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时激活。",
|
||||
"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",
|
||||
"description": "回滚方案插件。部署后发现问题时的回滚策略、数据修复、灰度回退。挂载在 deploy 阶段。",
|
||||
"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",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "dev-arch",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "dev-cicd",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "2.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "dev-coding",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "dev-commit",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "dev-deploy",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"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",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "dev-review",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,10 @@
|
||||
"name": "dev-scaffold-plugin",
|
||||
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架(Model/Repository/Service/Handler/Route)。挂载在 dev 阶段。",
|
||||
"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",
|
||||
"author": {
|
||||
"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",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "pull-request",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "review-checklist",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "data-excel",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
}
|
||||
|
||||
@@ -10,5 +10,8 @@
|
||||
"name": "doubao-voice",
|
||||
"path": "./skills/SKILL.md"
|
||||
}
|
||||
]
|
||||
],
|
||||
"install_name": "doubao-voice",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "feishu-bitable",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "feishu-docx",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.1.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "feishu",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "siyuan",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
}
|
||||
|
||||
@@ -5,128 +5,35 @@ description: 思源笔记 API 集成。通过自然语言创建、编辑、搜
|
||||
|
||||
# 思源笔记 API 集成 Skill
|
||||
|
||||
## 服务配置
|
||||
## 环境变量配置(必须)
|
||||
|
||||
### 阿里云生产环境 (推荐)
|
||||
本 skill 通过环境变量读取思源笔记连接信息,不同用户/组织配置不同的值。
|
||||
|
||||
| 配置项 | 值 |
|
||||
|--------|-----|
|
||||
| 服务地址 | `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 (自动续期) |
|
||||
**在 `~/.claude/settings.json` 中配置:**
|
||||
|
||||
### Tailscale 内网环境 (备用)
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"SIYUAN_URL": "${SIYUAN_URL}",
|
||||
"SIYUAN_TOKEN": "your-api-token-here"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 配置项 | 值 |
|
||||
|--------|-----|
|
||||
| 服务地址 | `http://47.93.23.182` (Tailscale) |
|
||||
| 局域网地址 | `http://192.168.1.50:6806` |
|
||||
| API Token | `nfnycjb1g8vbexb2` |
|
||||
| 版本 | 3.1.5 |
|
||||
| 部署位置 | 飞牛OS (Docker) |
|
||||
或在项目级 `.claude/settings.json` 中配置(优先级更高)。
|
||||
|
||||
**检查配置是否就绪:** 执行任何思源操作前,先确认环境变量已设置:
|
||||
|
||||
```bash
|
||||
echo "URL: ${SIYUAN_URL:-未配置}"
|
||||
echo "TOKEN: ${SIYUAN_TOKEN:+已配置}"
|
||||
```
|
||||
|
||||
如果未配置,提示用户在 `~/.claude/settings.json` 的 `env` 中添加 `SIYUAN_URL` 和 `SIYUAN_TOKEN`。
|
||||
|
||||
---
|
||||
|
||||
## 服务器管理 (阿里云)
|
||||
|
||||
### 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"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 保存门禁:敏感信息脱敏
|
||||
## 🔐 保存<E4BF9D><E5AD98><EFBFBD>禁:敏感<E6958F><E6849F>息脱敏
|
||||
|
||||
**保存任何内容到思源笔记前,必须先完成脱敏处理。**
|
||||
|
||||
@@ -201,8 +108,8 @@ api.upsert_doc(notebook_id, "/文档路径", content)
|
||||
所有 API 请求使用 POST 方法,需携带 Token:
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/xxx \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/xxx \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"param": "value"}'
|
||||
```
|
||||
@@ -239,8 +146,8 @@ curl -X POST https://siyuan.pipexerp.com/api/xxx \
|
||||
#### 列出所有笔记本
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/notebook/lsNotebooks \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/notebook/lsNotebooks \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
@@ -248,8 +155,8 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/lsNotebooks \
|
||||
#### 创建笔记本
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/notebook/createNotebook \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/notebook/createNotebook \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "笔记本名称"}'
|
||||
```
|
||||
@@ -257,8 +164,8 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/createNotebook \
|
||||
#### 删除笔记本
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/notebook/removeNotebook \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/notebook/removeNotebook \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"notebook": "笔记本ID"}'
|
||||
```
|
||||
@@ -276,22 +183,22 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/removeNotebook \
|
||||
```bash
|
||||
# 1. 先查询是否存在(按路径精确匹配)
|
||||
DOC_PATH="/网络管理/家庭Tailscale网络"
|
||||
EXISTING=$(curl -s -X POST https://siyuan.pipexerp.com/api/query/sql \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
EXISTING=$(curl -s -X POST ${SIYUAN_URL}/api/query/sql \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"stmt\": \"SELECT id FROM blocks WHERE type='d' AND hpath='${DOC_PATH}' LIMIT 1\"}" \
|
||||
| jq -r '.data[0].id // empty')
|
||||
|
||||
if [ -n "$EXISTING" ]; then
|
||||
# 2a. 存在则更新
|
||||
curl -s -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -s -X POST ${SIYUAN_URL}/api/block/updateBlock \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"id\": \"${EXISTING}\", \"dataType\": \"markdown\", \"data\": \"# 更新的内容\"}"
|
||||
else
|
||||
# 2b. 不存在则创建
|
||||
curl -s -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -s -X POST ${SIYUAN_URL}/api/filetree/createDocWithMd \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"notebook": "笔记本ID", "path": "/网络管理/家庭Tailscale网络", "markdown": "# 新内容"}'
|
||||
fi
|
||||
@@ -300,8 +207,8 @@ fi
|
||||
#### 创建文档 (仅新建时使用)
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/filetree/createDocWithMd \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"notebook": "笔记本ID",
|
||||
@@ -313,8 +220,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
|
||||
#### 获取文档内容
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/filetree/getDoc \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/filetree/getDoc \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"id": "文档ID"}'
|
||||
```
|
||||
@@ -322,8 +229,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/getDoc \
|
||||
#### 删除文档
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/filetree/removeDoc \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/filetree/removeDoc \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"notebook": "笔记本ID", "path": "/文档路径"}'
|
||||
```
|
||||
@@ -331,8 +238,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/removeDoc \
|
||||
#### 重命名文档
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/filetree/renameDoc \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/filetree/renameDoc \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"notebook": "笔记本ID", "path": "/旧路径", "title": "新标题"}'
|
||||
```
|
||||
@@ -344,8 +251,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/renameDoc \
|
||||
#### 插入块
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/block/insertBlock \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/block/insertBlock \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"dataType": "markdown",
|
||||
@@ -357,8 +264,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/insertBlock \
|
||||
#### 更新块
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/block/updateBlock \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"dataType": "markdown",
|
||||
@@ -370,8 +277,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
|
||||
#### 删除块
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/block/deleteBlock \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/block/deleteBlock \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"id": "块ID"}'
|
||||
```
|
||||
@@ -383,8 +290,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/deleteBlock \
|
||||
#### 全文搜索
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/search/fullTextSearchBlock \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/search/fullTextSearchBlock \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "搜索关键词",
|
||||
@@ -395,8 +302,8 @@ curl -X POST https://siyuan.pipexerp.com/api/search/fullTextSearchBlock \
|
||||
#### SQL 查询
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/query/sql \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/query/sql \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"stmt": "SELECT * FROM blocks WHERE content LIKE '\''%关键词%'\'' LIMIT 10"
|
||||
@@ -410,8 +317,8 @@ curl -X POST https://siyuan.pipexerp.com/api/query/sql \
|
||||
#### 导出 Markdown
|
||||
|
||||
```bash
|
||||
curl -X POST https://siyuan.pipexerp.com/api/export/exportMdContent \
|
||||
-H "Authorization: Token nfnycjb1g8vbexb2" \
|
||||
curl -X POST ${SIYUAN_URL}/api/export/exportMdContent \
|
||||
-H "Authorization: Token ${SIYUAN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"id": "文档ID"}'
|
||||
```
|
||||
@@ -421,13 +328,16 @@ curl -X POST https://siyuan.pipexerp.com/api/export/exportMdContent \
|
||||
## Python 封装
|
||||
|
||||
```python
|
||||
import os
|
||||
import requests
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
class SiYuanAPI:
|
||||
"""思源笔记 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.headers = {
|
||||
"Authorization": f"Token {token}",
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "siyuan-to-feishu",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"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",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "req-compare",
|
||||
"install_type": "skill",
|
||||
"dir_category": "req"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "2.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "req-design",
|
||||
"install_type": "skill",
|
||||
"dir_category": "req"
|
||||
}
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
"deprecated": true,
|
||||
"author": {
|
||||
"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",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "req",
|
||||
"install_type": "skill",
|
||||
"dir_category": "req"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "2.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "req-prd",
|
||||
"install_type": "skill",
|
||||
"dir_category": "req"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "2.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "req-prototype",
|
||||
"install_type": "skill",
|
||||
"dir_category": "req"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,10 @@
|
||||
"name": "req-research-plugin",
|
||||
"description": "需求调研插件。代码审计、数据库分析、现有功能调研。挂载在 analysis 阶段,需要深度调研时激活。",
|
||||
"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",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "req-review",
|
||||
"install_type": "skill",
|
||||
"dir_category": "req"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "req-test-gate",
|
||||
"install_type": "skill",
|
||||
"dir_category": "req"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"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