mcpField
R
research-archival

research-archival

github.com/terrylica生产力1126 次安装1 天前更新
代码仓库

关于 research-archival

Research Archival 是用于归档 AI 研究对话和网页的技能,可将抓取内容保存为原始文本及带 YAML frontmatter 的编辑稿,并创建关联的 GitHub Issue。流程要求进行身份验证,并保留原始抓取内容的校验信息与指针。

主要功能

  1. 01抓取 AI 对话和网页
  2. 02分别保存原始抓取与编辑稿
  3. 03记录原始文件 SHA-256
  4. 04创建并交叉引用 GitHub Issue
  5. 05按来源类型路由抓取方式

适用场景

  1. 01归档 ChatGPT、Gemini 或 Claude 对话
  2. 02保存网页研究资料及其来源凭证
  3. 03将研究档案关联到 GitHub Issue
  4. 04在写入 Issue 前核验 GitHub 身份

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/terrylica/cc-skills --skill research-archival

安装后在 Claude Code 中输入/research-archival确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
evolution-log.md
frontmatter-schema.md
url-routing.md

详细介绍

研究归档

抓取 AI 研究对话(ChatGPT、Gemini、Claude)和网页,将其归档为带有 YAML frontmatter 的 Markdown 文件,并创建相互引用的 GitHub Issues——每一步都必须验证身份。

自我演进技能:此技能会在使用过程中不断改进。如果说明有误、参数发生偏差,或需要采取变通方案,请立即修复此文件,不要拖延。仅针对真实、可复现的问题进行更新。

首先——TodoWrite 任务模板

必须:任何归档工作开始前,选择并加载适当的模板。

模板 A - 完整归档(抓取 + 保存 + issue)
1. Identity preflight — verify GH_ACCOUNT or resolve via curl /user
2. Scrape URL — route to Firecrawl or Jina per url-routing.md
3. Save the RAW scrape — docs/research/raw/…{source_type}.raw-scrape.txt, byte-exact, never edited
4. Record its sha256 — over the raw file's exact bytes
5. Write the EDITORIAL .md — frontmatter incl. raw_scrape_path + raw_scrape_sha256, and a POINTER.
   Do NOT paste the transcript in; see "File Saving — TWO artifacts, and never one"
6. Survey labels — gh label list, reuse existing, max 3-6
7. Create GitHub Issue — use --body with heredoc or --body-file
8. Update frontmatter — add github_issue_url and github_issue_number
9. Post canonical backlink comment on Issue
模板 B - 仅保存(不创建 issue)
1. Identity preflight (still required for consistency)
2. Scrape URL — route to Firecrawl or Jina per url-routing.md
3. Save the RAW scrape as .raw-scrape.txt + record its sha256
4. Write the EDITORIAL .md with the provenance pin and a pointer — never the transcript itself
模板 C - 仅创建 issue(文件已存在)
1. Identity preflight
2. Read existing file frontmatter
3. Survey labels — gh label list, reuse existing, max 3-6
4. Create GitHub Issue — use --body with heredoc or --body-file
5. Update file frontmatter with issue cross-reference
6. Post canonical backlink comment on Issue

身份预检(必须 — 步骤 0)

必须在任何 gh 写入命令之前执行。不可妥协。

gh-repo-identity-guard.mjs PreToolUse 钩子提供了一层安全保障,但此技能会自行执行检查,以提供纵深防御。

解析顺序
  1. 显式覆盖 — GH_ACCOUNT 环境变量,仅当调用方导出该变量时才使用(现在不会再有任何东西注入它)
  2. API 调用 — 设置了 GH_TOKEN 时,执行 curl -sH "Authorization: token $GH_TOKEN" https://api.github.com/user
  3. 密钥环 — 在仓库的 gh.configdir 下执行 gh api user --jq .login(正确配置的机器上的常规路径)
验证

