大模型应用
把本地改修的 skill 目录作为 PR 贡献到…
当你想把本地修改过的 skills/<skill-name>/ 通过 PR 提交到 skills-lock.json 中记录的 upstream 仓库时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
把本地改修的 skill 目录作为 PR特定
解决思路
- Step 1 校验参数:取 SKILL_NAME=$ARGUMENTS;若为空则列出 skills/、.agents/skills/、.claude/skills/(仅实目录,symlink 由实体会在其它候选中出现)的候选并退出,提示用户选名后重跑。
- Step 1 在路径解析前用 ^[a-z][a-z0-9-]+$ 校验 SKILL_NAME 为小写 kebab-case,防止路径穿越。
- Step 1 定义 assert_no_symlink_components 函数:对相对路径 rel 的每个路径元素从头累积检查,任一元素为 symlink 则设置 SYMLINK_COMPONENT 并返回 1(fail-closed);可传 base 参数以 base 为基准检查。
- Step 1 若设置了环境变量 LOCAL_SKILL_DIR,则校验其必须为 skills/<name>、.agents/skills/<name>、.claude/skills/<name> 之一,且路径全元素(含中间父目录)非 symlink、目录存在,然后采用并跳过自动解析。
- Step 1 未设置 LOCAL_SKILL_DIR 时自动解析:分别检查 skills/<name>、.agents/skills/<name>、.claude/skills/<name>(均需全路径元素非 symlink)是否存在;若多个存在则中止并提示用 LOCAL_SKILL_DIR 指定;若恰好一个则采用;若都不存在则报错中止。
- Step 2 读取根目录 skills-lock.json,用 jq 取出 .skills["${SKILL_NAME}"].source 与 .sourceType。
- Step 2 安全阀:先把 source 规范化(https://github.com/* 转为 OWNER/REPO,其它形式原样),再统一去掉末尾 .git,得到 REPO_SLUG。
- Step 2 用 ^Fandhe-AI/[A-Za-z0-9._-]+$ 严格校验规范化后的 REPO_SLUG,不匹配则中止(拒绝路径穿越、查询串、fragment、多余路径段)。
- Step 2 显式拒绝 repo 名为 . 或 .. 的情况(如 Fandhe-AI/..git 去 .git 后会变成 Fandhe-AI/.)。
- Step 2 若 sourceType 不是 github 则中止(gh repo clone / gh pr create 无法正常工作)。
- Step 3 用 git log --oneline -- "${LOCAL_SKILL_DIR}/" 查看改动;用 git rev-parse --verify -q HEAD~1 判断是否有父提交,有则 git diff HEAD~1 HEAD -- "${LOCAL_SKILL_DIR}/",无则与空树 $(git hash-object -t tree /dev/null) 比较。
- Step 3 向用户确认「是否用此改修内容向 upstream 提 PR」。
- Step 4 做安全审查:认证/授权缺失、API key/密钥硬编码、XSS(含文档中的外部嵌入)、输入校验缺失、OWASP Top 10;发现问题则中止贡献并警告用户。
- Step 5 先捕获 ORIG_DIR="$(pwd)"(不要用 cd - 以免污染 stdout)。
- Step 5 解析本技能自身(contribute-skill)的安装位置 CONTRIBUTE_SKILL_DIR:若环境变量已设置,则用完全一致比较匹配 ${ORIG_DIR}/skills/contribute-skill、${ORIG_DIR}/.agents/skills/contribute-skill、${ORIG_DIR}/.claude/skills/contribute-skill 三者之一(不用参数展开去前缀,避免 glob 字符问题),并用 assert_no_symlink_components 校验路径全元素非 symlink、目录存在。
- Step 5 未设置 CONTRIBUTE_SKILL_DIR 时自动解析:检查上述三个候选(均需全路径元素非 symlink);多个存在则中止并提示用 CONTRIBUTE_SKILL_DIR 指定;恰好一个则采用;都不存在则报错中止。
- Step 5 回到 ORIG_DIR 后执行 SCRIPT_OUTPUT=$(LOCAL_SKILL_DIR="${LOCAL_SKILL_DIR}" "${CONTRIBUTE_SKILL_DIR}/scripts/skills-contribute.sh" "${SKILL_NAME}" "${REPO_SLUG}" | tee /dev/stderr),必须通过该脚本执行,不要手敲片段命令(脚本内含 rm -rf 前的 symlink 边界与 TOCTOU 校验)。
- Step 5 从脚本输出解析 CONTRIBUTE_SKILL_WORKDIR= 与 CONTRIBUTE_SKILL_UPSTREAM_PATH=,作为后续唯一权威值;校验 WORKDIR 存在且 UPSTREAM_SKILL_PATH 非空,否则报错中止;令 WORKDIR="$(dirname "${SCRIPT_UPSTREAM_DIR}")" 并 cd 到该目录。
- Step 5 用 DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || true) 取默认分支(加 || true 以兼容 set -e),缺省回退 main。
- Step 5(脚本内部逻辑)判定 upstream 中技能所在路径 UPSTREAM_SKILL_PATH:依次检查 skills/<name>、.agents/skills/<name>、.claude/skills/<name> 是否存在且全路径元素非 symlink;否则按父目录惯例 skills/ → .agents/skills/ → .claude/skills/ 判定;都不存在则警告并以 skills/<name> 作为新技能默认路径。
- Step 5(脚本内部逻辑)删除传播:cp -R 不反映删除,故先删目标再复制。删除前用 case 校验 UPSTREAM_SKILL_PATH 只能是 skills/<name>、.agents/skills/<name>、.claude/skills/<name> 三种形态之一,否则中止。
- Step 5(脚本内部逻辑)rm -rf 前再做实体路径校验:检查 clone 根到删除目标各中间路径元素非 symlink;用 pwd -P 确认删除目标的父目录真实路径在 clone 根之下;用 cd -P 进入已校验的父目录后以相对路径 rm 以关闭 TOCTOU;任一违反则中止。
- Step 5(脚本内部逻辑)删除目标始终限定在 ${WORKDIR}/upstream/ 内;随后 mkdir -p "${WORKDIR}/upstream/${UPSTREAM_SKILL_PATH}" 并 cp -R "${ORIG_DIR}/${LOCAL_SKILL_DIR}/." "${WORKDIR}/upstream/${UPSTREAM_SKILL_PATH}/"。
- Step 6 cd "$WORKDIR/upstream" 后执行 git status 与 git diff,把差异展示给用户确认是否符合预期。
- Step 7 用 SLUG=$(date +%Y%m%d-%H%M%S) 生成时间戳,git switch -c "contribute/${SKILL_NAME}-${SLUG}" 创建分支。
- Step 7 用 git add "${UPSTREAM_SKILL_PATH}/" 按路径添加(可包含 delete-then-copy 产生的删除 D),再以 Conventional Commits 格式提交(不加 --no-verify,不加 co-author)。
- Step 8 执行 git push -u origin "contribute/${SKILL_NAME}-${SLUG}"。
- Step 8 从贡献源仓库取 OWNER/REPO:SRC_REPO=$(git -C "${ORIG_DIR}" remote get-url origin 2>/dev/null | sed -E 's#^(git@github\.com:|https://github\.com/)##; s#\.git$##'),为空则回退 basename "${ORIG_DIR}"。
- Step 8 用 gh pr create --repo "${REPO_SLUG}" --base "${DEFAULT_BRANCH:-main}" --title ... --body ...(body 含 Summary / Source / Test plan,Source 中填入 SRC_REPO 实值,禁止硬编码仓库名);如需 Draft 加 --draft。
- Step 9 返回 PR URL 给用户,并提示「合并后执行 /sync-skills-lock 更新 skills-lock.json 的 computedHash」;在结果确定前保留 $WORKDIR。
- Step 9 验证:用 gh pr view --repo "${REPO_SLUG}" --web 或直接访问 URL,确认 PR 已创建、标题与差异符合预期、已输出 sync-skills-lock 提示。
- 注意事项:SKILL_NAME 仅允许 kebab-case;多个实体路径并存时中止而非静默优先 skills/;source 非 Fandhe-AI org 时中止;发现安全问题中止;upstream 路径按 clone 后仓库布局判定(不要用 skills-lock.json 的 skillPath);先删后拷以传播删除;同名分支冲突时询问用户。
- sandbox 环境下:本技能需要网络访问 GitHub(fork、git push、创建 PR),相关命令需按命令粒度禁用 sandbox 执行;无法解除网络隔离的环境不能运行。
适用边界 · 注意事项
- 未安装并认证 gh CLI,或对目标 org 没有 push / PR 权限时。
- 目标技能未登记在 skills-lock.json 中,或本地改修未提交、工作树不干净时。
- upstream 不是 Fandhe-AI org、sourceType 不是 github,或运行环境无法访问 GitHub 网络时。