# Chinese Indie Dev

> 处理 1c7/chinese-independent-developer 仓库的项目提交。 检查 issue #160 新评论、新开的 Issue、新开的 PR， 将有效项目添加到对应 README，关闭垃圾内容。 当用户要求“收录项目”“处理提交”“处理 issue”“跑一下列表”或运行中国独立开发者列表维护流程时使用。

- Skill: `1c7/chinese-indie-dev` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add 1c7/chinese-indie-dev`
- Raw SKILL.md: https://api.skillmd.com/api/skills/1c7/chinese-indie-dev/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: 1c7 (https://skillmd.com/u/1c7)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/1c7/chinese-indie-dev

---


# 中国独立开发者列表：处理项目提交

你的任务是处理 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 新评论数
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"
```

如果三个数字**全部为 0**，输出「无新内容」，跳过检查一、二、三和通用处理流程，**但仍然必须执行文末的「收尾扫描」**（那一步与本次有没有新提交无关），扫完再结束。

只要有任意一个数字 > 0，才继续向下执行。

---

## 检查一：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 不做任何操作
- URL 不存在 → 进入「通用处理流程」
- 当前运行中已通过 PR 合并的条目，视为已存在，不再重复处理

⚠️ **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 的作者信息）推送后如果因为权限或分支保护等原因失败，视为该路径不可行，降级到步骤 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 邮箱 → 收录程序员版面）
- 作者本人更新自己已有的条目（改 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：提取信息并格式化

从原始内容智能提取，整理为标准格式。提交格式千奇百怪，需灵活判断：

**必须有（确属无法提取且无法从别处补全时，才归入拒绝类）：**
- 制作者名字：用户没写则用其 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 顶部的「### 子版面」清单（那里只放本仓库自己的页面，见文档开头的子版面禁令）。

如果当天日期区块尚不存在，则在最新日期区块之前新建。

⚠️ **主 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` 正文的最顶部（紧接其头部 `---` 之后的空行，存档内部保持时间倒序），并同步更新子版面清单和末尾 👉 指引行里的年份范围文字
- 挪完自查：README.md 回落到 490,000 字节以下；`grep -c "^### " README.md` 的减少数与存档增加数一致
- 存档按「渲染限制」而不是「年份」拆分，所以**文件名永远不带年份**（README-Archive.md），年份范围只出现在文件标题和链接文字里，每次扩容只改文字，不改文件名
- 存档文件自身也盯住 `wc -c`：接近 500,000 字节时把较新的一半拆成 README-Archive-2.md（编号拆分时就冻结，之后不再改名）

**所有项目的文件修改全部做完后**，统一一次性提交推送到 master：

```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 条，看起来像「已经干净」——这是假阴性，务必用重试 + 落盘。
for i in 1 2 3 4 5; do
  gh api "repos/1c7/chinese-independent-developer/issues/comments?sort=created&direction=desc&per_page=100" \
    > /tmp/sig_scan.json 2>/dev/null && break
  sleep 3
done
jq -e 'type == "array" and length > 0' /tmp/sig_scan.json >/dev/null \
  || { echo "扫描接口没拿到数据，本次扫描无效，必须重试后再判断"; exit 1; }

jq -r '.[] | select(.body | test("Generated by")) | select(.user.login as $u | ["1c7","claude[bot]"] | index($u)) | .id' \
  /tmp/sig_scan.json > /tmp/sig_dirty.txt
echo "检出待修: $(wc -l < /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 再跑一遍即可，不要因此判定整批失败。

---

## 注意事项

- 幂等性靠 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）
- 三个检查都没有新内容时，跳过处理流程，但仍要跑「收尾扫描」再结束