🔴 在 heredoc 内 cd 进入仓库,并自行导出 GH_CONFIG_DIR。 单独运行 /usr/bin/env bash 会启动一个新进程,不会继承交互式 shell 的函数;因此,在使用按所有者设置路径的机器上,你在该进程中调用的 gh 并不是你在提示符中调用的那个 gh。2026-09-06 的测量结果:在同一目录中,交互式 shell 的 gh api user 返回的是仓库自己的账号,而 heredoc 返回的是另一个全局激活的账号——这是一个会阻止正确归档的虚假不匹配,看起来却与真实凭据问题完全相同。其原因是 gh 是一个 shell 函数,它会使用从每个仓库的 gh.configdir git 配置键中获取的 GH_CONFIG_DIR 来包装二进制文件,因此该配置键决定了使用哪个身份。在脚本中解析此值,而不是依赖调用方的 shell,检查就不会受 shell 影响。

/usr/bin/env bash << 'IDENTITY_EOF'
cd "$REPO_DIR" || { echo "BLOCKED — cannot cd to \$REPO_DIR"; exit 1; }

# Honour owner-per-path BEFORE calling gh. `gh.configdir` is a per-repository git config key, so it
# is invisible to a fresh shell unless read explicitly. Without this the next line asks the wrong
# account and the whole check is answered about a repo you are not writing to.
_cfgdir=$(git config --get gh.configdir 2>/dev/null)
if [ -n "$_cfgdir" ]; then
  export GH_CONFIG_DIR="${_cfgdir/#\~/$HOME}"
  unset GH_TOKEN GITHUB_TOKEN   # an ambient token silently outranks GH_CONFIG_DIR
fi

# Resolve authenticated user
if [ -n "${GH_ACCOUNT:-}" ]; then
  AUTH_USER="$GH_ACCOUNT"
  AUTH_SOURCE="GH_ACCOUNT"
