
Subtree Publish
- 50 installs
- 543 repo stars
- Updated August 5, 2026
- cat-xierluo/legal-skills
Pushes a monorepo subdirectory to a standalone GitHub repo via git subtree, using a registry manifest and automatic change detection for incremental pushes.
About
Pushes registered monorepo subdirectories to independent GitHub repos using git subtree push. Developers use it after committing changes to a registered subproject to sync the standalone repo with one command.
- Registry manifest with automatic change detection
- Incremental subtree push, files stored once
Subtree Publish by the numbers
- 50 all-time installs (skills.sh)
- Ranked #302 of 733 Git & Pull Requests skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cat-xierluo/legal-skills --skill subtree-publishAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 50 |
|---|---|
| repo stars | ★ 543 |
| Last updated | August 5, 2026 |
| Repository | cat-xierluo/legal-skills ↗ |
What it does
Pushes a monorepo subdirectory to a standalone GitHub repo via git subtree, using a registry manifest and automatic change detection for incremental pushes.
Files
Subtree Publish
将 monorepo 中指定子目录通过 git subtree push 推送到独立 GitHub 仓库。
文件只存一份(在 monorepo 中),一条命令同步到独立仓库。
前置条件
- 当前工作目录是 monorepo 根目录
- 子目录位于
<prefix>/<name>/ - 已安装上述依赖(git subtree、gh、jq)
依赖
系统依赖
| 依赖 | 用途 | 安装方式 |
|---|---|---|
| git(含 subtree) | 子目录拆分与推送 | macOS: 已预装 |
| gh | GitHub CLI,创建仓库和认证 | macOS: brew install gh |
| jq | JSON 配置解析 | macOS: brew install jq |
使用方式
1. 自动检测并推送(推荐)
当用户完成一次 git commit 后,如果本次提交涉及清单中的子项目:
bash scripts/subtree-push.sh --auto脚本会检查最近一次 commit 涉及的文件,如果命中清单中的子目录,自动推送到对应独立仓库。
触发时机: 用户说"提交完了"、"帮我推到独立仓库"、"同步 subtree"时,先检查是否有清单中的子项目被修改。
2. 首次注册新子项目
当用户说"注册 subtree"、"新增独立仓库发布"时:
Step 1: 确认子目录存在
ls <prefix>/<name>/SKILL.mdStep 2: 前置校验 — 检查 README.md
在执行任何远程操作之前,必须先检查子目录中是否已存在 README.md:
ls <prefix>/<name>/README.md- 如果不存在:必须先创建 README.md,才能继续后续步骤。不得跳过。创建时参照
references/readme-template.md模板。 - 如果已存在:跳过创建,继续下一步。
README.md 是独立 GitHub 仓库的人类展示页,不是 skill runtime 文件。创建或重写时必须遵守:
- 首屏讲清结果:标题、首段和引用句必须让访客在 30 秒内知道“这个 skill 帮谁,在什么场景下,产出什么”。不要先讲 frontmatter、目录结构或内部实现。
- 真实提问开场:在前半部分放一个真实用户提问和 AI 介入方式,优先展示“怎么用”,而不是罗列功能。
- 安装路径可执行:必须包含 GitHub Releases 下载、解压到 skill 目录、启用环境这三步;如需 Python/CLI/API Key 等依赖,必须就近写出安装或配置命令。
- 产物可预期:用清单说明会产出哪些文件、报告、表格、批注、检索结果或行动清单;避免只写“提高效率”“智能分析”等空泛词。
- 边界与责任清楚:必须分别说明适合与不适合的场景。法律、专利、商标、合规类 skill 必须写明“不替代正式法律意见/代理判断/注册成功承诺”等边界。
- 可信度有支撑:复杂或重点推广 skill 应说明方法框架、覆盖范围、关键文件、评测/示例/脚本等质量支撑,但不要把 README 写成完整技术文档。
- 许可证一致:README 的许可证类型必须与
SKILL.mdfrontmatter 和LICENSE.txt保持一致;CC BY-NC 类 skill 应提示商用授权联系方式以LICENSE.txt为准。 - 外部导流收尾:底部应说明本仓库所属的上游项目或技能集合,并推荐相关项目、主仓库或作者联系方式。不要把“所有修改在 monorepo 中进行”“通过 git subtree 同步”这类内部维护机制写给最终用户。
如需统一作者信息、二维码、主仓库链接或相关项目推荐,先读取 config/readme-profile.json;该文件为本地个性化配置,默认不提交。没有本地配置时,参考 config/readme-profile.example.json 的字段结构,并用通用占位符生成 README。
写作前按复杂度选择 profile:
minimal:简单工具型 skill。保留“典型场景 / 能产出什么 / 安装方式 / 使用边界 / 许可证 / 作者 / 关联项目”。standard:大多数公开 skill。在 minimal 基础上增加“适合谁用 / 当前覆盖范围 / 常见用法”。showcase:重点推广、复杂法律或高风险 skill。在 standard 基础上增加“项目解决什么问题 / 核心设计 / 示例输出 / 质量支撑 / 关键文件”。
发布前用以下检查清单复核 README:
1. 首屏是否能直接判断目标用户、核心场景和输出结果? 2. 是否有真实用户提问示例,而不是只有功能列表? 3. 安装步骤是否足以让外部用户开始试用? 4. 是否写明适合/不适合,且高风险场景有免责声明? 5. 许可证、作者、关联项目是否与当前 skill 元数据一致? 6. 是否删除了模板占位符、内部维护口吻和过长目录树?
Step 3: 创建独立 GitHub 仓库(如果需要首次设置)
gh repo create <org>/<repo-name> --public --description "<描述>"Step 4: 添加 remote
git remote add <name>-standalone https://github.com/<org>/<repo-name>.gitStep 5: 首次推送
git subtree push --prefix=<prefix>/<name> <name>-standalone mainStep 6: 注册到清单
将子项目信息加入 config/subtree-skills.json,并更新上面的清单表格。
Step 7: 创建首个 Release
首次推送后,运行脚本创建 GitHub Release:
bash scripts/create-release.sh <name>3. 手动推送单个子项目
bash scripts/subtree-push.sh <name> [--setup] [--dry-run]--setup: 同时创建 GitHub 仓库和添加 remote--dry-run: 只显示将要执行的操作,不实际推送
Remote 命名规则
独立仓库的 remote 名称统一为 <name>-standalone。
Release 创建规则
每次 subtree push 后,自动检查是否需要创建 GitHub Release。
触发条件
1. 读取 <prefix>/<name>/SKILL.md 中的 version 字段,获取当前版本号 2. 通过 gh release list --repo <org>/<repo-name> --limit 1 检查最新 Release 的 tag 3. 如果当前版本号对应的 v<version> tag 尚不存在,则创建 Release 4. 如果已存在相同版本的 Release,跳过
执行步骤
bash scripts/create-release.sh <name> [--dry-run]脚本会自动完成:读取版本号 → 检查已有 Release → 提取 CHANGELOG → 打包(排除 README.md 和 .DS_Store)→ 创建 Release。
Release Notes 来源
- 优先从
<prefix>/<name>/CHANGELOG.md提取对应版本的变更记录 - 如果 CHANGELOG.md 不存在或无对应版本,使用默认文本:
"发布 v<version>"
压缩包内容
压缩包解压后得到 <name>/ 文件夹(如 code2patent/),用户直接放入 .claude/skills/ 即可使用。
排除规则(create-release.sh 中 zip -r 的 -x 参数):
| 排除模式 | 理由 |
|---|---|
*.DS_Store | macOS 系统文件 |
README.md | 面向独立仓库浏览者,不属于 skill 运行时文件 |
archive/、archive/** | 历史审核/处理记录,含用户真实数据 |
output/、output/** | 运行时输出产物 |
**/__pycache__、**/__pycache__/* | Python 编译缓存 |
**/*.pyc、**/*.log | Python 编译缓存、日志 |
**/*.tmp、**/*.bak | 临时/备份文件 |
**/.env、**/.env.* | 环境变量文件(含密钥风险) |
DECISIONS.md | 本地决策记录与工作日志,含内部细节,不对外发布 |
TASKS.md | 本地任务清单,不属于 skill 运行时文件 |
config/*.json | 本地个性化配置,仅保留 *.example.json |
排除规则的完整性通过比对 .gitignore 中 **/archive/*、**/output/*、**/__pycache__/、**/DECISIONS.md、**/TASKS.md 等规则保证。脚本不主动读 .gitignore,而是用显式 -x 模式覆盖常见场景。新增排除需求时同步更新此处表格。
版本跃迁
支持非连续版本号(如 1.2.0 → 1.2.2,跳过 1.2.1)。每次只为当前推送的版本创建 Release,不会补建中间版本的 Release。
注意事项
- 不要在独立仓库中直接修改文件,所有修改都应在 monorepo 中进行
git subtree push在大仓库上可能较慢,这是正常的- 独立仓库中的文件位于根目录(不是嵌套的子目录)
- Git subtree 只推送已 commit 的文件,受 monorepo 根目录
.gitignore控制 - 若 monorepo 用 symlink 让
.claude/skills/<name>指向skills/<name>(如 legal-skills),手动提交 skill 自身改动时git add .claude/skills/<name>/...会报“pathspec 位于符号链接中”;改用真实物理路径git add skills/<name>/...即可。本 skill 的推送和打包脚本内部已统一使用skills/前缀,不受此 symlink 影响。
配置文件
config/subtree-skills.json
实际配置文件位于 config/subtree-skills.json,用于 --auto 模式检测和仓库名映射。如需新建配置,可参考 config/subtree-skills.example.json。
字段说明:
prefix: 子目录前缀(相对于 monorepo 根目录)org: GitHub 组织/用户名skills: 子项目数组name: 子目录名称(必填)repo: 独立仓库名(可选,默认为<name>.skill)version: 当前 SKILL.md 中的版本号(可选,用于核查是否需要发布新 Release)last_release: 最新已发布的 Release tag(可选,如v1.0.0)last_updated: 最近一次 subtree push 或 Release 更新时间(可选,格式YYYY-MM-DDTHH:MM:SS)
config/readme-profile.json(可选)
本地 README 个性化配置。用于统一生成独立仓库 README 的作者入口、二维码、上游项目导流和相关项目推荐。该文件通常包含个人或项目特定信息,应保留在本地;发布包和版本库只保留 config/readme-profile.example.json。
读取优先级:
1. 环境变量(临时覆盖) 2. config/readme-profile.json(本地固定配置) 3. config/readme-profile.example.json(字段结构示例) 4. references/readme-template.md 中的通用占位符
常用环境变量:
SUBTREE_README_UPSTREAM_NAMESUBTREE_README_UPSTREAM_URLSUBTREE_README_AUTHOR_DISPLAYSUBTREE_README_WECHAT_IDSUBTREE_README_QR_IMAGE_URLSUBTREE_README_CONTACT_DEFAULTSUBTREE_README_CONTACT_LEGALSUBTREE_README_CONTACT_TOOL
推荐字段:
upstream: 上游项目或技能集合信息author: 作者展示名、联系方式、二维码图片 URLcontact_messages: 默认、法律业务类、工具类联系文案related_projects: 可推荐的相关项目池skill_overrides: 按 skill 名定制推荐项目、联系文案或 profile
发布规则:
- 不要把真实的
readme-profile.json写入公开发布包 scripts/create-release.sh会排除非 example 的config/*.json,保留*.example.json
仓库名默认规则
独立仓库名默认在子目录名后追加 .skill 后缀。例如:
opc-legal-counsel→opc-legal-counsel.skillcode2patent→code2patent.skill
用户可以在 config/subtree-skills.json 中通过 repo 字段显式指定不同的仓库名,但如果不指定则自动使用 <name>.skill。
脚本
scripts/subtree-push.sh
自动化 subtree 推送流程,支持 --auto 变更检测和仓庝名映射。
scripts/create-release.sh
自动化 GitHub Release 创建流程。用法:bash scripts/create-release.sh <name> [--dry-run]
脚本自动完成:读取 SKILL.md 版本号 → 检查已有 Release → 提取 CHANGELOG 作为 Release Notes → 打包(排除 README.md 和 .DS_Store,解压后得到 <name>/ 文件夹)→ 创建 Release 并附上压缩包。
Changelog
All notable changes to this skill will be documented in this file.
[v1.7.1] - 2026-06-04
修复
- 修复
create-release.sh不读.gitignore的设计缺陷。zip -r之前只排除.DS_Store、README.md、config/*.json,未处理.gitignore中被全局排除的目录(archive/、output/、__pycache__/)和文件类型(*.pyc、*.log、*.tmp、*.bak、.env*)。 - 触发场景:contract-copilot v1.5.2 release 包含 242 个 archive/ 历史合同审核条目、60 个 __pycache__/ 条目、55 个 .pyc 编译缓存。
- 影响范围:archive/output/__pycache__ 等运行时产物可能被打包进独立仓库 release zip,潜在泄露用户数据(如合同审核历史)或污染 skill 安装目录。
- 修复后排除规则完整覆盖 .gitignore 中常见运行时产物类型。
文档完善
- 更新
TASKS.md和DECISIONS.md(本地)记录脚本设计缺陷。 - 更新
SKILL.md"压缩包内容" 章节,文档化完整排除规则。 - 排查其他 skill 历史 release:code2patent v1.5.3 / md2word v1.0.1 / de-ai-polish v1.0.0 / opc-legal-counsel v0.2.5 / contract-copilot v1.4.50 / v1.5.1 均无此问题。
[v1.7.0] - 2026-04-23
新增
- 新增
config/readme-profile.example.json,用于定义独立仓库 README 的作者入口、二维码、上游项目导流和相关项目推荐字段 - 支持通过本地
config/readme-profile.json或SUBTREE_README_*环境变量提供 README 个性化信息
改进
- README 模板改为从 profile 配置读取作者展示名、联系文案、二维码图片、上游项目和相关项目推荐
- Release 打包排除非 example 的
config/*.json,避免本地个性化配置进入公开发布包,同时保留*.example.json
文档完善
- 更新
SKILL.md、TASKS.md与DECISIONS.md,说明 README profile 的读取优先级、字段结构和发布规则
[v1.6.0] - 2026-04-22
改进
- 强化独立仓库 README 写作要求:从栏目模板升级为首屏价值、真实提问、安装依赖、产物预期、边界责任、可信度支撑和许可证一致性的质量验收标准
- 更新
references/readme-template.md,增加依赖/API Key 就近说明、CC BY-NC 商用授权提示和发布前检查清单 - 明确已注册独立仓库 README 应按 skill 复杂度选择
minimal/standard/showcaseprofile,而不是机械套用固定长模板 - 调整“关联项目”规范:README 应面向最终用户推荐上游项目、相关 skill 或作者入口,不再输出 monorepo/subtree 内部同步说明
- README 模板保持通用占位符,不硬编码特定作者二维码或特定项目推广文案
文档完善
- 更新
TASKS.md与DECISIONS.md,记录 README 规范升级的背景、理由和影响范围
[v1.5.0] - 2026-04-19
新增
- README 模板升级为“固定骨架 + 可选叙事模块”的结构,支持
minimal/standard/showcase三种复杂度 profile - README 写作原则:结果优先、示例优先、边界清晰、安装简单
- 首次注册流程新增 README 创建规则,要求根据 skill 复杂度选择合适展开层级
修改
references/readme-template.md从轻量固定模板改为面向独立 GitHub 仓库的人类展示模板SKILL.md中明确 README.md 是独立仓库展示页,不属于 skill runtime 文件
[v1.4.0] - 2026-04-19
新增
- Release 创建规则:每次 subtree push 后自动检查 SKILL.md 版本号,如无对应 tag 则创建 GitHub Release
- Release 附加 zip 压缩包(排除 README.md 和 .DS_Store),用户下载后可直接放入
.claude/skills/使用 - README.md 是面向独立仓库浏览者的展示文件,不属于 skill 运行所需,不纳入压缩包
- 新增
scripts/create-release.sh脚本,自动化 Release 创建流程 - Release Notes 优先从 CHANGELOG.md 提取,无则使用默认文本
- 首次注册流程新增 Step 7:创建首个 Release
- 仓库名默认规则:独立仓库名默认为
<name>.skill
修改
- 配置文件引用改为指向实际文件和 example 文件,移除内联 JSON 示例
- 版本跃迁说明:支持非连续版本号(如 1.2.0 → 1.2.2),只为当前推送的版本创建 Release
[v1.3.0] - 2026-04-19
新增
- 首次注册流程新增 Step 2 前置校验:推送前必须检查子目录是否存在 README.md,不存在则必须先创建
修复
- 修正首次注册流程中 Step 编号重复的问题(原文有两个 Step 3 和重复的 gh repo create 命令)
[v1.2.0] - 2026-04-19
修改
- 配置文件从 TXT 格式迁移到 JSON 格式(
config/subtree-skills.json) - 支持子目录名与 GitHub 仓库名的映射(如
opc-legal-counsel→opc-legal-counsel.skill) - 脚本从 JSON 配置中读取
org和prefix,不再硬编码 - SKILL.md 措辞通用化,去除 legal-skills 特定引用
[v1.1.0] - 2026-04-19
新增
--auto模式:自动检测最近 commit 涉及的子项目并推送config/subtree-skills.txt注册清单
修改
- SKILL.md 中添加已注册子项目清单表格
[v1.0.0] - 2026-04-19
新增
- 初始版本:支持单子项目推送(
--setup、--dry-run) - 自动创建 GitHub 仓库
- 自动添加 git remote
{
"upstream": {
"name": "<upstream-project-name>",
"url": "https://github.com/<owner>/<repo>",
"description": "如果需要更多相关 skill、示例和更新,可以关注上游项目。"
},
"author": {
"display": "<author-display-name>",
"wechat_id": "<wechat-id>",
"qr_image_url": "https://example.com/path/to/wechat-qr.jpg"
},
"contact_messages": {
"default": "如需使用交流、企业内部落地、定制开发或商用授权,欢迎添加微信(请注明来意)。",
"legal": "如需就具体业务、复杂法律问题、企业内部落地或商用授权进一步沟通,欢迎添加微信(请注明来意)。",
"tool": "如需使用交流、企业内部落地、定制开发或商用授权,欢迎添加微信(请注明来意)。"
},
"related_projects": [
{
"name": "<related-skill-name>",
"url": "https://github.com/<owner>/<repo>/tree/main/skills/<related-skill-name>",
"description": "<一句话说明>",
"tags": ["<tag>"]
}
],
"skill_overrides": {
"<skill-name>": {
"contact_message_key": "default",
"related_projects": ["<related-skill-name>"]
}
}
}
{
"prefix": "skills",
"org": "<your-github-org-or-username>",
"skills": [
{
"name": "<skill-directory-name>",
"repo": "<repo-name>.skill",
"version": "1.0.0",
"last_release": "v1.0.0",
"last_updated": "2026-01-01T12:00:00"
}
]
}
MIT License
Copyright (c) 2025 杨卫薪律师(微信ywxlaw)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
<skill-name>
<一句话说明:这个 skill 帮谁、在什么场景下、产出什么。>
<一句话价值主张:让访客在 30 秒内知道为什么值得安装。>
<!-- README.md 面向独立 GitHub 仓库的人类访客,不是 skill runtime 文件。 Release zip / 安装包中仍应排除 README.md,避免污染实际 skill 目录。
个性化信息来源:
- 优先读取 config/readme-profile.json(本地配置,不提交)
- 可用 SUBTREE_README_* 环境变量临时覆盖
- 公开发布只保留 config/readme-profile.example.json 作为字段示例
写作原则:
- outcome first:首屏先讲结果,不先讲 YAML/frontmatter/目录结构
- example first:前半部分给真实用户提问和 AI 介入方式
- install executable:安装步骤必须能照做;有依赖就就近写命令
- boundary clear:适合/不适合分开写,高风险场景必须写清免责声明
- trust supported:复杂 skill 补方法框架、覆盖范围、质量支撑和关键文件
- metadata consistent:许可证、作者、项目链接必须与 SKILL.md / LICENSE.txt 一致
复杂度 profile:
- minimal:保留“典型场景 / 能产出什么 / 安装方式 / 使用边界 / 许可证 / 作者 / 关联项目”
- standard:再加“适合谁用 / 当前覆盖范围 / 常见用法”
- showcase:再加“项目解决什么问题 / 核心设计 / 示例输出 / 关键文件 / 质量支撑”
-->
适合谁用
- <目标用户 1>
- <目标用户 2>
- <目标用户 3>
典型场景
<用一个真实、短小的例子说明什么时候会用到这个 skill。不要写成完整工作流,只要让读者一眼看懂“原来可以这样用”。>
用户:<用户会怎么提出需求>
AI:<这个 skill 会如何介入,并产出什么>它能产出什么
- <产出物 1>
- <产出物 2>
- <产出物 3>
安装方式
1. 打开本仓库的 GitHub Releases。 2. 下载最新版本的 skill 压缩包。 3. 解压后将 <skill-name>/ 文件夹放入你的 skill 目录。 4. 在支持 SKILL.md 的 Agent / Claude 环境中启用该 skill。
<!-- 如果需要依赖,必须就近补充,例如: 5. 安装依赖:pip install -r scripts/requirements.txt 6. 配置 API Key:复制 scripts/.env.example 为 scripts/.env 并填入密钥 -->
<!-- 不要在 README 中写“通过 git subtree 同步”“所有修改在 monorepo 中进行”等内部维护机制。README 面向最终用户,应把这里留给安装、使用和项目导流。 -->
可以怎么用
你可以直接向 Agent 提出类似问题:
- “<示例问题 1>”
- “<示例问题 2>”
- “<示例问题 3>”
使用边界
这个 skill 适合:
- <适合场景 1>
- <适合场景 2>
- <适合场景 3>
这个 skill 不适合:
- <不适合场景 1>
- <不适合场景 2>
- <不适合场景 3>
<!-- 以下为 standard / showcase profile 可选模块。简单 skill 可以删除。 -->
这个项目解决什么问题
<可选。用于复杂或重点推广的 skill。讲真实痛点,不要先讲文件结构。>
当前覆盖范围
- <覆盖范围 1>
- <覆盖范围 2>
- <覆盖范围 3>
核心设计
1. <设计原则 A>
<说明这个设计原则为什么存在,它如何影响输出质量。>
2. <设计原则 B>
<说明这个设计原则为什么存在,它如何影响使用体验。>
3. <设计原则 C>
<说明这个设计原则为什么存在,它如何控制边界或风险。>
示例输出
<可选。放一个短示例即可,不要把完整长文档粘进 README。重点展示“使用后会得到什么”。>
<简短示例输出>Before / After
不用这个 skill:
- <原来需要手工做什么>
- <容易遗漏什么>
使用这个 skill:
- <现在能自动完成什么>
- <输出如何更稳定、更结构化>
项目结构
<skill-name>/
├── SKILL.md
├── CHANGELOG.md
├── LICENSE.txt
├── references/
├── assets/
├── examples/
├── evals/
└── scripts/关键文件
- SKILL.md:正式 skill 入口,供 Agent 判断何时加载和如何执行
- CHANGELOG.md:版本变更记录
- LICENSE.txt:许可证文本
- references/:领域资料、流程规则或补充说明
- examples/:公开示例或示范输入输出
- evals/:评测样本或回归检查
- scripts/:辅助脚本
质量支撑
- <评测或回归方式 1>
- <示例或样稿 2>
- <脚本或人工检查 3>
许可证
本作品采用 <许可证类型> 许可证。
<!-- CC BY-NC 类 skill 如需提示商用授权,写: 如需商用授权,请以 LICENSE.txt 中的联系方式为准。 -->
作者
<从 config/readme-profile.json 的 author.display 读取;如未配置,再从 SKILL.md frontmatter 的 author 字段提取>
<!-- 如 config/readme-profile.json 或环境变量配置了作者咨询入口,可加入二维码或联系方式,例如:
<contact_messages.default / contact_messages.legal / contact_messages.tool 中匹配当前 skill 的联系文案>
<div align="center"> <img src="<author.qr_image_url>" width="200" alt="微信二维码"/> <p><em>微信:<author.wechat_id></em></p> </div> -->
关联项目
本仓库是 <upstream.name> 的子项目。<upstream.description>
相关项目:
- <related_projects 中的相关项目 A>:<一句话说明>
- <related_projects 中的相关项目 B>:<一句话说明>
- <related_projects 中的相关项目 C>:<一句话说明>
发布前检查
- [ ] 首屏说明了目标用户、核心场景和输出结果
- [ ] 前半部分有真实提问示例
- [ ] 安装步骤可执行,依赖/API Key 说明就近出现
- [ ] 适合/不适合场景分开写清楚
- [ ] 法律、专利、商标、合规类 skill 写明不替代正式专业意见
- [ ] 许可证、作者、关联项目与当前 skill 元数据一致
- [ ] 关联项目面向最终用户导流,不暴露 monorepo/subtree 等内部维护机制
- [ ] 删除模板占位符、内部维护口吻和过长目录树
#!/usr/bin/env bash
# create-release.sh - 为 subtree 发布的 skill 创建 GitHub Release
# 用法: bash scripts/create-release.sh <name> [--dry-run]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILL_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# 从 config 或默认值读取配置
CONFIG_FILE=""
PREFIX="skills"
ORG=""
# 尝试定位配置文件
for candidate in "$SKILL_ROOT/config/subtree-skills.json" "$SKILL_ROOT/../../../config/subtree-skills.json"; do
if [ -f "$candidate" ]; then
CONFIG_FILE="$candidate"
break
fi
done
if [ -n "$CONFIG_FILE" ] && command -v jq &>/dev/null; then
PREFIX=$(jq -r '.prefix // "skills"' "$CONFIG_FILE")
ORG=$(jq -r '.org // ""' "$CONFIG_FILE")
fi
# 参数解析
NAME=""
DRY_RUN=false
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true; shift ;;
-*)
echo "未知选项: $1" >&2
exit 1
;;
*)
if [ -z "$NAME" ]; then
NAME="$1"
else
echo "用法: $0 <name> [--dry-run]" >&2
exit 1
fi
shift
;;
esac
done
if [ -z "$NAME" ]; then
echo "用法: $0 <name> [--dry-run]" >&2
exit 1
fi
# 查找仓库名
REPO="${NAME}.skill"
if [ -n "$CONFIG_FILE" ] && command -v jq &>/dev/null; then
CUSTOM_REPO=$(jq -r --arg n "$NAME" '.skills[] | select(.name == $n) | .repo // empty' "$CONFIG_FILE" 2>/dev/null || true)
if [ -n "$CUSTOM_REPO" ]; then
REPO="$CUSTOM_REPO"
fi
fi
SKILL_DIR="${PREFIX}/${NAME}"
SKILL_MD="${SKILL_DIR}/SKILL.md"
# 校验
if [ ! -f "$SKILL_MD" ]; then
echo "错误: 找不到 $SKILL_MD" >&2
exit 1
fi
if [ -z "$ORG" ]; then
echo "错误: 无法确定 GitHub org,请检查 config/subtree-skills.json" >&2
exit 1
fi
if ! command -v gh &>/dev/null; then
echo "错误: 需要安装 gh (GitHub CLI)" >&2
exit 1
fi
# 读取版本号
VERSION=$(grep '^version:' "$SKILL_MD" | head -1 | sed 's/version: *//' | tr -d '"' | tr -d "'")
if [ -z "$VERSION" ]; then
echo "错误: 无法从 $SKILL_MD 读取 version" >&2
exit 1
fi
TAG="v${VERSION}"
FULL_REPO="${ORG}/${REPO}"
echo "=== Release 创建 ==="
echo " Skill: $NAME"
echo " 版本: $VERSION"
echo " 仓库: $FULL_REPO"
echo ""
# 检查是否已有该版本的 Release
EXISTING=$(gh release list --repo "$FULL_REPO" --limit 100 2>/dev/null | grep -E "^${TAG}\t" || true)
if [ -n "$EXISTING" ]; then
echo "跳过: ${TAG} 已存在 (${FULL_REPO})"
exit 0
fi
# 准备 Release Notes
NOTES="发布 v${VERSION}"
CHANGELOG_FILE="${SKILL_DIR}/CHANGELOG.md"
if [ -f "$CHANGELOG_FILE" ]; then
# 提取对应版本的变更记录(匹配 ## [vX.Y.Z] 或 ## vX.Y.Z 格式)
CHANGELOG_CONTENT=$(awk "/^## .*\[?v?${VERSION}\]?/{found=1; next} /^## /{if(found) exit} found{print}" "$CHANGELOG_FILE" 2>/dev/null || true)
if [ -n "$CHANGELOG_CONTENT" ]; then
NOTES="$CHANGELOG_CONTENT"
fi
fi
# 打包
ZIP_FILE="/tmp/${NAME}-${VERSION}.zip"
if [ "$DRY_RUN" = true ]; then
echo "[dry-run] 将执行以下操作:"
echo " 1. 打包: cd ${PREFIX} && zip -r ${ZIP_FILE} ${NAME}/ -x '${NAME}/*.DS_Store' '${NAME}/README.md' '${NAME}/config/*.json' '${NAME}/archive/*' '${NAME}/archive/**' '${NAME}/output/*' '${NAME}/output/**' '${NAME}/**/__pycache__' '${NAME}/**/__pycache__/*' '${NAME}/**/*.pyc' '${NAME}/**/*.log' '${NAME}/**/*.tmp' '${NAME}/**/*.bak' '${NAME}/**/.env' '${NAME}/**/.env.*' '${NAME}/DECISIONS.md' '${NAME}/TASKS.md'"
echo " 然后补回: ${NAME}/config/*.example.json"
echo " 2. 创建 Release:"
echo " gh release create ${TAG} --repo ${FULL_REPO} --title '${TAG}' --notes '...'"
echo " 附件: ${ZIP_FILE}"
exit 0
fi
echo "打包 ${NAME}/ ..."
cd "$PREFIX"
zip -r "$ZIP_FILE" "${NAME}/" \
-x "${NAME}/*.DS_Store" \
"${NAME}/README.md" \
"${NAME}/archive/*" \
"${NAME}/archive/**" \
"${NAME}/output/*" \
"${NAME}/output/**" \
"${NAME}/**/__pycache__" \
"${NAME}/**/__pycache__/*" \
"${NAME}/**/*.pyc" \
"${NAME}/**/*.log" \
"${NAME}/**/*.tmp" \
"${NAME}/**/*.bak" \
"${NAME}/**/.env" \
"${NAME}/**/.env.*" \
"${NAME}/DECISIONS.md" \
"${NAME}/TASKS.md" \
"${NAME}/config/*.json"
if [ -d "${NAME}/config" ]; then
while IFS= read -r example_config; do
zip -r "$ZIP_FILE" "$example_config" >/dev/null
done < <(find "${NAME}/config" -maxdepth 1 -type f -name "*.example.json" | sort)
fi
cd - > /dev/null
echo "创建 Release ${TAG}..."
gh release create "$TAG" --repo "$FULL_REPO" \
--title "$TAG" \
--notes "$NOTES" \
"$ZIP_FILE"
echo "完成: ${TAG} 已发布到 ${FULL_REPO}"
#!/usr/bin/env bash
# subtree-push.sh — 将 monorepo 中的子目录推送到独立 GitHub 仓库
#
# 用法:
# bash scripts/subtree-push.sh --auto # 自动检测最近 commit 涉及的子项目并推送
# bash scripts/subtree-push.sh <name> # 推送指定子项目
# bash scripts/subtree-push.sh <name> --setup # 首次设置:创建仓库 + 添加 remote + 推送
# bash scripts/subtree-push.sh <name> --dry-run # 只显示将要执行的操作
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/../config/subtree-skills.json"
# 从 JSON 配置读取 org 和 prefix
GITHUB_ORG=$(jq -r '.org // "cat-xierluo"' "$CONFIG_FILE")
PREFIX=$(jq -r '.prefix // "skills"' "$CONFIG_FILE")
# 检查是否在 monorepo 根目录
if [ ! -f "${PREFIX}/subtree-publish/SKILL.md" ]; then
echo "错误: 请在 monorepo 根目录执行此脚本"
exit 1
fi
# --- 自动检测模式 ---
if [ "${1:-}" = "--auto" ]; then
if [ ! -f "$CONFIG_FILE" ]; then
echo "配置文件不存在: ${CONFIG_FILE}"
exit 1
fi
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
if [ -z "$CHANGED_FILES" ]; then
echo "没有检测到最近的 commit 变更"
exit 0
fi
PUSHED=0
SKILL_COUNT=$(jq '.skills | length' "$CONFIG_FILE")
for i in $(seq 0 $((SKILL_COUNT - 1))); do
skill_name=$(jq -r ".skills[$i].name" "$CONFIG_FILE")
repo_name=$(jq -r ".skills[$i].repo // .skills[$i].name" "$CONFIG_FILE")
REMOTE_NAME=$(jq -r ".skills[$i].remote // \"${skill_name}-standalone\"" "$CONFIG_FILE")
SKILL_DIR="${PREFIX}/${skill_name}"
if echo "$CHANGED_FILES" | grep -q "^${SKILL_DIR}/"; then
echo "检测到 ${skill_name} 有变更,推送到独立仓库..."
if ! git remote get-url "$REMOTE_NAME" &>/dev/null; then
echo " 跳过: remote '${REMOTE_NAME}' 未配置"
continue
fi
echo " git subtree push --prefix=${SKILL_DIR} ${REMOTE_NAME} main"
git subtree push --prefix="$SKILL_DIR" "$REMOTE_NAME" main
echo " 完成: https://github.com/${GITHUB_ORG}/${repo_name}"
PUSHED=$((PUSHED + 1))
fi
done
if [ "$PUSHED" -eq 0 ]; then
echo "本次 commit 未涉及任何已注册的 subtree 子项目"
fi
exit 0
fi
# --- 单个子项目模式 ---
SKILL_NAME="${1:?用法: subtree-push.sh [--auto|<name>] [--setup|--dry-run]}"
FLAG="${2:-}"
REMOTE_NAME="${SKILL_NAME}-standalone"
SKILL_DIR="${PREFIX}/${SKILL_NAME}"
# 从 config 读取仓库名和 remote 映射
REPO_NAME="$SKILL_NAME"
REMOTE_NAME="${SKILL_NAME}-standalone"
if [ -f "$CONFIG_FILE" ]; then
MAPPED_REPO=$(jq -r --arg name "$SKILL_NAME" '.skills[] | select(.name == $name) | .repo // .name' "$CONFIG_FILE")
if [ -n "$MAPPED_REPO" ] && [ "$MAPPED_REPO" != "null" ]; then
REPO_NAME="$MAPPED_REPO"
fi
MAPPED_REMOTE=$(jq -r --arg name "$SKILL_NAME" '.skills[] | select(.name == $name) | .remote // ""' "$CONFIG_FILE")
if [ -n "$MAPPED_REMOTE" ] && [ "$MAPPED_REMOTE" != "null" ]; then
REMOTE_NAME="$MAPPED_REMOTE"
fi
fi
REPO_URL="https://github.com/${GITHUB_ORG}/${REPO_NAME}.git"
if [ ! -d "$SKILL_DIR" ]; then
echo "错误: 子目录不存在: ${SKILL_DIR}"
exit 1
fi
if [ ! -f "${SKILL_DIR}/SKILL.md" ]; then
echo "错误: 未找到 ${SKILL_DIR}/SKILL.md"
exit 1
fi
echo "=== Subtree Publish: ${SKILL_NAME} ==="
echo ""
DESCRIPTION=$(grep -m1 '^description:' "${SKILL_DIR}/SKILL.md" | sed 's/^description: *//' || echo "")
if [ "$FLAG" = "--dry-run" ]; then
echo "[dry-run] 将要执行的操作:"
echo " 1. 检查 remote '${REMOTE_NAME}' 是否存在"
echo " 2. 如不存在,创建 GitHub 仓库 ${GITHUB_ORG}/${REPO_NAME}"
echo " 3. 添加 remote: git remote add ${REMOTE_NAME} ${REPO_URL}"
echo " 4. 推送: git subtree push --prefix=${SKILL_DIR} ${REMOTE_NAME} main"
echo ""
echo "描述: ${DESCRIPTION}"
exit 0
fi
if git remote get-url "$REMOTE_NAME" &>/dev/null; then
echo "remote '${REMOTE_NAME}' 已存在: $(git remote get-url "$REMOTE_NAME")"
else
if [ "$FLAG" = "--setup" ]; then
echo "创建 GitHub 仓库: ${GITHUB_ORG}/${REPO_NAME}"
gh repo create "${GITHUB_ORG}/${REPO_NAME}" --public ${DESCRIPTION:+--description "$DESCRIPTION"}
else
echo "remote '${REMOTE_NAME}' 不存在。使用 --setup 首次设置,或手动添加:"
echo " git remote add ${REMOTE_NAME} ${REPO_URL}"
exit 1
fi
echo "添加 remote: ${REMOTE_NAME}"
git remote add "$REMOTE_NAME" "$REPO_URL"
fi
echo ""
echo "推送 subtree: ${SKILL_DIR} -> ${REMOTE_NAME}/main"
git subtree push --prefix="$SKILL_DIR" "$REMOTE_NAME" main
echo ""
echo "=== 完成 ==="
echo "独立仓库: https://github.com/${GITHUB_ORG}/${REPO_NAME}"
# 提醒注册到清单
if [ -f "$CONFIG_FILE" ]; then
REGISTERED=$(jq --arg name "$SKILL_NAME" '.skills[] | select(.name == $name) | .name' "$CONFIG_FILE")
if [ -z "$REGISTERED" ]; then
echo ""
echo "提醒: ${SKILL_NAME} 尚未注册到 config/subtree-skills.json"
fi
fi