AI agent skill · by @1c7
Chinese Indie Dev
处理 1c7/chinese-independent-developer 仓库的项目提交。 检查 issue #160 新评论、新开的 Issue、新开的 PR, 将有效项目添加到对应 README,关闭垃圾内容。 当用户要求“收录项目”“处理提交”“处理 issue”“跑一下列表”或运行中国独立开发者列表维护流程时使用。
Archived 2026-10-23 · Source updated 2026-10-11. Check the original repository for the latest version.
SKILL.md instructions
# 中国独立开发者列表:处理项目提交
你的任务是处理 GitHub 仓库 `1c7/chinese-independent-developer` 的项目提交。
检查最近的新评论、新 Issue、新 PR,将有效项目添加到对应 README,关闭垃圾内容。
⚠️ 严格禁止:不得调用添加 reaction 的 API。
⚠️ 严格禁止:不得以任何理由删除或修改 README 中**他人**已有的条目。本 skill 的核心允许操作是**新增**。如果发现疑似重复,只需跳过,绝对不能删除。**唯一例外:产品作者本人提交更新自己的条目**(如换官网 URL、优化自家产品描述)时,按检查三正常合并——这是作者维护自己的产品,不构成"篡改他人条目",不属于本条禁令范围(例:MyServers 作者 lovercode 更新官网到 myservers.plus → 合并)。除此之外,一律不碰已有条目。
⚠️ **合并 PR 前必须逐行审查完整 diff:不允许 PR 删除别人的产品。** 检查三的每个 PR 在执行任何 `gh pr merge` 之前,先 `gh pr diff <number>` 导出完整 diff 逐行看一遍:正常提交只应出现**新增**行;只要 diff 里有 `-` 行删掉了**他人已有条目或大段既有内容**(哪怕 PR 标题、描述完全没提),就绝不能原样合并。标准处理方式:把被删的行原样恢复(`maintainer_can_modify` 为 true 时优先把恢复 commit 直接推到贡献者分支上,再正常合并;推不了就在本地合并时恢复),使 PR 的最终 diff 回到纯新增,绝不能让删除落进 master。历史事故:2026-09-07 PR #1356(AI 独立制造所)——贡献者在 GitHub 网页端编辑 README 加 1 行链接,网页编辑器把文件尾部静默截断,193 行(2022年1月~5月全部条目)一起被删;当时的处理是把恢复 commit(85440f1)推到贡献者分支再合并,master 实际净增 1 行,这个处理是正确范本。GitHub 网页端编辑大文件会静默截断尾部,所以"PR 标题只说加了 X"不等于"diff 只有加 X",必须看 diff 本身。
⚠️ **README 顶部的「### 子版面」清单只放本仓库自己的页面**(程序员版面、游戏版面、历史存档 README-Archive.md),**不允许把任何外部产品/外部链接加进这个清单**。子版面是仓库的目录结构,不是收录位。外部产品的正确位置按类型二选一:**普通产品**进主列表当天日期区块;**基于本列表数据源制作的产品**(网页版、可视化、导航站等)进 README 底部的「## 基于本列表数据源的产品」区块——那是它们的专属位置,不放日期区块。检查一/二的收录流程绝不写子版面清单;检查三的 PR 若往子版面清单加了外部产品(历史事故:#1356 把「导航站:AI 独立制造所」放进了子版面清单),照常合并不拒绝,但合并后必须立即用**单独的 commit** 把条目挪到上述正确位置(普通产品挪到日期区块并补作者行 `#### 作者 - [Github](url)`;数据源衍生产品挪到底部专属区块);挪完单独说明,不混进新增 commit。参考:#1356 的 AI 独立制造所先被放进子版面清单(错误),2026-09-09 最终归入底部「基于本列表数据源的产品」区块(现行规则)。
⚠️ 关于历史评论签名("Generated by Claude Code"):当前 Codex 通过 `gh` 和维护者 PAT 操作,不会主动添加这段签名;但历史自动化可能留下残余。本 skill 里每处发评论仍要求 POST 后立即 PATCH 覆写并验证正文,文末「收尾扫描」也必须执行。
⚠️ 关于用户名旁边的 GitHub App 归属标记:这是 GitHub 根据执行 API 调用的 App 自动显示的 `performed_via_github_app`,无法通过 PATCH 正文去掉。运行前确认 `gh auth status` 使用 1c7 账号自己的 Personal Access Token。
⚠️ 严格禁止:本 skill 涉及的所有 GitHub 操作(发评论、开关 issue、合并/关闭 PR、改 reaction 等)**只能通过 shell 中的 `gh` / `git` 命令行执行**,绝对不能使用 GitHub Connector 或平台原生 GitHub 工具。这样才能确保操作使用维护者 PAT 身份。文件读取与修改使用 Codex 提供的本地文件工具。
⚠️ 本仓库**只收录中国独立开发者**的项目(仓库标题即「中国独立开发者项目列表」)。检查一、二、三的每一位提交者,在进入「通用处理流程」或合并 PR 之前,都要按下方「身份判断」章节判断。**默认原则是「能收就收」:除非有明确证据表明提交者是老外,否则一律收录/合并,绝不因为身份信息不完整就拒绝。** 只有确凿是外国人才礼貌拒绝。宁可多收一个疑似中国的,也不轻易拒绝一个其实是中国人的提交——漏收/误拒对中国开发者造成的伤害,远大于偶尔收进一个身份模糊的条目。
⚠️ 三个版面的**固定名称**是「主版面」「程序员版面」「游戏版面」——都以"版面"两个字结尾,不是"主版"/"程序员版"/"游戏版"。所有感谢评论、PR/issue 评论中提到版面名称的地方,写完之后要逐字核对有没有漏掉"面"字(历史运行中出现过在感谢评论里把"程序员版面"错写成"程序员版"的情况,且没有被自动校验发现)。
⚠️ **格式问题绝不能作为关闭 PR / Issue / 拒绝收录的理由。** 提交格式再乱(一行纯文本、没有 `####` 作者行、没有 `:white_check_mark:`、中英文没空格、描述像广告词),都只是我们收尾时要清理的事,不是拒绝提交者的理由。允许关闭的理由只有两类:**垃圾广告/无关内容**,以及**非中国开发者**。正确做法是先合并/先收录,再按下面的格式规范单独发一条整理 commit。历史上出现过 bot 以"格式和本仓库的收录要求不符"为由关掉了一个本可以合并的 PR(#1205),这是错误的。发拒绝评论前,逐字检查理由里有没有出现"格式"两个字——如果有,说明这次拒绝就是错的,退回去改成合并。
⚠️ **版面判断必须点开产品链接实际看一眼,不能只凭提交者的标题和描述猜。** 判断标准见「步骤2:分类」的表格,这里不重复。要强调的是动作:每个待收录项目都要真的打开一次;是 GitHub 项目就再看一眼 `gh api repos/<owner>/<repo> | jq '{homepage}'`、release 有没有可下载的 assets、README 快速开始第一步是不是 `git clone` / docker / 包管理器命令。
⚠️ **多个 PR 同时打开、且会插入同一个日期区块时,必须逐个串行处理,禁止图省事改成"手动誊抄内容 + 批量关闭 PR"。** 历史事故:2026-07-30 12:53 前后 #1220、#1221、#1226、#1227 四个格式完全合规、非垃圾、提交者也是中国开发者的 PR,因为都要插入同一天的日期区块、彼此会冲突,处理时没有按下面「检查三」定义的单 PR 合并流程逐个走完,而是直接用本地编辑工具把内容誊抄进 master 合成一条 commit,再把四个 PR 全部 `gh pr close`——内容确实进了 README,但四个 PR 全都错误地显示为红色 Closed 而不是紫色 Merged,贡献者观感很差。正确做法:每次只处理一个 PR,处理完(无论是 `gh pr merge --squash` 还是下面步骤3 的本地合并+push)**立即** `git fetch origin master` 拿到最新 master,再开始下一个 PR——这样后处理的 PR 在合并时天然就能感知到前一个 PR 已经插入的行,走正常的冲突合并路径(保留双方条目),而不会退化成"批量手动誊抄"。**只有当某个 PR 的 `maintainer_can_modify` 确实是 `false` 且推送环节确实失败时**,才允许落到步骤3 最终的"本地合并+关闭"兜底;不允许仅仅因为"同时有好几个 PR 要处理"就跳过前面的真实合并尝试直接走兜底。
⚠️ **PR 贡献者选的文件经常是错的,版面由我们判断,不由他改了哪个文件决定。** 检查三的 PR 走直接合并、不经过步骤2,但版面判断这一步对 PR 同样必须做。发现放错版面时,**照常先合并**(绝不因此关闭或退回 PR),再把条目挪到正确的版面并单独发一条修正 commit;如果该 PR 的 `maintainer_can_modify` 为 `true`,也可以直接改他的分支、让 PR 一次就落到正确的文件再合并——两种方式都行,重点是必须 merge 且最终落在正确版面。挪完后如果感谢评论已经发出去了,记得 PATCH 修正评论里的版面名称。历史错误:Site Guard 是自托管 Docker 部署却被合并进主版面(#1206)、摸鱼解压玩具是游戏却提交到主版面(#1208)。
⚠️ 感谢评论必须简短,只说结果,不说过程:固定句式是「@用户名 感谢提交,你的产品 X 已添加到 Y 版面!」(或已收录多个产品时按本文档后面给的变体)。**禁止**在评论里额外加"谢谢!"这类结尾客套话(前面已经有"感谢提交"了,不需要再谢一次);**禁止**提及处理过程中的内部细节,例如"PR 有冲突"「已由我们手动合并」「已手动处理」之类——不管背后是直接合并、手动解决冲突、还是走 issue 流程,提交者只需要知道结果(收录到了哪个版面),不需要知道我们是怎么做到的。发送前对照这条逐字检查。
---
## 预检:快速判断是否有任何新内容
⚠️ 本 skill 由用户手动触发,运行间隔不固定(通常 1~2 天一次,但可能间隔更久),**不能再假设"每 6 小时自动跑一次"**。所有时间窗口统一使用 **72 小时**(3 天)安全余量,宁可扫描范围偏大、多花几秒确认已处理过,也不能因为窗口太窄而漏掉提交(历史事故:2026-08-07 08:10 的评论因窗口只有 7 小时被漏处理,直到用户截图问起才发现)。如果确认距离上次运行已经超过 3 天(例如出差、长期没运行),临时把下面的 `-v-72H` 改成更大的值(如 `-v-240H` 覆盖 10 天)再运行一次。
**在做任何实质处理之前**,先并行运行以下三条命令,统计各自的结果数量:
```bash
SINCE=$(date -u -d '72 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-72H +%Y-%m-%dT%H:%M:%SZ)
# 检查一:#160 新评论数
# 注意:这个数字包含我们自己(1c7/claude[bot])发的致谢评论,通常明显大于「真正待处理的提交数」。
# 例:2026-09-21 预检显示 18 条,其中 7 条是自己的致谢、9 条上次运行已处理,实际待处理只有 2 条。
# 数字偏大是正常的(窗口比运行间隔宽),不必紧张,按下面检查一的逐条去重流程走即可。
COUNT_COMMENTS=$(gh api "repos/1c7/chinese-independent-developer/issues/160/comments?since=$SINCE&per_page=100" | jq 'length')
# 检查二:新 Issue 数
COUNT_ISSUES=$(gh api "repos/1c7/chinese-independent-developer/issues?state=open&per_page=50" \
| jq --arg since "$SINCE" '[.[] | select(.number != 160 and .pull_request == null and .created_at >= $since)] | length')
# 检查三:待处理 PR 数(排除 auto-add- 分支)
COUNT_PRS=$(gh api "repos/1c7/chinese-independent-developer/pulls?state=open&per_page=50" \
| jq '[.[] | select(.head.ref | startswith("auto-add-") | not)] | length')
echo "新评论: $COUNT_COMMENTS 新Issue: $COUNT_ISSUES 待处理PR: $COUNT_PRS"
# 容量预检:README.md 可能在上次运行之后就已经被别的操作顶过阈值(他人 merge、网页端编辑等),
# 不能只在「插入条目后」才检查,否则本次空跑时会把超标状态原样留到下一次。
# 2026-09-27 实测:运行开始时 README.md 已经是 490,082 字节(上次运行记录是 489,424),
# 属于运行间隔内被别的提交顶过线。超 490,000 就直接按步骤3 的存档规则搬到 ~487,500。
wc -c README.md
```
⚠️ 预检里的容量检查 > 490,000 时,与「插入条目后超标」同样处理:立即做一次存档迁移(见「通用处理流程」步骤3),**一次剪到 487,500 字节左右**,不要只剪刚够的量。即使本次最终没有新收录,也要把这轮迁移单独 commit 掉。
如果三个数字**全部为 0**,输出「无新内容」,跳过检查一、二、三和通用处理流程,**但仍然必须执行文末的「收尾扫描」**(那一步与本次有没有新提交无关),扫完再结束。
只要有任意一个数字 > 0,才继续向下执行。
⚠️ **数字 > 0 不等于"有新提交",也不等于"都处理过了"——必须把「待处理清单」真正列出来再判断(2026-09-23 补)。** 历史事故:2026-09-22 16:44 那次运行预检到 #160 有 24 条评论,凭"多数是已致谢的旧条目"的印象就判定只剩 1 条新提交,实际漏掉了 3 条(yvonuk 09:07、StrokMitream 13:06、whattobuildtoday 13:16),直到 2026-09-23 00:52 重跑才发现。原因是:**窗口内的评论总数里混着我们自己的致谢,"看着像都处理过"和"真的都处理过"是两回事**。所以在继续往下走之前,**无条件**先跑一次集合运算,把结论打出来:
```bash
# 1) 窗口内所有提交者(排除我们自己)
jq -r '.[] | select(.user.login != "1c7" and .user.login != "claude[bot]") | .user.login' /tmp/c160.json | sort -u > /tmp/submitters.txt
# 2) 已经被致谢过的(我们自己发过 @某人 的评论)
jq -r '.[] | select(.user.login == "1c7" or .user.login == "claude[bot]") | .body' /tmp/c160.json \
| grep -oE '^@[A-Za-z0-9-]+' | tr -d '@' | sort -u > /tmp/thanked.txt
# 3) 差集 = 真正待处理(注意:重复提交已收录产品的老作者也会出现在这里,需再走一遍 URL 去重才能确定收不收)
comm -23 /tmp/submitters.txt /tmp/thanked.txt
```
(`/tmp/c160.json` 就是检查一里 `gh api .../issues/160/comments?since=$SINCE&per_page=100` 的落盘结果,预检时先存一份。)
**只要差集非空,就必须逐条进检查一流程**,不许凭印象跳到"无新内容"。
⚠️ **但差集为空 ≠ 一定无新提交——差集只按「用户名」去重,会漏掉老作者的二次投稿**(2026-10-10 补)。历史事故:huangy7 因更早的 Lokii 提交已被致谢、用户名早已进了 thanked 集合,他在 2026-10-09T14:51Z 又发了一条 outline-skill 的新提交;用户名差集把这个新提交整个遮掉(huangy7 同时出现在 submitters 和 thanked 里 → `comm` 判为已处理),上一次运行只致谢了另一位提交者就收尾,这条被漏了整整一轮。所以差集运算只是**第一道**过滤,不能当作"无新提交"的终判。**必须再逐条核对:窗口内每一条提交者发的评论(非致谢类)之后,是否有一条 `1c7` / `claude[bot]` 发出的、`@` 到该提交者的致谢**(用 `jq` 取该评论的 `created_at`,看之后有无对应回复,做法同检查一里的「72 小时窗口重复处理」小节)。只要有任何一条评论没有对应致谢,就按新提交处理,再走 URL 去重决定收不收。
---
## 检查一:issue #160 的新评论
获取最近 72 小时内的评论(人工运行、间隔不固定,用 3 天安全余量覆盖,宁可多扫不能漏扫):
```bash
SINCE=$(date -u -d '72 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-72H +%Y-%m-%dT%H:%M:%SZ)
gh api "repos/1c7/chinese-independent-developer/issues/160/comments?since=$SINCE&per_page=100"
```
对每条评论,从内容中提取产品 URL,检查是否已在任意 README 中:
```bash
grep -rF "<产品完整URL>" README.md .github/pages/README-Programmer-Edition.md .github/pages/README-Game.md
```
⚠️ 去重规则(必须严格遵守):
- 必须用产品的**完整 URL**(如 `https://example.com`)做精确字符串匹配,使用 `grep -F`(固定字符串,非正则)
- 禁止用产品名称、描述文字或部分关键词判断重复——描述里提到某个工具名不等于该工具已在列表中
- URL 已存在 → 跳过,对 README 不做任何操作,**也不发致谢评论**(那是老作者重复提交自己已收录的产品,说"已添加到主版面"会误导;2026-09-22 shanleiguang 重复提交 vPlayer HiFi,该条 2026-08-08 已收录 → 静默跳过)
- URL 不存在 → 进入「通用处理流程」
- 当前运行中已通过 PR 合并的条目,视为已存在,不再重复处理
- ⚠️ **同一款产品换个域名重复提交,URL 去重挡不住,但也不是拒绝理由**(2026-09-30 补)。只要它是真实可用、非大厂、非返佣的独立产品、**产品名或站点实际用途能区分开**,就照常收录。典型场景:同一模型/同一 API 的不同套壳站点会成簇投稿(Jev 系列:jev.stockai.trade、jev.guide、whatcanjevdo.com、jev-ai.pro、ai-jev.pro、decisionapi.net 在两周内分别由不同账号提交;AI 潮流短视频系列:gasstationdance.pro、raindance-ai.pro,同一作者同一批投稿)。处理方式:**收录,但代拟描述时必须写出它自己独有的功能点**(例如 ai-jev.pro 的「Playground + CSV/TXT/JSONL 批量 + 注册送 5 额度」),不要照抄簇里已有条目的描述,避免列表里出现两行看不出差别的同款。簇本身不构成偏见——每个提交者的身份判断仍按「身份判断」章节单独走。
⚠️ **72 小时窗口比单次运行间隔更宽,同一条评论可能在连续两次运行中都落在窗口内。** 对于最终被判定为「拒绝」(垃圾广告 / 非中国开发者)的评论,因为不会在 README 里留下 URL 痕迹,上面的 URL 去重挡不住重复处理。处理每条评论、判断要不要发拒绝回复之前,先检查该评论下面是否已经有 `1c7` 或 `claude[bot]` 发过 `@<提交者用户名>` 开头的回复(即这条评论已经被上一次运行处理过):
```bash
gh api "repos/1c7/chinese-independent-developer/issues/160/comments?per_page=100" \
| jq --arg since "<该评论created_at>" \
'[.[] | select(.created_at > $since and (.user.login=="1c7" or .user.login=="claude[bot]"))]'
```
如果已存在针对该提交者的回复,视为已处理过,跳过,不再重复发送拒绝评论。
处理完所有检查一的评论后,记录每位成功处理的评论作者用户名、收录的产品名和目标版面(用于最后一步在 #160 逐人发感谢评论)。
---
## 检查二:最近 72 小时内开启的新 Issue(非 #160)
```bash
SINCE=$(date -u -d '72 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-72H +%Y-%m-%dT%H:%M:%SZ)
gh api "repos/1c7/chinese-independent-developer/issues?state=open&per_page=50" \
| jq --arg since "$SINCE" \
'[.[] | select(.number != 160 and .pull_request == null and .created_at >= $since)]'
```
判断每个 Issue:
- **有效项目提交**(含产品名 + 可访问 URL)→ 进入「通用处理流程」,完成后:
1. 在**该 issue 本身**(不是 #160!)发感谢评论,需说清楚收录到了哪个版面(主版面/程序员版面/游戏版面,对应「通用处理流程」步骤2的分类结果),立即捕获 ID 并 PATCH 去掉自动追加的署名:
```bash
CLEAN_BODY="@<提交者用户名> 感谢提交,已将你的产品 <产品名> 添加到 <主版面/程序员版面/游戏版面>!"
COMMENT_RESPONSE=$(gh api repos/1c7/chinese-independent-developer/issues/<number>/comments \
-X POST -f body="$CLEAN_BODY")
COMMENT_ID=$(echo "$COMMENT_RESPONSE" | jq -r '.id')
gh api --method PATCH \
repos/1c7/chinese-independent-developer/issues/comments/$COMMENT_ID \
-f body="$CLEAN_BODY"
```
2. 关闭 issue(**不能漏,历史事故:#1225 发完感谢评论后就漏了这一步,issue 一直挂着 open**):
```bash
gh issue close <number>
```
- **垃圾广告、无关内容** → 直接关闭:`gh issue close <number>`
- **内容不清晰 / 信息缺漏** → 这不是关闭理由。默认按「通用处理流程」尽力提取收录(名字用 GitHub 用户名代替、URL 从正文/仓库里找);确属纯广告无任何可定位产品才按垃圾关闭。不确定时倾向于收录,不要轻易关闭。
- **确凿是老外** → 按上方「身份判断」的拒绝流程处理。
检查二的提交者不需要在最后一步中 @ 到 #160,他们已在各自的 issue 里收到了感谢。
---
## 检查三:所有未关闭的 PR(排除 auto-add- 分支)
直接获取所有 open 状态的 PR(不限时间,确保不遗漏):
```bash
gh api "repos/1c7/chinese-independent-developer/pulls?state=open&per_page=50" \
| jq '[.[] | select(.head.ref | startswith("auto-add-") | not)]'
```
判断每个 PR:
- **有效项目提交**(对 README 的修改,含产品名 + URL)→ **合并前先导出完整 diff 逐行审查**:
```bash
gh pr diff <number>
```
按文档开头两条禁令检查:① diff 里若有删除**他人条目**的 `-` 行(GitHub 网页端编辑大文件会静默截断尾部;"PR 标题只说加了 X"不等于"diff 只有加 X",历史事故 #1356 表面加 1 行、实际删 193 行),先把被删内容原样恢复再合并(处理范本见文档开头),绝不让删除落进 master;② 若新增行落在「### 子版面」清单里,先照常合并,合并后立即单独 commit 把条目挪到正确位置(普通产品→当天日期区块;基于本列表数据源的产品→README 底部「基于本列表数据源的产品」区块,处理方式见文档开头)。审查通过后正常合并:
```bash
gh pr merge <number> --squash
```
- 合并后同样跑 `wc -c README.md` 容量检查(规则见「通用处理流程」步骤3):单个 PR 通常只加几百字节一般不会触发,但同一天多个 PR 叠加可能触发阈值
- 如果合并成功:在该 PR 发感谢评论,需说清楚收录到了哪个版面(根据 PR 修改的是 README.md / .github/pages/README-Programmer-Edition.md / .github/pages/README-Game.md 中的哪个文件,对应主版面/程序员版面/游戏版面),立即捕获 ID 并 PATCH 去掉署名:
```bash
CLEAN_BODY="@<提交者用户名> 感谢提交,已将你的产品 <产品名> 合并到 <主版面/程序员版面/游戏版面>!"
PR_COMMENT_RESPONSE=$(gh api repos/1c7/chinese-independent-developer/issues/<number>/comments \
-X POST -f body="$CLEAN_BODY")
PR_COMMENT_ID=$(echo "$PR_COMMENT_RESPONSE" | jq -r '.id')
gh api --method PATCH \
repos/1c7/chinese-independent-developer/issues/comments/$PR_COMMENT_ID \
-f body="$CLEAN_BODY"
```
- 如果合并失败(如 merge conflict):**由我们自己解决冲突并合并,绝不要求提交者 rebase**。优先尝试真正合并(让 PR 显示为 Merged),只有技术上不可行时才降级为「手动合并进 master + 关闭 PR」。步骤:
1. 先查该 PR 是否允许维护者编辑分支:
```bash
MAINTAINER_CAN_MODIFY=$(gh api repos/1c7/chinese-independent-developer/pulls/<number> | jq -r '.maintainer_can_modify')
```
2. **如果 `MAINTAINER_CAN_MODIFY` 为 `true`**(优先路径,能让 PR 真正显示为 Merged):
```bash
HEAD_REF=$(gh api repos/1c7/chinese-independent-developer/pulls/<number> | jq -r '.head.ref')
HEAD_REPO_URL=$(gh api repos/1c7/chinese-independent-developer/pulls/<number> | jq -r '.head.repo.clone_url')
git fetch origin master
git fetch origin pull/<number>/head:pr-<number>
git checkout pr-<number>
git merge origin/master --no-edit
```
如果出现冲突:**手动编辑冲突文件**,保留双方新增的条目(不要删除任何一方已有的行),冲突标记全部清理干净,然后:
```bash
git add <冲突文件>
git commit --no-edit
git push "$HEAD_REPO_URL" "pr-<number>:$HEAD_REF"
gh pr merge <number> --merge
```
(用 `--merge` 而非 `--squash`,保留贡献者原始 commit 的作者信息)
⚠️ 推送后立刻 `gh pr merge` 可能报 `GraphQL: Base branch was modified. Review and try the merge again.`。**这不是真失败**——GitHub 的 mergeable 状态是异步计算的,撞上未刷新的缓存而已。做法:`sleep 5` 后重查 `gh api repos/.../pulls/<number> | jq '{mergeable, mergeable_state}'`,看到 `true` / `clean` 再重试一次 `gh pr merge <number> --merge` 就会成功(2026-09-22 PR #1408 实测)。**绝不能因为这条报错就退回步骤 3 的"本地合并 + 关闭 PR"兜底**,那会让贡献者的 PR 变成红色 Closed。只有权限/分支保护导致的推送失败才算该路径不可行,才降级到步骤 3。合并成功则按下面「合并成功」的致谢评论流程处理,PR 会正常显示为 Merged。
3. **如果 `MAINTAINER_CAN_MODIFY` 为 `false`**(贡献者未勾选"允许维护者编辑",没有权限推送到其分支,只能走这条兜底路径),或步骤 2 推送失败:
```bash
git fetch origin master
git fetch origin pull/<number>/head:pr-<number>
git checkout master && git reset --hard origin/master
git merge pr-<number> --no-ff -m "合并 PR #<number>:<项目名>"
```
如果出现冲突:冲突几乎必然是 README 里同一个日期区块被多个 PR 同时插入条目导致的。**手动编辑冲突文件**,保留双方新增的条目(不要删除任何一方已有的行),冲突标记全部清理干净,然后:
```bash
git add <冲突文件>
git commit --no-edit
```
校验 README 格式无误后推送:
```bash
git push origin master
```
因为贡献者的分支落后于 master、GitHub 无法用按钮直接标记该 PR 为 merged,所以改为关闭 PR 并致谢。**评论正文不要提冲突、不要提手动合并这类处理过程细节**,提交者不需要知道内部是怎么处理的,只需要知道结果:
```bash
CLEAN_BODY="@<提交者用户名> 感谢提交,你的产品 <产品名> 已添加到 <主版面/程序员版面/游戏版面>!"
gh pr close <number> --comment "$CLEAN_BODY"
```
4. 如果冲突内容复杂到无法安全判断该保留什么(例如冲突不只是新增条目,而是修改了已有内容的结构),**不要瞎猜、不要删除任何已有内容**,改为在 PR 里说明具体冲突原因并保持 PR 打开,等待人工介入;但这应是极少数情况,绝大多数「新增条目」型冲突都应该自动解决。
- **不允许**的做法:连续多次发送「请 rebase / 请解决冲突后重新提交」这类要求人类提交者自己解决冲突的评论。冲突处理是我们的责任,不是提交者的。
- **垃圾广告、无关内容** → 直接关闭:`gh pr close <number>`
- **确凿是老外** → 不合并,按上方「身份判断」章节的拒绝流程处理:礼貌评论说明后 `gh pr close <number>`,不使用 `gh pr merge`。身份模糊不等于老外,默认合并(见「身份判断」章节)。
- **格式乱、不符合模板** → 这不是关闭理由,照常合并,合并后按下面「PR 合并后要检查描述格式」的要求单独发一条格式整理 commit(见文档开头的格式禁令)
⚠️ 严格禁止:检查三的 PR **无冲突时**必须走上面的 `gh pr merge --squash`,不能走通用处理流程(那样会丢失贡献者的 git 归属)。**有冲突时**才使用上面的本地合并步骤,因为 `git merge --no-ff` 会保留贡献者原始 commit 的作者信息,不会丢失归属。
⚠️ PR 合并后要检查描述格式(`gh pr merge` 是原样合并贡献者写的文字,不会自动清理):如果存在「中英文/数字之间没有空格」「一个、一款、高效、简洁、强大等填充词」「产品类型被埋在长修饰语最后面(如"是一个基于 X、Y、Z 构建的 W"这种结构,应改成"W,基于 X、Y、Z 构建"把 W 提前)」这几类问题,合并完之后**单独用本地编辑工具再发一条格式整理的 commit**(只改标点空格等硬伤,不改事实信息,也不算作「修改已有条目」——因为这是本次新增内容的同一批次收尾,不是改动历史上已收录的条目)。不要把这类清理和「新增」提交混在一条 commit 里,保持 commit 语义清晰。
⚠️ **格式整理的边界:绝不调整提交者自己写好的句子语序。** 如果 PR diff / 评论里已经是一行 README 格式条目,描述文字就是提交者的定稿,必须逐字保留,我们只允许改硬伤:半角标点改全角、句尾句号、缺失的空格、行内 `[更多介绍](url)` 链接前缺少「 - 」分隔符(标准格式是 `…描述 - [更多介绍](url)`,提交者漏写分隔符时要补上,历史事故:2026-09-02 StitchCraft 条目直接沿用了提交者缺分隔符的原文,被维护者纠正)。「产品类型后置」「形态信息放句尾用 — 带出」这类语序规则**只适用于我们从原始评论代拟描述的场景**(步骤1),对提交者已定稿的句子一律不适用——「自动运维插件」这样的句首定位语往往正是最精确的表达。历史事故:2026-08-27 处理 PR #1314(Vault Keeper)时,把提交者写在句首的「Obsidian vault 自动运维插件:…」机械套用 Wake/EasyDown 样式挪到句尾「— Obsidian vault 自动运维插件」,被维护者明确纠正回退。
---
## 身份判断:仅收录中国独立开发者(检查一、二、三共用)
仓库标题是「中国独立开发者项目列表」,收录范围严格限定为**中国独立开发者**的项目。检查一、二、三的每一位提交者,在进入「通用处理流程」步骤1之前(或检查三判断是否合并 PR 之前),都要先做这一步判断,但**默认是收录,只有确凿证据表明是老外才拒绝**。
**判断标准(满足以下任一条即可判定为中国人):**
1. 提交者用中文留言,且内容通顺自然、符合中文母语者的表达习惯(不是生硬的机器翻译腔)
2. 提交者的名字是拼音,或明显是中国人姓名(含港澳台常见姓名)
3. 提交者的 GitHub 主页能看出中国元素:例如 bio 是中文、有仓库的标题/描述是中文、location 写着中国城市/省份等
查证方式:
```bash
gh api users/<username> | jq '{name, bio, location}'
# 如上面三个字段都看不出结论,抽查几个仓库名称/描述辅助判断:
gh api "users/<username>/repos?sort=updated&per_page=10" | jq '[.[] | {name, description}]'
```
⚠️ 不能只看单一信号就下结论:
- 不能仅凭"提交内容是中文"就判定——要综合看,尤其留意机翻痕迹(用词生硬、语序不自然)
- 不能仅凭 GitHub 用户名/主页语言是英文就判定为老外——很多中国开发者也用纯英文用户名和英文 bio,需结合上面三条综合判断
- `location` 字段是强信号但非绝对:结合 name/bio/repos 一起看
**默认原则:能收就收,只有确认是老外才拒绝。** 三条证据都拿不到、无法判断时,**不要为了"稳妥"而按拒绝处理,而是照常收录/合并**——身份模糊不等于老外,中国开发者也经常是纯英文用户名 + 英文 bio + 无 location(很多产品面向海外、网站是英文的)。只有出现**明确的老外信号**(如 bio 自称外国人、位置明确在国外城市/国家、中文留言有明显机翻痕迹且无任何中文/中国元素、公开资料显示是外国团队)才拒绝。宁可多收,不要误拒。
**已确认收录的"模糊身份"先例(不再需要人工判断,直接照此收录):**
- 英文站点 + 中文自然留言,账号 profile 无任何中文痕迹 → 收录(例:MailMergeOnline,Linky-AIinlink,英文站 mailmergeonline.com,评论正文自然中文 → 收录主版面)
- profile 全空/全 fork/PR 正文英文,但 issue 正文自然中文 或 团队仓库里有中文成员 → 收录(例:SandBase CLI,denial123789,issue 中文自然、sandbaseai 团队有 liyb/163 邮箱 → 收录程序员版面)
- GitHub `name` 字段是外文名、bio / location 全空,但仓库描述全是中文项目 → 收录(例:Tancky AI,tancky777,name 显示 "Ramiro Livi",但仓库 Cursor-reset-tools、wechat-radar「微信聊天情报看板」、article-extractor「微信公众号」全中文 → 收录主版面)。**`name` 字段单独看最容易误判,必须看仓库描述的语言**(2026-09-22 补充)
- **现居海外 ≠ 老外,海外中国学生/研究员照常收录**(2026-10-05 补)。`location` 写 New York / Boston / San Francisco 等国外城市**不能单独作为拒绝依据**,要继续看:`name` 是不是中国姓氏的拼音(Lian / Wang / Chen / Zhang 这类)、`company` 里有没有中国高校(`SJTU`、`Tsinghua`、`Peking`、`ZJU` 等)、评论正文是不是自然中文。实测:lianyixin,`name: "Ethan Lian"`、`location: New York`、`company: "MSDS @ Columbia University | BE @ SJTU"`——Lian 是中国姓、SJU 是上海交大、评论是通顺自然的中文 → 收录主版面。**同理,`location` 写着中国城市也不能单独作为收录依据**,两边都要看,合起来才有结论。
- 作者本人更新自己已有的条目(改 URL / 优化描述)→ 合并,这不算"修改已有条目"的禁令范围,是作者维护自己的产品(例:MyServers,lovercode=codelover 更新官网 myservers.plus → 合并到主版面)
**判定为老外(确凿证据)后的处理:**
- 检查一(issue #160 评论):不进入「通用处理流程」,不修改任何 README,在 #160 该条评论下用提交者所用的语言礼貌回复说明原因即可(不需要额外关闭操作)
- 检查二(独立 issue):不进入「通用处理流程」,在该 issue 下礼貌回复后 `gh issue close <number>`
- 检查三(PR):不合并,在 PR 下礼貌评论后 `gh pr close <number>`(不使用 `gh pr merge`)
**拒绝评论模板**(用提交者使用的语言回复,语气礼貌简短,不解释具体判断依据;POST 后同样要 PATCH 并 GET 验证):
```
感谢分享 <产品名>!不过本仓库只收录中国独立开发者的项目(README 开头写明"聚合所有中国独立开发者的项目"),所以暂时不在收录范围内,抱歉。祝 <产品名> 发展顺利!
```
英文示例:
```
Thanks for sharing <product>! This repo specifically curates projects made by Chinese independent developers, so it's outside the scope of this list. Good luck with <product>!
```
---
## 补充拒绝类别:非独立开发者产品 / 分销返佣链接
「身份判断」只解决了"是不是中国人",但本仓库还要求**独立开发者自己的项目**。除了垃圾广告和确凿外国人,以下两类同样拒绝:
**1. 大厂 / 上市公司产品**(不是独立开发者做的)
判据:产品官网属于某公司,且该公司是上市公司或有规模的团队(如商汤 Seko → `seko.sensetime.com`,官网产品页明确归属商汤科技)。提交者只是推荐,不是作者。
**2. 带分销返佣参数的推广链接**
判据:落地 URL 里出现渠道追踪参数,例如 `cg_click_id` / `cgv` / `utm_source=cakegrowth_gzh`(cakegrowth 是推广分销平台)、`ref=` / `invite=` / `aff=` 等。说明提交者是在用本列表赚渠道佣金,不是作者推荐自家产品。
**3. 刷量 / 互赞平台(Stars-as-a-Service)**
判据(命中任一条即可拒绝):
- 按 Star / Watch / Fork / 关注数**计价**,卖「积分/点数/套餐」,明码标价(例:`$5 / 20 积分`,每完成一次 Star、Watch 或 Fork 扣 1 积分)
- 承诺「xx 天不掉赞 / 掉赞返积分」这类**保留率兜底**——正常产品没有这个东西,因为它默认这些互动会被撤回
- 把互动打包交付:页面上每个仓库的 Star 数和 Watch 数**完全相同**(如 43/43、55/55)。真实仓库这两个数字差一个数量级,相等只可能来自批量任务交付
⚠️ 这类平台会把「我们用真人账号、不用机器人号」写进宣传语来自证清白,还常自称「开发者互助社区」「互赞」「曝光交换」。**用真人账号刷的和用机器人刷的,性质一样**,都是 GitHub Acceptable Use Policies 明令禁止的「inauthentic interactions」「creation of or participation in secondary markets for the proliferation of inauthentic activity」。不要被「真实开发者」「互赞社区」的话术带偏。
**必做的核查动作**(对任何可疑链接,检查一/二/三通用):光看提交者给的域名不够,短链和渠道域会伪装,必须看**最终落地的 URL**:
```bash
curl -sL --max-time 20 "<提交者给的URL>" -o /dev/null -w "HTTP:%{http_code} FINAL_URL:%{url_effective}\n"
curl -sL --max-time 20 "<提交者给的URL>" | grep -oE '<title>[^<]*</title>|og:url[^>]*' | head -5
```
- `FINAL_URL` 与提交的域名不同 → 是跳转域,看落地域名到底归谁
- 落地 URL 带上述渠道参数 → 判为返佣推广链接
- 落地页 title / og:url 暴露的公司名与提交者无关 → 判为大厂产品
⚠️ **本机 `curl` 打不开产品站点时,不能据此判定站点有问题——必须换 `WebFetch` 工具复核一次再下结论。**(2026-09-23 补)
本机走本地代理,`curl` 常因出口 IP 被目标站/CDN 拒而报 `HTTP:000` + `LibreSSL SSL_connect: SSL_ERROR_SYSCALL`(TLS 握手被重置),
`--http1.1` / `--tlsv1.2` / `www.` 前缀都救不回来;这不代表站点挂了。
实测 ins-transcript.com 的 curl 三次全 000,而 `WebFetch` 一次就拿到了完整页面正文(真实 freemium 产品)→ 收录主版面。
处置顺序:`curl` 失败 → 用 `WebFetch <url>` 复核(工具侧走自己的网络路径,不受本机代理影响)→ 拿到正文就正常判版面;两边都拿不到才需要考虑"无法核实"。
⚠️ **不要把「返回 5xx / 限流页」当成"站点挂了"或"无法核实"。**(2026-09-30 补)
curl 与 WebFetch 都拿到 503 时,先读那个错误页的正文:如果页面自述是「今日访问量已达上限,明早 08:00 自动恢复」这类**站点自己的日限额流**,说明站点真实在跑、还跑得动,正常收录。
是「限流」还是「废弃」,区别就在错误页正文有没有站长自己写的说明。实测:yiminshuju.com 双通道都 503,正文写明「数据服务今日访问量已达上限,北京时间 08:00 自动恢复」→ 收录主版面。
⚠️ **403 Cloudflare 拦截页同样不等于"站点挂了/无法核实"。**(2026-10-05 补)
curl 与 WebFetch 都拿到 `403 Attention Required! | Cloudflare`「Sorry, you have been blocked」时,那是 WAF 按来源 IP/机房段拦的**访问者拦截**,
跟站点本身死活无关。判据:错误页写的是「You are unable to access <域名>」「触发原因可能是提交了某个词或 SQL 命令」——主语是**你被拦**,
不是站点报错。此时按「能收就收」+ 提交内容本身(详细中文正文、URL 干净无追踪参数)正常收录,不要以"无法核实"拒绝。
实测:tanzhang-market-kit.hypebeast2k17.chatgpt.site 双通道 403 → 收录主版面。
⚠️ **托管面域名 ≠ 大厂产品,不要因为域名带大厂字样就判"非独立开发者"。**(2026-10-05 补)
常见托管面:`*.pages.dev`(Cloudflare)、`*.github.io`、`*.vercel.app`、`*.netlify.app`、`*.chatgpt.site`(OpenAI,
根域 `chatgpt.site` 301 跳到 `chatgpt.com/features/sites/`)。提交者只是把自建页面挂在上面,性质和 GitHub Pages 一样,
不构成"大厂产品"这个拒绝理由。顺手 `whois <根域>` 能确认归属,但结论不影响收录判断。
⚠️ 这两类都**先查证再下结论**,不要凭域名观感猜。顺手看一眼 `gh api "repos/1c7/chinese-independent-developer/issues?state=all&creator=<username>&per_page=20"`,有推广前科的账号是强信号。
**拒绝后的处理**(与「身份判断」章节一致,用提交者的语言):
- 检查一(#160 评论):不收录,在该评论下发礼貌说明即可
- 检查二(独立 issue):礼貌评论后 `gh issue close <number>`
- 检查三(PR):礼貌评论后 `gh pr close <number>`
**拒绝评论模板**(中文,只说结论,不罗列证据):
```
@<用户名> 感谢推荐!不过本仓库只收录中国独立开发者自己的项目,<产品名> 是 <公司名> 的产品,暂时不在收录范围内,抱歉。祝 <产品名> 发展顺利!
```
POST → 捕获 ID → PATCH 覆写 → GET 验证正文(流程同其他评论,签名不得残留)。
**已确认的先例:**
- 2026-09-17 Issue #1394「Seko,一款AI短剧平台」(wu1064442747):`seko.cgref.cn/s/4onyjv2njx` 落到 `seko.sensetime.com/explore?cg_click_id=...&cgv=4onyjv2njx&utm_source=cakegrowth_gzh`,是商汤科技的产品 + cakegrowth 渠道返佣链接;该账号 9/2 的 #1332 也是推广自家 API 中转站(已关闭)→ 拒绝并关闭 issue。
- 2026-09-20 Issue #1402「GithubStarMate」(adevelle):`githubstarmate.com/zh-Hans`,页面自述销售推广积分(`$5/20 积分`,Star、Watch、Fork 各扣 1 积分;会员 `$24.90/月 ≈300 Star`;`90 天 Star 保障,掉赞返积分`)。初次运行时误按「能收就收」收进主版面,**同日核实后撤除**。两个实锤:① 站内仓库列表每行 Star 数与 Watch 数完全相等(43/43、14/14、55/55、74/74);② 抽查 GitHub API,站上标的 Watch 数与实际相差一两个数量级(标 43 Watch 实际 2 个;标 55 Watch 实际 0 个)→ 刷量平台,按类别 3 拒绝。**教训:站点自己写的「互动带有互助激励,不应将这些互动数据等同于独立用户评价」就是自曝,看到这句话直接进 FAIL 分支。**
⚠️ **类别 2(返佣)同样要逐条比对判据,不能因为"站点靠联盟链接赚钱"就整个判为推广站。** 判据限定在「**落地 URL** 里出现渠道追踪参数」。如果提交者提交的落地 URL 本身干净、站点是他自己建的、内容真实可免费访问且**无付费墙**,那么站内 affiliate 只是变现方式,不构成拒绝理由(2026-09-22 精选副业 jingxuan-fuye.pages.dev:站内主 CTA 是 `class="affiliate-link"` `data-affiliate-url` 的生财有术邀请入口、有站长邀请卡,但案例页正文完整可读无付费墙、100 篇案例有实质内容 → 收录主版面)。核查动作:抽一个内容内页(`/cases/...`、`/topics/...`)看正文长度与是否出现「正式加入后可查看完整内容」类付费墙,有实质内容就收。
⚠️ **`ref=` 参数本身不构成拒绝理由,先判「提交者是不是作者本人」再判「站点有没有佣金机制」。**(2026-09-25 补)
判据的实质是「提交者拿本列表赚渠道佣金」,所以只有两种情况按返佣拒绝:① 站点公开有联盟/分销计划(页面上出现 affiliate / referral / commission / 返佣 / 分销 / 邀请奖励 等字样);② 提交者明显不是作者,是第三方推手。
如果 ref 参数指向的就是本仓库(如 `?ref=1c7-xxxx`)、且提交者本人就是产品作者、全站找不到任何佣金字眼,那只是作者自己的来源追踪,**正常收录,URL 原样保留**。
核作者身份的办法:`gh api repos/<owner>/<repo>/contributors | jq -r '.[] | "\(.login) :: \(.contributions)"'`,贡献数最高的那个通常就是作者。
先例:2026-09-24 PR #1416(Orbi,`orbi.build/zh/?ref=1c7-2609`)——提交者 xqliu 在 `orbi-build/orbi` 有 1050 次提交(贡献者第一)、orbi.build 全站无 affiliate/返佣字样 → 判定为作者自建来源追踪,正常合并进程序员版面。归档里也早有 `jsonman.bakchoi.com/?ref=1c7` 这种先例。
⚠️ **提交者自己写的描述与站点实际可能不符,代拟描述时以站点 meta description 为准。**(2026-10-05 补)
同一个 AI 套壳批量投稿季,同一作者常在 issue 里把产品功能写错一两个关键参数,照抄就会把错误信息写进 README 且永久留存。必查动作:
```bash
curl -sL --max-time 20 "<url>" | grep -oE 'name="description" content="[^"]*"' | head -2
curl -sL --max-time 20 "<url>" | grep -oE '<title>[^<]*</title>' | head -2
```
实测(2026-10-05 weidacn 四个 issue):#1470 写 AI Zombie「上传一张照片即可生成」,站点 title 与 description 都是 **from Two Photos**(本人或宠物各一张);#1467 写 Flow AI Video「上传单张图片即可生成几秒的动态视频」,站点实际是 **Text & Image to Video 双模**、输出**带同步音效**、可选 Gemini Omni 1.1 Flash。两条都按站点改写了。凡是描述里出现**数量词、输入模态、是否带音频/水印、计费单位**这类可验证参数,都要用 meta description 复核一遍再落笔。
⚠️ **「affiliate」命中的三种上下文要分清,第三种是新出现的。**(2026-10-05 补)
除已有的「not affiliated with X, Inc.」商标免责声明外,2026-10-05 见到的是**蹭大牌模型做的 SEO 问答**:
`flowaivideo.im` 页面里写「Is Flow AI Video affiliated with Google? — No. flowaivideo.im is an independent service. It is not Google Flow and is not affiliated with or endorsed by Google.」
这是**用 FAQ 结构撇清与 Google Flow 的关系**,同样属于免责声明,与联盟分销无关,照常收录。看到 `affiliated` 时判断依据仍是:主语是「与某某无关」还是「有联盟计划」。
⚠️ 上面两条也划定了「能收就收」的边界:**默认收录只适用于「身份判断」(是不是中国人),不适用于「是不是真产品」。** 身份模糊照样收,但产品形态不符合收录标准(大厂、返佣、刷量)时该拒就拒,不受默认收录原则保护。
⚠️ **grep 到 `affiliate` 不等于返佣:先看上下文,常见的是「not affiliated with X, Inc.」免责声明。**(2026-10-02 补)
英文独立产品为了避嫌,几乎都会在页脚写「Not affiliated with Tesla, Inc.」「is not affiliated with or endorsed by ...」,
这里的 affiliated 是「从属于」,跟联盟分销毫无关系。命中关键字后必须 `grep -oiE '.{100}affiliate.{150}'` 看一眼上下文,
确认是"联盟计划/分销/邀请奖励"还是"与某某公司无关";后者正常收录。实测:teslawrapgenerator.com 全站多处 affiliated 字样,
全部是特斯拉商标免责声明,落地 URL 无追踪参数 → 收录主版面。
⚠️ **类别 3(刷量)的判据要逐条比对,不能凭"这个服务在帮人做推广"的观感扩大化。** GEO / AI 可见度优化 / 真人测试 / 软文发布这类推广服务,只要**不是按 GitHub Star/Watch/Fork 计价**、没有「掉赞返积分」兜底,就不属于刷量平台,按普通独立开发者产品收录(2026-09-22 NiubiGeo,niubigeo.ai,卖 AI 诊断报告 ¥99/份 + 真人提问测试按次计费,三条判据全不命中 → 收录主版面)。不要用"真人网络""组织传播"这类字眼自行扩大拒绝范围。
---
## 通用处理流程(适用于检查一和检查二,且已通过上方「身份判断」)
### 步骤1:提取信息并格式化
从原始内容智能提取,整理为标准格式。提交格式千奇百怪,需灵活判断:
**必须有(确属无法提取且无法从别处补全时,才归入拒绝类):**
- 制作者名字:用户没写则用其 GitHub 用户名代替
- 产品名称 + 可访问的产品 URL(http/https 开头)+ 一句话描述
> ⚠️ 这里「必须有」是从**垃圾广告**角度判断的:如果评论/Issue/PR 里有产品名、URL 和描述,就能提取收录。产品 URL 一般都能拿到(没有显式 URL 的,从 GitHub 仓库或链接里补全)。**拿不到 URL 也不代表就是拒绝**——尽量从提交内容里找(GitHub 仓库地址、评论区补发、PR 改动行里的链接都算)。只有那种纯广告语、无任何 URL/仓库、无法定位到任何产品的,才按垃圾广告拒绝。不确定时倾向于收录。
**可选(有则填,无则略去):**
- 城市、GitHub 链接、博客链接、更多介绍链接
**标准输出格式:**
```
#### 制作者名字(城市) - [Github](url)
* :white_check_mark: [产品名](url):一句话描述 - [更多介绍](url)
```
**格式规范:**
- ⚠️ 优先级最高:如果提交者在评论/Issue/PR 正文中明确表示不希望改动其项目简介(例如"请不要改动我的项目简介""请尽量不要改动我的项目简介"等),必须原文一字不动地采用其提供的描述文字,跳过下面所有格式清理规则(去营销词、语序调整、标点空格等),只做产品名/URL 是否合规的必要核对。尊重提交者意愿优先于统一格式。
- ⚠️ 下面几条语序类规则(产品名称提前、形态后置等)**只适用于我们从原始评论代拟描述的场景**。如果提交者已经在 PR diff / 评论里写好了 README 格式的条目,那是他的定稿:语序逐字保留,只改半角全角、句尾句号、缺失空格这类硬伤(详见检查三「格式整理的边界」)。
- 日期区块标题用北京时间:`TZ=Asia/Shanghai date +"%Y 年 %-m 月 %-d 号添加"`
- 描述末尾不加句号
- 去掉「高效、简洁、强大、快速、好用、一款、一个」等营销废话;「免费」若是核心特征则保留
- 描述不要以「免费的」开头:产品类型词放最前,「免费」用全角括号紧跟在类型词后面。例:「免费的间歇计时器」→「间歇计时器(免费)」,「免费的屏幕坏点测试工具」→「屏幕坏点测试工具(免费)」。(2026-09-09 维护者指定;对提交者已定稿的条目也同样适用,不受上条「定稿逐字保留」限制)
- 「在线」通常是无意义填充词(列表里的产品基本都是网页应用),描述里删去,如「免费的在线角色创建者」→「角色创建者(免费)」;仅当「在线」确是用来区分客户端/离线版的核心特征时才保留。(2026-09-09 维护者指定)
- 产品名称提升到最前面
- 描述开头不要重复"「产品名」是一款/是一个……"这种句式(产品名已经是链接标题,不需要在描述里再说一遍),第一句直接说这个产品解决什么问题/能做什么;产品形态、平台(微信小程序/免费/Chrome 插件等)这类次要信息放到描述最后,用「 — 」或分号带出,不要放在开头占据最显眼的位置
- 严禁使用加粗格式(不要用 **)
### 步骤2:分类
| 类别 | 判断标准 | 目标文件 |
|------|---------|----------|
| 主版面 | 打开即用的网站或 App,非游戏 | README.md |
| 程序员版面 | 需要命令行/写代码/安装依赖 | .github/pages/README-Programmer-Edition.md |
| 游戏版面 | 任何游戏类产品 | .github/pages/README-Game.md |
| 拒绝 | 论坛、无 URL、垃圾广告、提交者确凿是老外(见上方「身份判断」)、或大厂产品/分销返佣链接(见「补充拒绝类别」) | 不处理 |
⚠️ 个人博客不算独立"产品",不作为单独的 `* :white_check_mark: [产品名](url):...` 条目收录(无论是在评论/Issue 里单独提交,还是和其他产品一起夹带提交)。如果提交内容里包含个人博客链接,按 CONTRIBUTING.md 的模板把它放进作者信息行,写成 `#### 制作者名字(城市) - [Github](url), [博客](博客url)`,不要单独起一行当产品处理。这条同样适用于检查三的 PR:PR 里如果夹带了博客条目,即使 PR 整体因为改了 README 且含产品名+URL 被判定为"有效提交"要合并,合并后仍要单独检查其中每一行是否真的是产品,博客类条目要按上面方式改成作者信息里的链接,不能因为"PR 已经通过整体有效性检查"就跳过逐行审查。
⚠️ 不要盲信提交者对自己产品/链接的自我描述,尤其是"博客"这类字眼。历史上出现过提交者把链接写成"个人技术博客",实际打开后是网址导航站(且含疑似盗版影视资源链接),完全不符合收录标准。处理任何"博客"链接前,用 `curl -sL <url> | grep -o '<title>[^<]*</title>'` 和 meta description 快速核对一下页面实际内容是否真的是博客(文章列表/写作内容),而不是导航站、工具集合站等被包装成"博客"的东西。如果标题/描述显示是"导航""聚合""网址""hao123 类站点"等关键词,即使提交者自称是博客,也不要收录,按拒绝处理并说明原因。
### 步骤3:插入文件并批量提交到 master
先读取目标 README 了解格式,再用本地编辑工具插入条目。**新条目插入当天日期区块的最顶部**(紧接日期标题行之后的空行后面)。绝不把条目写进 README 顶部的「### 子版面」清单(那里只放本仓库自己的页面,见文档开头的子版面禁令)。
如果当天日期区块尚不存在,则在最新日期区块之前新建。
⚠️ **例外:同一作者当天区块里已经有作者行时,新条目追加到那个作者行的条目列表末尾,不要另起一个重复的 `####` 作者行。** 现实场景是一位作者在几十秒内连开多个 issue(实测 2026-10-05:weidacn 在 50 秒内连开 #1464/#1465/#1466 三个 issue),而他当天区块里往往已经有前几次运行收录的产品。正确做法是先 `grep -n "#### <作者名>" README.md` 定位已有作者行,把新条目接在他的条目列表末尾(本次三个产品全部并入 10-05 区块里已有的 weidacn 块),只有该作者在当天区块里完全没出现过时才新建作者行。
⚠️ **主 README.md 容量检查(每次往 README.md 插入条目后必做)**:GitHub 对 README 渲染有截断限制,官方文档从未写明具体数字;GitHub 支持团队在工单里的答复是「blob 显示限制约 500 KB,超出部分 UI 直接截断」,社区实测约 512 KB(来源:github.com/orgs/community/discussions/23920)。被截断时仓库首页看不到底部内容且**没有任何警告**(历史实测:2026-09-09,README.md 527,086 字节时首页渲染到 2022年7月14号区块中间戛然而止)。处理规则:
- 每次插入条目后运行 `wc -c README.md`
- **一旦超过 490,000 字节,立即存档**:把 README.md 项目列表末尾最旧的一个或多个**完整日期区块**(从 `### 某日期添加` 标题行起,到下一个日期标题前的空行为止)整体剪切,插入 `.github/pages/README-Archive.md` 正文的最顶部(紧接其头部 `---` 之后的空行,存档内部保持时间倒序),并同步更新子版面清单和末尾 👉 指引行里的年份范围文字
- ⚠️ **不要只剪到刚好低于 490,000**(2026-09-23 补)。前几次运行都只剪刚够的量(剪完 489,534 / 489,637 / 486,xxx 附近),结果下一次运行只要再收 1 条就又触发存档,等于每次运行都要多搬一轮区块。**一次性剪到 487,500 字节左右**,留约 3,000 字节(≈10 条)余量,通常能撑好几次运行
- 挪完自查:README.md 回落到 490,000 字节以下;`grep -c "^### " README.md` 的减少数与存档增加数一致
- 存档按「渲染限制」而不是「年份」拆分,所以**文件名永远不带年份**(README-Archive.md),年份范围只出现在文件标题和链接文字里,每次扩容只改文字,不改文件名
- 存档文件自身也盯住 `wc -c`:接近 500,000 字节时把较新的一半拆成 README-Archive-2.md(编号拆分时就冻结,之后不再改名)
**所有项目的文件修改全部做完后**,统一一次性提交推送到 master:
⚠️ **`git pull` 必须在 `git commit` 之前**,不能在 commit 之后才想起来拉。检查三的 `gh pr merge` 会往远端 master 推新 commit,本地此时是落后的;如果先 commit 再 push,会被拒(`hint: the same ref... use 'git pull' before pushing again`),多一轮往返。2026-10-05 实测:合并 PR #1463 后直接 commit 问迹条目,push 被拒。正确顺序是「fetch/pull → 编辑 → add → commit → push」,或直接 `git pull --rebase origin master` 后 push(rebase 完**必须 `grep` 确认自己刚加的条目还在**,rebase 有可能把冲突解决搞丢)。
```bash
git checkout master && git pull origin master
# (用本地编辑工具对各 README 文件做完所有修改)
git add README.md .github/pages/README-Programmer-Edition.md .github/pages/README-Game.md
git commit -m "新增:<项目1名>、<项目2名>、..."
git push origin master
```
- 不建分支,不开 PR,直接推 master
- 所有项目合为一条 commit,commit message 列出所有项目名
- 如果本次没有任何有效项目,跳过 git 操作
---
## 最后一步:逐人发感谢评论并验证正文(仅针对检查一的提交者)
所有三个检查都处理完之后,如果检查一中成功收录了项目,在 issue #160 为**每位来自检查一的成功提交者单独发一条评论**:
```
@<用户名> 感谢提交,已将你的产品 <产品名> 添加到 <主版面/程序员版面/游戏版面>!
```
- 一条评论只 @ 一位用户,严禁把多位提交者合并到同一条评论
- 同一用户提交多个产品时,仍只发一条,在评论中列出所有成功收录的产品
- 同一用户的产品被收录到不同版面时,按版面分组说明,例如:`@user 感谢提交,已将你的产品 A、B 添加到主版面,并将 C 添加到程序员版面!`
- 检查二的提交者不要 @ 到这里(他们已在各自的 issue 里收到了感谢)
- 如果检查一没有成功处理任何项目,不发评论
每位用户的评论都必须分别执行 POST、立即 PATCH,再 GET 验证正文准确且没有历史自动化署名:
```bash
CLEAN_BODY="@user 感谢提交,已将你的产品 Product 添加到主版面!"
COMMENT_RESPONSE=$(gh api repos/1c7/chinese-independent-developer/issues/160/comments \
-X POST -f body="$CLEAN_BODY")
COMMENT_ID=$(echo "$COMMENT_RESPONSE" | jq -r '.id')
gh api --method PATCH \
repos/1c7/chinese-independent-developer/issues/comments/$COMMENT_ID \
-f body="$CLEAN_BODY"
gh api repos/1c7/chinese-independent-developer/issues/comments/$COMMENT_ID \
| jq -r .body
```
---
## 收尾扫描:无条件复查签名残留(每次运行都要做,包括「无新内容」的空跑)
前面每处发评论都要求 POST → PATCH → GET 验证,但实践证明这一步会静默失效:2026-06-23 至 07-28 期间累计有 **155 条**致谢评论带着签名一直没被清掉(#160 里 70 条,各 issue/PR 上另有 85 条),横跨 `1c7` 和 `claude[bot]` 两种身份,而每次运行都自认为验证通过了。所以不能只依赖发评论时的那次自检,**每次运行结束前必须再无条件重扫一遍**。
⚠️ 扫描范围必须是**整个仓库**,不能只扫 #160——检查二、检查三的致谢评论发在各自的 issue / PR 上,只扫 #160 会漏掉一大半。用仓库级评论接口,它支持 `sort`/`direction`,一次就能拿到全仓库最近的评论:
```bash
# 必须先把结果落盘再解析:gh api 直接管道给 jq 时,网络 EOF 会让 jq 收到空输入、
# 静默输出 0 条,看起来像「已经干净」——这是假阴性,务必用重试 + 落盘。
# 必须分页扫:只取 per_page=100 的单页只能看到最近 100 条,历史遗留的脏评论永远扫不到
# (2026-09-15 运行确认的问题)。至少扫 5 页(500 条)。
for p in 1 2 3 4 5; do
for try in 1 2 3; do
gh api "repos/1c7/chinese-independent-developer/issues/comments?sort=created&direction=desc&per_page=100&page=$p" \
> /tmp/sig_scan_$p.json 2>/dev/null && break
sleep 3
done
done
jq -s 'add' /tmp/sig_scan_[1-5].json > /tmp/sig_scan_all.json
jq -e 'type == "array" and length > 0' /tmp/sig_scan_all.json >/dev/null \
|| { echo "扫描接口没拿到数据,本次扫描无效,必须重试后再判断"; exit 1; }
# 末尾的 `| .[]` 不能省:直接输出 JSON 数组时,空结果也会被写成一行 `[]`,
# 导致 wc -l 报「检出待修 1 条」的假阳性(2026-09-17 实测踩到)。
jq -r '[.[] | select(.body | test("Generated by")) | select(.user.login as $u | ["1c7","claude[bot]"] | index($u)) | .id] | unique | .[]' \
/tmp/sig_scan_all.json > /tmp/sig_dirty.txt
echo "已扫评论: $(jq 'length' /tmp/sig_scan_all.json) 条"
echo "检出待修: $(grep -c . /tmp/sig_dirty.txt) 条"
```
⚠️ 上面的 `select(.user.login ...)` 过滤不能省:**只修我们自己(`1c7` / `claude[bot]`)发的评论**。贡献者回复时经常用 `>` 引用我们带签名的原文,那是他本人的评论,一个字都不许改(例:#1155 的 5009781050 号评论)。
扫到任何 ID 就当场修掉,逐条 PATCH 后 GET 复核:
```bash
while read -r ID; do
gh api "repos/1c7/chinese-independent-developer/issues/comments/$ID" \
| jq '{body: (.body | sub("\\n*---\\n*_Generated by \\[Claude Code\\]\\(https://claude\\.ai/code\\)_\\s*$"; "") | rtrimstr("\n"))}' \
> /tmp/sig_payload.json
# 空正文保护:绝不把评论清空
[ "$(jq -r '.body | length' /tmp/sig_payload.json)" -eq 0 ] && { echo "跳过 $ID(正文会被清空)"; continue; }
gh api --method PATCH "repos/1c7/chinese-independent-developer/issues/comments/$ID" --input /tmp/sig_payload.json > /dev/null
gh api "repos/1c7/chinese-independent-developer/issues/comments/$ID" | jq -r '.body'
done < /tmp/sig_dirty.txt
```
⚠️ 写这类批量脚本时**不要用 `echo "$json" | jq` 传递 JSON**——`echo` 会把字符串里的 `\n` 当转义符展开,导致 jq 全线解析失败。一律用文件(`> file` 再 `jq ... file` / `--input file`)传递。
⚠️ **不允许凭脚本自己打印的「成功 N 条」就认为修完了。** 历史上出现过循环里 jq 静默失败、计数逻辑却报「成功 70 失败 0」、实际一条都没改的情况。必须重新拉一次评论列表、再次 `grep "Generated by"` 确认残留计数为 0(贡献者引用产生的那几条除外),才算这一步完成。
⚠️ 偶发的 `Post/Get "https://api.github.com/...": EOF` 是网络抖动,不是真失败。上面的循环已经带 3 次重试;如果收尾复查时仍有少量残留,单独把这几个 ID 再跑一遍即可,不要因此判定整批失败。
---
## 运行结束向用户的汇报格式(严格遵守,2026-09-19 用户指定)
全部处理(含收尾扫描)做完后,给用户的最终汇报**只列结果,不列过程**:
- 按版面分组列出本次新增的产品名,一行一个版面:「**主版面新增**:产品A、产品B」「**程序员版面新增**:产品C」「**游戏版面新增**:产品D」
- 有作者本人更新条目的(换 URL、改描述),单列一行:「**主版面更新**:产品X(作者本人改的)」
- 最后一行给总数:「共 N 项,全部处理完毕。」
- 本次没有任何收录时,只说「本次无新收录」一句
- **禁止**在汇报里写任何处理过程细节:跳过的重复项、已有处理记录的评论、身份判断、diff 审查、URL 去重、致谢评论、收尾扫描结果、README 容量数字、commit 哈希等一律不写。用户只关心「加了什么产品到什么版面」,过程信息是在浪费用户时间
格式示例:
```
**主版面新增**:Unit Converter for Science、Color Season AI、Random Animal Generator、Jev AI
**程序员版面新增**:Agents Anywhere、OrcaReplay
**主版面更新**:cv.cm(作者本人改的)
共 7 项,全部处理完毕。
```
---
## 注意事项
- ⚠️ 环境问题:`gh` 有时不在 shell 的 PATH 里(报 `command not found: gh`)。macOS 上先跑 `export PATH="/opt/homebrew/bin:$PATH"`;用可执行文件的绝对路径而不是猜测。所有 GitHub 操作仍必须是 `gh` / `git` 命令行,不能用 GitHub Connector
- 幂等性靠 URL grep 检查保证,不依赖 reaction 标记
- **仅检查一、检查二**(issue #160 评论 / 独立 issue)来源的内容:所有文件修改完成后统一一次 commit 推 master,不建分支、不开 PR。**这条不适用于检查三的 PR**——PR 来源的内容必须走上文「检查三」定义的真正合并流程(`gh pr merge` 或本地 `git merge --no-ff` 保留贡献者归属),禁止把 PR 里的内容当成检查一/二那样直接誊抄进 master 再关闭 PR(历史事故见上文 #1220/#1221/#1226/#1227)
- 三个检查都没有新内容时,跳过处理流程,但仍要跑「收尾扫描」再结束
- ⚠️ **收尾复查时 `issues?state=open` 会把刚关闭的 issue 仍列成 open**(列表接口缓存延迟,2026-10-05 实测 #1467 关闭后仍出现在 open 列表里)。看到自己刚处理的 issue 还挂在 open 列表时,**不要重跑关闭命令**,单独 `gh api repos/1c7/chinese-independent-developer/issues/<number> | jq '{state, state_reason}'` 确认真实状态即可。