mcpField
R
research-archival

research-archival

github.com/terrylica生産性1126 回インストール1 日前に更新
リポジトリ

research-archival について

Research Archivalは、AIの研究会話やWebページを保存するスキルです。生の取得内容とYAML frontmatter付きの編集用Markdownを分けて保管し、手順内で本人確認を行ってGitHub Issueを関連付けられます。

主な機能

  1. 01AI会話とWebページの取得
  2. 02生データと編集用ファイルの分離保存
  3. 03生ファイルのSHA-256記録
  4. 04GitHub Issueの作成と相互参照
  5. 05ソース種別に応じた取得方法の選択

活用シーン

  1. 01ChatGPT、Gemini、Claudeの会話のアーカイブ
  2. 02出典情報を保ったWeb調査資料の保存
  3. 03研究アーカイブとGitHub Issueの連携
  4. 04Issue作成前の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 フロントマター付きの Markdown ファイルとしてアーカイブして、相互参照する GitHub Issue を作成します。各ステップで本人確認が必須です。

自己進化するスキル:このスキルは使用を通じて改善されます。手順に誤りがある場合、パラメーターがずれている場合、または回避策が必要だった場合は、先延ばしにせず、すぐにこのファイルを修正してください。実際に再現できる問題がある場合にのみ更新してください。

最初に — 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 は新しいプロセスであり、対話型シェルの関数を継承しません。そのため、owner-per-path を使用するマシンでは、そのプロセスから呼び出す gh は、プロンプトから呼び出す gh とは異なります。2026-09-06 の測定では、対話型シェルの gh api user はリポジトリ自身のアカウントを返しましたが、heredoc では同じディレクトリにいるにもかかわらず、グローバルで有効な別のアカウントが返されました。これは正しいアーカイブを妨げる誤った不一致であり、実際の認証情報の問題とまったく同じように見えます。原因は、gh がシェル関数として、リポジトリごとの gh.configdir git 設定キーから設定した GH_CONFIG_DIR を使ってバイナリをラップしていることです。つまり、このキーが本人確認に使う ID を選択します。呼び出し元のシェルに依存せず、スクリプト内で解決することで、シェルに依存しないチェックになります。

/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 でした。これは正しい ID ですが、チェックでは不一致と判定されました。

このガードの目的は、誤ったアカウントへの書き込みを防ぐことです。「この ID でここに書き込めるか」は、所有者との文字列一致ではなく、permissions.push で判断します。個人リポジトリでは、オフラインでも使える高速な一致判定の経路を維持します。組織向けの経路では API 呼び出しが 1 回必要ですが、一致判定に失敗した場合にのみ実行されます。