elif [ -n "${GH_TOKEN:-}" ]; then
  AUTH_USER=$(curl -sf --max-time 5 -H "Authorization: token $GH_TOKEN" \
    https://api.github.com/user 2>/dev/null | grep -o '"login":"[^"]*"' | cut -d'"' -f4)
  AUTH_SOURCE="API /user"
else
  # `gh` keeps the credential in the OS keyring, so GH_TOKEN is frequently absent from the
  # environment on a correctly-configured machine. Asking gh is not a fallback, it is the
  # normal path -- and omitting it made the API branch return empty on every keyring setup.
  AUTH_USER=$(gh api user --jq .login 2>/dev/null)
  AUTH_SOURCE="gh api user (keyring, GH_CONFIG_DIR=${GH_CONFIG_DIR:-default})"
fi

# Resolve target repo owner.
# `github\.com` alone does NOT match an SSH host alias: this machine's own owner-per-path policy
# gives remotes like `[email protected]:terrylica/repo.git`, where the character after
# `github.com` is `-`, not `:` or `/`. The optional `[^:/]*` consumes the alias suffix.
REPO_SLUG=$(git remote get-url origin 2>/dev/null | sed -n 's|.*github\.com[^:/]*[:/]\([^/]*/[^/.]*\).*|\1|p')
REPO_OWNER=${REPO_SLUG%%/*}

echo "Authenticated as: ${AUTH_USER:-<unresolved>} (via $AUTH_SOURCE)"
echo "Target repo owner: ${REPO_OWNER:-<unresolved>}"

# FAIL CLOSED. If either side is empty the comparison below is "" = "" and the guard reports
# SUCCESS while knowing nothing -- the exact green-signal-is-a-proxy failure this repo catalogues.
# Observed 2026-08-20: an SSH-alias remote produced an empty owner, an absent GH_TOKEN produced an
# empty user, and the check printed "Identity verified".
if [ -z "${AUTH_USER:-}" ] || [ -z "${REPO_SLUG:-}" ]; then
  echo ""
  echo "BLOCKED — identity could not be RESOLVED, which is not the same as verified."
  [ -z "${AUTH_USER:-}" ] && echo "  authenticated user : unresolved (set GH_ACCOUNT, or check \`gh auth status\`)"
  [ -z "${REPO_SLUG:-}" ] && echo "  repo slug          : unresolved from $(git remote get-url origin 2>/dev/null || echo '<no origin>')"
  exit 1
fi

if [ "$AUTH_USER" = "$REPO_OWNER" ]; then
  echo "Identity verified (personal repo, owner == authenticated user)"
  exit 0
fi

# NOT automatically a failure. On an ORGANISATION repo the owner is the org, never a user, so a
# string comparison can never match however correct the credential is. The real question is
# whether this identity may write here.
API_RC=0
REPO_JSON=$(gh api "repos/$REPO_SLUG" 2>/tmp/gh-identity-err) || API_RC=$?
OWNER_TYPE=$(printf '%s' "$REPO_JSON" | jq -r '.owner.type // empty')
CAN_PUSH=$(printf '%s' "$REPO_JSON" | jq -r '.permissions.push // empty')

if [ "$OWNER_TYPE" = "Organization" ] && [ "$CAN_PUSH" = "true" ]; then
  echo "Identity verified (organisation repo; $AUTH_USER holds push on $REPO_SLUG)"
  exit 0
fi

echo ""
echo "MISMATCH — do NOT proceed with gh write commands"
# Print what GitHub actually returned. A field that is absent prints as nothing, because absent is
# a state -- substituting a word like "unknown" would report a value GitHub never sent.
[ -n "$OWNER_TYPE" ] && echo "  owner.type       : $OWNER_TYPE"
[ -n "$CAN_PUSH" ] && echo "  permissions.push : $CAN_PUSH"
[ "$API_RC" -ne 0 ] && echo "  gh exit code     : $API_RC"
[ -s /tmp/gh-identity-err ] && echo "  gh stderr        : $(head -1 /tmp/gh-identity-err)"
echo "Fix: export GH_TOKEN=\$(~/.claude/tools/bin/gh-token-for-repo)"
exit 1
IDENTITY_EOF

如果不匹配,则阻止操作 — 显示诊断信息,并且不要继续执行任何 gh 写入操作。

为什么存在组织分支。 最初的检查条件是 AUTH_USER != REPO_OWNER → block。在组织仓库中,所有者是组织,因此无论凭据多么正确,两者都不可能匹配——这会阻止所有向共享仓库进行的合法归档。2026-08-20 在 example-org/research-repo 上的测量结果:已验证身份为 terrylica,所有者为 example-org,owner.type = Organization,permissions.push = true。这就是正确的身份,但检查却将其判定为不匹配。

此保护机制的目的是阻止向错误账号写入;而“此身份是否有权在此处写入”应由 permissions.push 判断,而非与所有者进行字符串比较。个人仓库的相等性检查路径仍作为快速的离线路径保留;组织路径只需额外调用一次 API,并且仅在不相等时才会执行。


抓取工作流

根据 URL 模式对抓取请求进行路由。完整详情请参阅 url-routing.md。

决策树
Any JS-rendered share link (chatgpt.com/share/, gemini.google.com/share/, claude.ai/artifacts/)
  → Firecrawl public API (POST https://api.firecrawl.dev/v2/scrape)
  → Use curl (not WebFetch — it summarizes instead of returning raw)

Simple static page
  → Either works. Jina Reader (https://r.jina.ai/{URL}) is one GET and fine here.

默认使用 Firecrawl。 2026-08-13 对两个 chatgpt.com/share/* 链接的测量结果显示,Jina 返回的内容只有 Firecrawl 的 17% 和 12%,并且在句子中途截断;Firecrawl 两次都到达了页面真正的页脚。Jina 还需要 -H "x-timeout: 30",否则只会返回约 321 字节的登录界面内容。

Firecrawl 抓取

不做健康检查、不做预检、不做恢复。公共 API 无需密钥,也没有可能宕机的主机——请求失败时按单个请求处理,而不是先做存活探测再决定是否运行。

/usr/bin/env bash << 'SCRAPE_EOF'
set -euo pipefail

# waitFor gives the SPA time to render; without it a share link returns the shell.
RESPONSE=$(curl -sS --max-time 180 -X POST https://api.firecrawl.dev/v2/scrape \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg u "$URL" \
        '{url: $u, formats: ["markdown"], waitFor: 8000, timeout: 60000}')")

CONTENT=$(printf '%s' "$RESPONSE" | jq -r '.data.markdown // empty')

if [ -z "$CONTENT" ]; then
  echo "ERROR: Firecrawl returned no markdown:" >&2
  printf '%s\n' "$RESPONSE" | head -c 400 >&2
  echo "Retry once, then fall back to: curl -H 'x-timeout: 30' https://r.jina.ai/${URL}" >&2
  exit 1
fi

printf '%s\n' "$CONTENT"
SCRAPE_EOF

不要重新引入自托管的 Firecrawl。 gpu-host-2 部署(端口 3002/3003,共 5 个容器)已于 2026-08-13 退役,并释放了约 18 GB。它需要健康检查、容器重启和 WORKER-STALLED 故障排查,而在当前用量下,公共 API 已无需这些操作。


文件保存——两个文件,绝不能只有一个

归档会生成两个文件。这是本页最重要的一点,因为弄错的代价很高,而且只有当有人尝试验证归档时,这个代价才会显现。

docs/research/raw/YYYY-MM-DD-{slug}-{source_type}.raw-scrape.txt   the scrape, byte-exact, NEVER edited
docs/research/YYYY-MM-DD-{slug}-{source_type}.md                   editorial + a POINTER to it
  • slug — kebab-case 摘要(最多 50 个字符)
  • source_type — 从枚举中选择:chatgpt、gemini、claude、web
🔴 不要把文字记录粘贴到 .md 中

这样做看似理所当然,实际上是错的。Research-repo PR #540 删除了一份重复的文字记录,前后经过 5 轮评审,每轮都发现一种会改变含义的编辑,而等价性检查器却判定内容完全相同:结构标记被删除(引用内容被降格为作者自己的断言),行内分隔符被删除(`not` 被读作英文单词),标识符和 URL 路径经由 _ 和 __ 改写,表格单元格边界被移动,导致结果对应到了错误的引用,以及字面星号在转义符、围栏代码块、缩进代码块和原始 HTML 属性中被吞掉。

最后一种情况属于类别错误,而非 bug。CommonMark 0.31.2 规定了解析分为两个阶段——"第一阶段会逐行读取输入,并构建文档的块结构……文本会被分配到这些块中,但不会被解析。……第二阶段会将段落和标题的原始文本内容解析为一系列 Markdown 行内元素"——因此,面向行的规范化器没有块解析阶段,无法区分段落和围栏代码块。再怎么打补丁也无法解决问题。

这个问题为何会出现:任何会重新格式化 Markdown 的仓库工具(这里是运行 prettier --write 和 markdownlint-cli2 --fix 的 itp-hooks Stop hook)都会让粘贴的副本无法保持字节级完全一致,因此每次提交时,你都必须回答“副本是否仍在表达与原文相同的内容?”这个问题。只有一份内容,就不存在这个问题。

强制仅保留一份:测量重复内容,绝不匹配标记

第一个门禁通过查找旧版 `` 注释来阻止重复嵌入。一次审查就绕过了它:在普通的 --- 后追加整份转录,省略标记,门禁便会返回成功——而名为 test_re_embedding_the_transcript_is_refused 的测试仍然通过,因为该测试使用标记来构建测试夹具。把模式匹配说成结构性保证,却只在 bug 不可能出现的那种输入上验证。

重复嵌入是一个近似重复检测问题,因此应采用这一问题的标准度量:Andrei Z. Broder 在《On the resemblance and containment of documents》,SEQUENCES 1997 中提出的基于 w-shingle 的_包含度_,doi:10.1109/SEQUEN.1997.666900——“A 在 B 中的包含度 c(A,B) 是一个介于 0 和 1 之间的数,当它接近 1 时,表示 A 大致包含在 B 中”,基于_D 中所有大小为 w 的 shingle 所组成的袋(多重集)_。

四点让它在实践中奏效:

  • 包含度,而非相似度。 问题具有方向性——转录有多少内容再次出现——而相似度是对称的,因此较长的编辑内容会稀释结果,掩盖整段粘贴。
  • 校准阈值,而非凭空设定。 测量真实存档和一系列不同粘贴比例。我们的结果:合法引用为 0.0097 → 粘贴十分之一为 0.1172 → 整份内容为 1.0000,因此 0.10 的上限约为引用值的 10 倍,同时低于所有有意义的粘贴比例。将这一系列结果和常量一同公开,否则这个数字看起来就像是随意猜的。
  • 将字母数字字符转换为小写 token,这样即使经过格式重排,度量仍然有效,而正是格式重排让字节比较无法奏效。即使经过 prettier 处理,重复嵌入的转录仍必须能被检测到。
  • 使用集合,而非多重集。 Broder 定义的是一个袋;集合回答的是“有多少_不同的_内容被重现”,因此同一片段粘贴 20 次也只计一次——这正符合这里的语义。

将不变量的范围限定为整个仓库,而非单个文件。 第二种绕过方式很简单:移动副本。只扫描_固定_转录的存档会报告成功,即便同级的 .md 文件中包含了 100% 的转录。扫描所有 Markdown 文件,并且测量它们拼接后的内容——12 个片段各自都低于上限,仍可能拼出整份转录。

应明确说明这两项限制,而不是声称它们不存在:单行引用仍必须通过(存档本身就包含约 1% 的合法引用,因此任何会拒绝一行粘贴内容的阈值都会彻底禁止引用);高度改写的重复嵌入会绕过这项度量——此时文档确实已不相同,任何自动检查都无法判定含义是否得以保留。这与导致规范化器失效的不可判定性相同;唯一的控制手段是审阅者的判断。

.txt 扩展名至关重要

将原始抓取内容存为 .txt,而不是 .md。Markdown 工具会匹配 *.md;原始抓取内容若以 .md 保存,就会被改写,导致其哈希失效,即使无人碰过它。通过扩展名从设计上使该文件不受影响,而不是依赖配置实现——可以额外添加 .prettierignore 和 .gitattributes -text 作为纵深防御,但它们并非实现这一点的机制。

YAML Frontmatter

完整的字段约定请参阅 frontmatter-schema.md。

---
source_url: https://chatgpt.com/share/...
source_type: chatgpt-share
scraped_at: "2026-02-09T18:30:00Z" # a REAL timestamp; never a rounded guess
scraped_at_source: "how you obtained it, e.g. mtime of the scraper's output file"
raw_scrape_path: docs/research/raw/YYYY-MM-DD-{slug}-{source_type}.raw-scrape.txt
raw_scrape_sha256: "sha256 of the raw file's exact bytes"
transcript_location: "not reproduced in this file; the sole copy is raw_scrape_path"
model_name: gpt-4o
claude_code_uuid: SESSION_UUID
github_issue_url: ""
github_issue_number: ""
---

raw_scrape_path 和 raw_scrape_sha256 必须成对出现。只声明其中一个比两者都不声明更糟:有哈希却没有文件,什么也证明不了;有文件却没有哈希,则未经验证,却看起来像是已固定。门禁应检测任意一个键,以便拒绝只声明一项的情况,而不是悄然跳过。

将 github_issue_url 和 github_issue_number 留空——在创建 Issue 后更新。

.md 中应包含的内容

仅写编辑性内容,而且值得认真撰写,因为这是人类会阅读的部分:

  1. 一条来源警示,说明此处未复现对话记录,且其内容是未经信任的 AI 输出。
  2. 一条审核说明——检查了哪些引文、采用了什么方法,以及发现了什么。要区分_身份_检查(DOI 是否指向该标题、作者和刊物)与_内容_检查(来源是否确实表达了摘要所声称的内容)。两者不同,混为一谈是常见错误。
  3. 源文件中的任何已知错误,应写在读者看得到的地方,而不只是 PR 中。
  4. 指向 .txt 文件的链接,并注明其大小和 sha256。

通过 Crossref API 按内容核验引文,切勿根据 HTTP 状态码判断——403 机器人拦截和 404 从状态行看无法区分。如果 Crossref 未提供摘要,请明确说明,并将相关说法标记为未经核验,而不要暗示已核验。

以编程方式将每一处逐字引文与检索到的文档逐一比对,并先检查提取结果,再相信比对结果。对 PDF 使用 textutil -convert txt -stdout 时,返回的字节数恰好等于文件大小——原始字节直接通过,却报告成功——任何引文都会因此在二进制噪声中“核验通过”。先根据页数检查提取文本的长度,再进行比对时忽略空白差异并折叠连字,因为提取过程会破坏词间空格。

比对失败时,先查明是哪种失败,再进行任何修改。我们遇到过两次失败:一次是提取器在句子中插入了页码(引文本身准确无误);另一次是引号内部的 ' 被替换成了源文中的 "。无论看起来多么微小,第二种情况都属于修改引文内容,这和把无法提取的字形替换成口述的名称是同一种缺陷。


创建 GitHub Issue

标签调研

先调研现有标签——优先复用,只有概念确实新颖时才创建标签。

gh label list --repo owner/repo --limit 100

政策:每个 Issue 最多使用 3-6 个标签。常见标签:research、ai-output、chatgpt、gemini、archival。

创建 Issue

使用 --body 和 heredoc 进行内联撰写,或使用 --body-file 处理非常长的内容。

/usr/bin/env bash << 'ISSUE_EOF'
# Write body to temp file
cat > "/tmp/issue-body-${SLUG}.md" << 'BODY_EOF'
## Summary

Brief description of the archived research content.

## Source

- **URL**: SOURCE_URL
- **Type**: source_type
- **Model**: model_name
- **Scraped**: scraped_at

## Key Findings

- Finding 1
- Finding 2

## Archived File

`docs/research/FILENAME.md`
BODY_EOF

# Create issue
gh issue create \
  --repo owner/repo \
  --title "Research: descriptive title here" \
  --body-file "/tmp/issue-body-${SLUG}.md" \
  --label "research,ai-output"

# Clean up
rm -f "/tmp/issue-body-${SLUG}.md"
ISSUE_EOF
更新 Frontmatter

创建 Issue 后,使用 Issue 的 URL 和编号更新归档文件的 frontmatter。


规范的反向链接评论

在 Issue 上发表评论,链接回归档文件。元数据应使用 Markdown 列表,而不是连续的散文行:

**Archived**: `docs/research/YYYY-MM-DD-slug-source_type.md`

- **Scraped**: 2026-02-09T18:30:00Z — scraper, byte count, whether it reached the page footer
- **Source**: [chatgpt-share](https://chatgpt.com/share/...)
- **Session**: `SESSION_UUID`
- **PR**: #NNN — commit `abcdef12`

不要恢复成连续裸行的写法。 GitHub 会将评论中的每个换行符渲染为 <br>,因此连续的四行 Key: value 看起来与硬换行的散文无法区分,而 GH-HARD-WRAP-GUARD PreToolUse 钩子会拒绝该命令(测量日期为 2026-08-20——本文件中先前的模板本身就是触发问题的内容)。列表通过结构表达“这些是不同的条目”,这既符合本意,也符合守护机制的要求。在这里使用 GH-HARD-WRAP-OK 覆盖选项,只会压制正确的警告。

每个 Issue/PR 正文都必须写成不间断的段落

这也适用于 Issue 正文和 PR 正文,而不仅是反向链接。每个段落都应写成一整行,让 GitHub 自动换行;只在列表项、标题、代码块和空行处换行。编辑器的软换行会让这一点不易察觉——可用以下命令检查:

awk '{ if (length($0) > 100 && $0 !~ /^[-*|#> ]/) printf "L%d: %d cols\n", NR, length($0) }' body.md

散文段落使用长行是预期且正确的;问题在于连续出现许多约 80–100 列的行。


修改后的检查清单

修改此技能后:

  1. YAML 前置元数据有效(描述中不含冒号)
  2. 描述中的触发关键词是最新的
  3. 所有 ./references/ 链接均可访问
  4. 身份预检部分仍位于工作流的首位
  5. 将更改追加到 evolution-log.md
  6. 验证:uv run plugins/plugin-dev/scripts/skill-creator/quick_validate.py plugins/gh-tools/skills/research-archival
  7. 验证链接:bun run plugins/plugin-dev/scripts/validate-links.ts plugins/gh-tools/skills/research-archival

故障排除

问题原因修复
GH-HARD-WRAP-GUARD 拒绝该命令正文采用了硬换行的行文方式将每个段落重新排为一行;对于堆叠的 Key: value 元数据,使用列表。不要尝试使用 GH-HARD-WRAP-OK——此检查是正确的
身份预检因组织仓库而拦截所有者是组织,因此 AUTH_USER != REPO_OWNER 始终成立这是预期行为——现在检查会继续判断 owner.type == Organization 和 permissions.push == true。如果仍被拦截,说明你确实没有推送权限
预检报告 MISMATCH,显示的账号并非你正在使用的账号,而且在提示符下 gh 可以正常工作heredoc 是一个全新的进程,没有 shell 函数,因此它调用了原始 gh 二进制文件,未找到每个仓库的 gh.configdir在 heredoc 内执行 cd,并在首次调用 gh 前,从 git config --get gh.configdir 导出 GH_CONFIG_DIR。分别在 heredoc 内外运行 gh api user --jq .login 进行确认——结果不同说明这是此问题,而非凭据错误
成功推送后,gh pr create 仍提示“you must first push the current branch”从关联的 worktree 运行;gh 无法解析上游显式传入 --head <branch> --base main。使用 git ls-remote --heads origin <branch> 确认该分支确实在远程仓库中
使用错误的账号发帖GH_TOKEN 不匹配在仓库内运行 git remote get-url origin(预期为 [email protected]<account>:owner/repo)和 gh api user --jq .login
正文超过 65536 个字符GitHub API 限制将内容拆分到 issue 正文和第一条评论中
Firecrawl 未返回 MarkdownAPI 暂时性故障重试一次,然后使用 Jina 并传入 -H "x-timeout: 30"
抓取结果只有页面外壳SPA 尚未完成渲染在请求正文中调高 waitFor(8000 → 15000)和 timeout
Jina 返回约 321 字节缺少超时标头添加 -H "x-timeout: 30"——不加时,Jina 返回的是登录页面框架
Jina 输出被截断Jina 对 JavaScript 密集型页面的覆盖不足这是预期行为——请使用 Firecrawl;在 2026-08-13 的测试中,Jina 的覆盖率为 17%/12%
身份检查拦截使用的账号不是所有者export GH_TOKEN=$(~/.claude/tools/bin/gh-token-for-repo)

参考资料

执行后复盘

此技能完成后,请在结束前检查:

  1. 命令是否成功? — 如果没有,请修复导致失败的指令或错误表。
  2. 参数或输出是否发生变化? — 如果底层工具的接口发生了变化,请相应更新 Usage 示例和 Parameters 表。
  3. 是否需要采用变通方法? — 如果你不得不临时变通(使用不同的标志、增加额外步骤),请更新此 SKILL.md,确保下次调用时无需采用相同的变通方法。

仅在问题真实且可复现时更新,不要根据推测进行更新。

评论

支持 Markdown