スクレイピングのワークフロー

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 に 2 件の chatgpt.com/share/* リンクで測定したところ、Jina が取得したコンテンツは Firecrawl の**17%と12%**にとどまり、文の途中で切れていました。一方、Firecrawl はどちらも実際のページ末尾まで取得しました。また、Jina は -H "x-timeout: 30" も必要です。指定しない場合、ログイン画面の HTML が約 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 を使えば不要です。


ファイルの保存 — 成果物は2つ。1つだけにしないでください

アーカイブを作成すると、ファイルが 2つ 生成されます。このページで最も重要な点です。これを間違えると、アーカイブを検証しようとしたときまで気付かない、目に見えない損失が発生します。

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 — ケバブケースの要約(最大50文字)
  • source_type — 次の列挙値から選択:chatgpt、gemini、claude、web
🔴 トランスクリプトを .md に貼り付けないでください

これは誰もが思いつく方法ですが、間違いです。research-repo の PR #540 では、5回連続のレビューを経て、重複したトランスクリプトが削除されました。どのレビューでも、意味を変える編集の種類が見つかりましたが、同等性チェッカーは同一だと判定していました。具体的には、構造上のマーカーが消される(引用文が執筆者自身の主張として扱われる)、インラインの区切り文字が消される(`not` が英単語として読まれる)、識別子や URL のパスが _ や __ によって別の場所を指すようになる、表のセルの境界が移動して結果が誤った参照に紐付く、エスケープ内、フェンス付きコードブロック、インデント付きコードブロック、HTML の生の属性内でリテラルのアスタリスクが消費される、といった問題です。

最後の問題はバグではなく、カテゴリの誤りです。CommonMark 0.31.2 では、解析を2つのフェーズに分けています。「第1フェーズでは、入力行を読み込み、ドキュメントのブロック構造を構築します。テキストはこれらのブロックに割り当てられますが、解析はされません。……第2フェーズでは、段落と見出しの生テキストの内容が、Markdown のインライン要素の列として解析されます」。そのため、行単位のノーマライザーにはブロック処理のフェーズがなく、段落とフェンス付きコードブロックを見分けられません。どれだけパッチを追加しても、収束することはありません。

そもそも、なぜこの問題が起きるのでしょうか。Markdown の書式を整えるツールを使うリポジトリ(ここでは、prettier --write と markdownlint-cli2 --fix を実行する itp-hooks Stop フック)では、貼り付けたコピーをバイト単位で同一に保つことは不可能です。そのため、「コピーは元の内容と同じことを言っているか?」という問いに、コミットのたびに答えなければなりません。コピーが1つなら、その問いは生じません。

1つだけに制限する:マーカーとのパターンマッチではなく、重複を測定する

最初のゲートは、旧形式の `` コメントを探して再埋め込みを拒否していました。しかし、レビューで簡単に回避されました。通常の --- の後にトランスクリプト全体を追加し、マーカーを省略すると、ゲートは成功を返します。一方、test_re_embedding_the_transcript_is_refused というテストは、フィクスチャの作成にマーカーを_使用していた_ため、引き続き成功していました。バグが現れ得ない唯一の入力で検証された、構造上の保証を装うパターンマッチです。

再埋め込みは近似重複の検出の問題なので、その標準的な尺度である、Andrei Z. Broder の「On the resemblance and containment of documents」、SEQUENCES 1997、doi:10.1109/SEQUEN.1997.666900 による、w-シャingle上の_包含度_を使います。これは、_「A の B に対する包含度 c(A,B) は 0 から 1 の数値であり、1 に近いほど、A がほぼ B に含まれていることを示す」もので、「D に含まれるサイズ w のすべてのシャingleのバッグ(マルチセット)」_を対象とします。

実用上、次の4つの工夫が効果を発揮します。

  • 類似度ではなく、包含度を使う。 問いは方向性を持ちます。つまり、_トランスクリプト_のどれだけが再出現しているかです。一方、類似度は対称的なので、長い編集セクションがスコアを薄め、全文の貼り付けを見逃す原因になります。
  • しきい値は選ぶのではなく、調整する。 実際のアーカイブと、部分的な貼り付けの段階的な例を測定します。今回の結果は、正当な引用が0.0097、10分の1の貼り付けが0.1172、全文が1.0000でした。そのため、0.10の上限は引用の約10倍で、意味のある貼り付けはすべて下回る値です。数値が当てずっぽうに見えないよう、この段階的な結果を定数の隣に公開します。
  • 小文字化した英数字にトークン化します。こうすれば、バイト単位の比較を不可能にした書式変更があっても、尺度は機能します。prettierの実行後も、再埋め込みされたトランスクリプトを検出できなければなりません。
  • マルチセットではなく、集合を使います。 Broder の定義はバッグですが、集合を使えば「再現された_異なる_内容の量」に答えられます。1つの断片を20回貼り付けても、1回として数えます。この場合に適切なのは、この意味づけです。

不変条件の対象は1つのファイルではなく、リポジトリ全体にします。 2つ目の回避方法は、単にコピーを別の場所に移すことでした。トランスクリプトを_固定する_アーカイブだけを走査すると成功を報告する一方で、隣の.mdファイルにはその100%が含まれていました。すべてのMarkdownファイルを走査し、さらにそれらを連結した内容も測定します。上限未満の断片が12個あっても、連結すればトランスクリプト全体を再構成できるからです。

限界は隠さず、2つ明示します。引用された1行だけなら、検査を通過できなければなりません。アーカイブ自体に約1%の正当な引用があるため、貼り付けられた1行を拒否するしきい値では、引用を完全に禁止することになります。また、大幅に言い換えられた再埋め込みは、この尺度では検出できません。この段階では文書は実質的に異なっており、意味が維持されたかどうかを自動検査で判定することはできません。これは正規化器を断念させたのと同じ判定不能性であり、唯一の対策はレビュアーの判断です。

.txt拡張子は重要です

生のスクレイプデータは.mdではなく.txtとして保存します。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 が該当するタイトル/著者/掲載先を示していること)と、_内容_の確認(情報源に要約の主張どおりの記述があること)を区別してください。この2つは別物であり、混同するのがよくある誤りです。
  3. 情報源に既知の誤りがある場合は、PR だけでなく、読者の目に触れる場所に記載します。
  4. .txt へのリンクと、そのサイズおよび sha256。

引用は、HTTP ステータスではなく、Crossref API を使って内容を確認してください。403 のボット対策ページと 404 は、ステータス行だけでは区別できません。Crossref に要旨がない場合はその旨を記載し、確認済みであるかのように示さず、主張を未検証としてマークしてください。

逐語引用はすべて、取得した文書とプログラムで照合し、照合結果を信頼する前に抽出内容を確認してください。 PDF に対する textutil -convert txt -stdout は、ファイルのバイト数とまったく同じ長さを返しました。生のバイト列がそのまま渡され、成功と報告されたため、これを使えばどんな引用でもバイナリノイズと照合されて「一致」したことになります。抽出した長さをページ数と照らして妥当性を確認してから、抽出時に単語間の空白が崩れるため、空白を無視し、合字を展開したうえで比較してください。

照合に失敗した場合は、編集に取りかかる前に、どのような失敗なのかを特定してください。こちらでは2回失敗しました。1回は、抽出ツールが文の途中にページ番号を挿入していました(引用自体は正確でした)。もう1回は、引用符の内側で、原文の " が ' に置き換えられていました。どれほど小さな違いに見えても、後者は引用文の改変です。抽出できないグリフを音声で読み上げた名前に置き換えるのも、同じ問題です。


GitHub Issue の作成

ラベルの調査

まず既存のラベルを調べ、再利用を優先してください。新しいラベルは、その概念が本当に新しい場合にのみ作成します。

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

ポリシー:Issue ごとのラベルは最大 3〜6 個です。一般的なラベル:research、ai-output、chatgpt、gemini、archival。

Issue の作成

インラインで内容を作成する場合は --body とヒアドキュメントを、非常に長い内容の場合は --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 の作成後、アーカイブされたファイルの frontmatter に Issue の URL と番号を追加します。


正式なバックリンクコメント

アーカイブされたファイルへのリンクを含むコメントを 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 を4行重ねても、ハードラップされた文章と見分けがつきません。そのため GH-HARD-WRAP-GUARD PreToolUse フックがコマンドを拒否します(2026-08-20 に計測。このファイルの以前のテンプレート自体がフックを作動させていました)。リストにすれば、項目が個別であることを構造的に表せます。意図にも合致し、ガードにも受け入れられます。ここで GH-HARD-WRAP-OK による上書きを使うのは、正しい指摘を無効にすることになります。

すべての Issue/PR の本文は、改行のない段落として作成する必要があります

これはバックリンクだけでなく、Issue の本文と PR の本文にも適用されます。各段落を1本の長い行として作成し、GitHub に折り返し表示させてください。改行を入れるのは、リスト項目、見出し、コードブロック、空行だけにします。ソフトラップするエディターではこれが見えないため、次のコマンドで確認してください。

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

文章段落が長い1行になるのは想定どおりで正しいことです。問題となるのは、約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

トラブルシューティング

IssueCauseFix
GH-HARD-WRAP-GUARD がコマンドを拒否する本文がハードラップされた文章として作成されている各段落を 1 行に整形し、複数の Key: value メタデータにはリストを使用します。GH-HARD-WRAP-OK に頼ってはいけません。このガードは正しく機能しています
組織のリポジトリで ID の事前チェックがブロックするOwner が組織であるため、AUTH_USER != REPO_OWNER が常に成立します想定どおりです。チェックは owner.type == Organization と permissions.push == true に進むようになりました。それでもブロックされる場合は、実際に push 権限がありません
事前チェックで、使用していないアカウントを示す MISMATCH が報告されるが、プロンプトからは gh が正常に動作するheredoc はシェル関数を持たない新しいプロセスであるため、素の gh バイナリを呼び出し、リポジトリごとの gh.configdir が適用されませんheredoc 内で cd し、最初に gh を呼び出す前に git config --get gh.configdir から GH_CONFIG_DIR をエクスポートします。heredoc の内外で gh api user --jq .login を実行して確認します。結果が異なる場合、原因はこの不具合であり、認証情報の誤りではありません
push が成功した後に gh pr create が「最初に現在のブランチを push する必要があります」と表示するリンクされたワークツリーから実行しているため、gh が upstream を解決できません--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 が Markdown を返さない一時的な API エラー1 回再試行し、それでも失敗する場合は -H "x-timeout: 30" を付けて Jina に切り替えます
スクレイピングでページのシェルだけが返されるSPA のレンダリングがまだ完了していませんリクエスト本文の waitFor(8000 → 15000)と timeout を大きくします
Jina が約 321 バイトを返すタイムアウトヘッダーがありません-H "x-timeout: 30" を追加します。これがないと、Jina はログイン画面の表示を返します
Jina の出力が途中で切れるJina は JavaScript を多用するページの取得範囲が不十分です想定どおりです。Firecrawl を使用してください。2026-08-13 のテストでは、Jina の取得率は 17%/12% でした
ID ガードがブロックするOwner 以外のアカウントexport GH_TOKEN=$(~/.claude/tools/bin/gh-token-for-repo)

参考資料

実行後の振り返り

このスキルの実行が完了したら、終了する前に以下を確認してください。

  1. コマンドは成功しましたか? — 成功しなかった場合は、失敗の原因となった指示またはエラーテーブルを修正してください。
  2. パラメーターまたは出力に変更はありましたか? — 基盤となるツールのインターフェースに変更があった場合は、使用例とパラメーターテーブルを更新して内容を一致させてください。
  3. 回避策が必要でしたか? — 工夫が必要だった場合(異なるフラグや追加の手順など)は、次回の実行で同じ回避策が不要になるよう、この SKILL.md を更新してください。

問題が実際に発生し、再現可能な場合にのみ更新してください。推測に基づいて更新しないでください。

コメント