mit einem Klick
mit einem Klick
公開済みの blog post から NotebookLM Audio Overview (日本語ポッドキャスト) を生成し、記事フロントマターに紐付ける
既存 blog post に対してファクトチェックを単独実行する。skill 自身が claim を抽出 → claim-atom-verifier (Haiku) を claim 単位で並列 fan-out する swarm を回す
wiki section または個別ページに対して claim 単位の swarm ファクトチェックを実行する。skill 親セッションが claim を抽出し claim-atom-verifier (Haiku) を並列起動する
Wiki ページの `lastmod` を section 別の閾値で評価し、touch されずに古びた候補を浮かせる。refresh サブコマンドで wiki-fact-checker agent を起動して claim を再検証する
ブログ記事を読み込んで Wiki ページを自動生成・更新する
Wiki の健全性チェック(矛盾検出、孤立ページ、欠落リンク、古い記述)
| name | blog |
| description | Hugo ブログ記事を新規作成し、PR を作成する |
| arguments | [{"name":"topic","description":"記事のトピック、タイトル、または GitHub Issue URL","required":true},{"name":"date","description":"記事の日付(YYYY-MM-DD 形式)。省略時は本日","required":false}] |
指定されたトピックで Hugo ブログ記事を作成し、PR を作成してください。
短い tip ・コードスニペット中心の軽量記事で、図解や PR レビューが不要な場合は
/gist-writerを使う方が早い(gist に書いてimport-gists.shで取り込まれるフロー)。GitHub Issue 由来 / long-form / 図解入りはこの/blogを使う。
重要: トピックとして GitHub URL が指定された場合、https://github.com/Eotel/blogs/ 配下の URL のみ受け付ける。
https://github.com/Eotel/blogs/issues/..., https://github.com/Eotel/blogs/pull/... などトピック引数が以下のいずれかを判定する:
https://github.com/Eotel/blogs/issues/{number} 形式旧
https://github.com/Eotel/blogs/issues/{number}#issuecomment-{id}形式(コメント単位 URL)は サポート対象外。1 topic = 1 Issue 運用に統一されている。フォーク元 hdknr の歴史的記事は既にcontent/posts/に取り込み済みで、これ以降コメント URL を入力にすることはない。
Issue を 記事の brief(依頼書) として扱う。本文の文字数は問わない(タイトル + URL + 0〜1 行で十分)。
owner, repo, issue_number を抽出するgh api /repos/{owner}/{repo}/issues/{issue_number} --jq '{title, body, created_at, html_url}'
aegis_fetch で取得するaegis_fetch で調査して 執筆するsource_url として Issue の html_url を記録するaegis_fetch で最新情報を調査して記事を作成するすべての記事のフロントマターに author フィールドを必ず含めること。値は scripts/authors.json の id を使用する。
決定ロジック(優先順位順):
author id "<id>" を明示している場合、その値を使用するgithub_login を scripts/authors.json で id に解決する:
AUTHOR_ID=$(jq -r --arg l "$LOGIN" '.authors[] | select(.github_login == $l) | .id' scripts/authors.json)
解決できない投稿者は allowlist 外なので、エラーで中断するeotel を使用するcreated_at を使用するdate +%Y-%m-%d)karpathy LLM Wiki パターンの "Wiki as input" を実現する段。記事執筆前に既存 Wiki を読み込み、用語ブレ・重複・再説明を抑える。この手順は省略しない。
Wiki の関連ページを検索
トピックのキーワード(2〜5 個)を抽出し、content/wiki/{concepts,tools,guides} を grep する:
PROJECT_DIR=$(git rev-parse --show-toplevel)
grep -rli "<keyword>" \
"$PROJECT_DIR/content/wiki/concepts" \
"$PROJECT_DIR/content/wiki/tools" \
"$PROJECT_DIR/content/wiki/guides"
ヒットしたページを Read
Wiki ページ本文と frontmatter(特に aliases, related_posts, tags)を読み込み、以下を draft に反映する:
aliases(同義語) を確認し、検索性を担保した記述にする/blogs/wiki/<section>/<slug>/ でリンクして委ねる重複トピック早期検出
Wiki ヒットがなくても、content/posts/ 直近 1 年で同じトピックが既に書かれていないかを確認する:
ls -t "$PROJECT_DIR/content/posts/$(date +%Y)" 2>/dev/null
grep -rli "<keyword>" "$PROJECT_DIR/content/posts/$(date +%Y)" 2>/dev/null
明らかな重複があればユーザーに確認する。
Wiki が薄い・無い場合
ヒットしなくても作業は止めない。「これは新しい概念で Wiki に未収録」と認識して書く(後で /wiki-ingest がこの記事から拾う)。
記事が coding agent による自動執筆の場合、使用したモデルIDをフロントマターに記録する。
auto モードの shell ルール($() 置換禁止)に従い、結果は一時ファイル経由で受け渡す:
mkdir -p "$PROJECT_DIR/.claude/temp"
python3 "$PROJECT_DIR/scripts/get_model.py" > "$PROJECT_DIR/.claude/temp/model_id.txt"
書き出した .claude/temp/model_id.txt を Read ツールで読み、フロントマターの model: に値を入れる。
model を省略してもよいcontent/posts/YYYY/MM/YYYY-MM-DD-<slug>.md<slug> はトピックから生成する(英数字・ハイフンのみ、小文字)-2)scripts/categorize.py のルールに基づいて、記事の内容からカテゴリとタグを判定する重要:
slug:フィールドは必須。.claude/skills/wiki-lint/scripts/wiki_lint.pyのposts_missing_slugチェックで fatal 扱いされており、未定義のままだと CI で lint が落ちる。Hugo は title 由来 slug にフォールバックし URL が不安定になるため、slug:をファイル名と一致させて明示する(scripts/check_frontmatter.pyのPOST_REQUIREDはtitle/date/authorのみで slug は見ない)。
GitHub URL ソースの場合:
---
title: "記事タイトル"
slug: "<slug>" # ファイル名の <slug> 部分と一致させる。必須
date: YYYY-MM-DD
lastmod: YYYY-MM-DD
draft: false
author: "<author id>" # scripts/authors.json の id。GitHub URL ソースの場合は投稿者の github_login から解決
model: "<model id>" # coding agent で執筆した場合は自動設定。手動執筆の場合は省略可
source_url: "https://github.com/..."
categories: ["カテゴリ"]
tags: ["tag1", "tag2"]
---
テキストトピックの場合:
---
title: "記事タイトル"
slug: "<slug>" # ファイル名の <slug> 部分と一致させる。必須
date: YYYY-MM-DD
lastmod: YYYY-MM-DD
draft: false
author: "eotel" # 手動実行時のデフォルト
model: "<model id>" # coding agent で執筆した場合は自動設定。手動執筆の場合は省略可
categories: ["カテゴリ"]
tags: ["tag1", "tag2"]
---
記事作成・ファクトチェックを問わず、外部 URL のコンテンツを取得する際は以下の優先順位に従う:
aegis_fetch を優先使用する
aegis_fetch が利用できない場合は WebFetch にフォールバック
aegis_fetch の大きな結果の扱い
~/.claude/projects/.../tool-results/、Codex CLI も同様にホーム配下に退避)が自動的に外部ファイルへ保存するcp や Grep でアクセスすると同意確認が発生する.claude/temp/ にコピーしてから Read/Grep する
cp ~/.claude/projects/.../tool-results/mcp-aegis-aegis_fetch-XXXX.txt .claude/temp/aegis-result.txt
.claude/temp/ 内のコピーを削除するtool-results/ へのアクセスは自動許可されるSPA(JavaScript 描画)サイトの場合
aegis_fetch / WebFetch どちらでも生 HTML からコンテンツを取得できない場合があるapi.fxtwitter.com に変換して JSON API 経由で取得するWebSearch で該当ページの情報を検索する見出し(##)を使って構造化する
コード例がある場合はシンタックスハイライト付きのコードブロックを使用する
日本語で記述する
冒頭に概要・導入を書く
実用的な情報を含める(コマンド例、設定例、コードサンプルなど)
GitHub Issue からの内容はそのまま活かしつつ、ブログ記事として読みやすく整形する
言説 claim には原文逐語を blockquote で埋め込む (重要): 「X は Y と言っている」「書籍 Z でこう書かれている」と書くときは、要約だけで済ませず、原文の英語または日本語の逐語を blockquote (>) で本文に並置する。
.claude/agents/claim-source-verifier.md の「5 failure pattern」を参照記事末尾に「関連 Wiki」セクションを設ける(手順 5.5 で見つかった既存 Wiki ページを集約。空なら無くて良い)
## 関連 Wiki
- [LLM Wiki パターン](/blogs/wiki/concepts/llm-wiki-pattern/)
- [aegis-fetch](/blogs/wiki/tools/aegis-fetch/)
アーキテクチャ図やフロー図が必要な場合、アスキーアートは使わない。SVG を手書きで作るのもアンチパターン。経路は図の性質で選ぶ:
| 図の性質 | 推奨経路 |
|---|---|
| コンセプト図・ヒーロー画像・イメージ図・単発の説明図(後から編集しない) | Codex CLI に gpt-image で直接 PNG 生成 を最優先 |
| アーキテクチャ図・フロー図・シーケンス図・ER 図など、ソースを残して後から編集したい構造図 | drawio で mxgraph XML を書き、PNG にエクスポート |
Codex CLI が呼び出せる環境では、drawio XML を経由せず直接 PNG バイナリを得るのが最速。codex exec を Bash で直接呼ぶこと — codex:rescue skill / codex-companion.mjs task 経路は OPENAI_API_KEY を要求するため使わない(codex 本体は ChatGPT subscription auth で動く)。
codex exec -s workspace-write --skip-git-repo-check "次の図を image_generation ツールで生成して、絶対パス /path/to/assets/images/<slug>-<diagram>.png に PNG で保存してください。サイズは 1024x1024。
図の内容: '<図の自然文記述>'
完了したら、保存パスと ls -la でファイルサイズを 1 行で報告してください。"
~/.codex/generated_images/<session>/ig_*.png に一旦保存してから指定パスへコピーする (内部で自動的にやる)sips 呼び出しでリサイズされるassets/images/(static/ 配下は Hugo image processing の対象外で WebP srcset 展開が効かない)codex-cli 0.130.0、ChatGPT auth、image_generation feature stable=trueソースを残して後から編集したい構造図向け。
assets/images/<slug>-<diagram-name>.drawio
assets/images/harness-eval-cycle.drawio、assets/images/design-md-three-layers.drawio)を雛形にすると効率が良い--scale 2 で高解像度):
.claude/skills/drawio/ の skill が /Applications/draw.io.app/... を自動検出してエクスポートする./scripts/drawio-export.sh (Docker) を使う
./scripts/drawio-export.sh assets/images/<name>.drawio -f png --scale 2 -o assets/images/
rlespinasse/drawio-desktop-headless を起動する。Docker Desktop / orbstack / colima のいずれかが稼働していれば良いassets/images/openclaw-gateway-architecture.drawio 等)のスタイルを参考にする
../../images/)は使わない: Hugo のパーマリンク構造で 404 になるため、必ず /blogs/images/ の絶対パスを使うassets/images/ (static/ ではない)。static/ 配下は Hugo image processing の対象外で WebP srcset 展開が効かない記事ファイルを Write した直後、コミット前に 6 つの subagent を 1 つのメッセージで並列起動 する。 このステップは省略してはならない。 並列化により context 消費を抑え、観点ごとの verdict が独立して検証可能になる。
| agent | 担当観点 | 出力 |
|---|---|---|
claim-source-verifier | ツール存在・機能仕様・「公式」帰属など事実主張の裏付け | JSON verdict |
url-liveness-checker | 記事内 URL の HTTP ステータス / 404 検出 | JSON verdict |
command-syntax-verifier | CLI コマンド・API 呼び出しの構文・フラグ | JSON verdict |
version-date-checker | バージョン番号・リリース日・互換性記述 | JSON verdict |
tech-writer | 構成・読みやすさ・日本語品質 | 自然文レビュー |
seo-advisor | タイトル / カテゴリ / タグ / 内部リンク | 自然文レビュー |
$ARTICLE_PATH を $WORKTREE_DIR/content/posts/YYYY/MM/YYYY-MM-DD-<slug>.md に置き換えて、以下を 必ず 1 つのメッセージで並列起動 する:
Agent(subagent_type="claim-source-verifier", prompt="記事絶対パス: $ARTICLE_PATH")
Agent(subagent_type="url-liveness-checker", prompt="記事絶対パス: $ARTICLE_PATH")
Agent(subagent_type="command-syntax-verifier", prompt="記事絶対パス: $ARTICLE_PATH")
Agent(subagent_type="version-date-checker", prompt="記事絶対パス: $ARTICLE_PATH")
Agent(subagent_type="tech-writer", prompt="記事絶対パス: $ARTICLE_PATH")
Agent(subagent_type="seo-advisor", prompt="記事絶対パス: $ARTICLE_PATH")
$WORKTREE_DIR 内のファイル{
"agent": "claim-source-verifier",
"article": "content/posts/YYYY/MM/YYYY-MM-DD-slug.md",
"checked_at": "ISO8601 UTC",
"claims": [
{
"line": 42,
"claim_type": "factual | discourse | verbatim-quote",
"claim": "...",
"verdict": "verified | needs_fix | incorrect | uncertain",
"failure_pattern": "misattribution-author-confusion | citation-mismatch-empty-footnote | scare-quote-without-verbatim | conceptual-conflation | paraphrase-over-extension | null",
"evidence": "URL + 逐語引用 + byline 確認結果",
"suggestion": "needs_fix / incorrect のとき必須"
}
],
"summary": {
"verified": 0, "needs_fix": 0, "incorrect": 0, "uncertain": 0,
"by_pattern": {
"misattribution-author-confusion": 0,
"citation-mismatch-empty-footnote": 0,
"scare-quote-without-verbatim": 0,
"conceptual-conflation": 0,
"paraphrase-over-extension": 0
}
}
}
claim_type の意味:
factual — ツール存在・機能仕様・組織所属など客観的事実discourse — 「誰が何を言ったか / どこに書いたか」の言説主張verbatim-quote — 鉤括弧で囲まれた逐語引用主張言説主張 (discourse / verbatim-quote) は事実主張より厳しく判定される。詳細は .claude/agents/claim-source-verifier.md の「5 failure pattern」を参照。
verdict ごとに件数をユーザーへ簡潔に報告するby_pattern 集計も別途報告する。misattribution-author-confusion や citation-mismatch-empty-footnote が 1 件でもあれば、それは構造的に最重要なので冒頭で警告するEdit で記事を即座に修正(suggestion を反映)。言説 claim の場合は 原文逐語を blockquote で本文に追加する ことを優先Edit で修正。ソース無しの独自主張は削除を優先tech-writer / seo-advisor のレビュー結果は以下の基準でフィルタリング:
git diff で変更を確認してからコミットへ進むfact-checker agent を直接呼べる(4 観点を 1 agent で走査する用途).claude/agents/<agent-name>.md を参照記事作成後、以下の手順で PR を作成する。
重要: git worktree を使い、メインの作業ディレクトリのブランチを汚さないようにする。
重要: コマンドを && で繋がないこと。 && で繋いだ複合コマンドは許可パターンにマッチせず、毎回確認が求められる。各コマンドは個別の Bash 呼び出しとして実行する。
ブランチ名を決定する: blog/YYYY-MM-DD-<slug>
worktree を作成する:
# メインリポジトリのルートで実行(main ブランチのまま)
BRANCH_NAME="blog/YYYY-MM-DD-<slug>"
git worktree add -b "$BRANCH_NAME" ".worktrees/<slug>" main
worktree の絶対パスを取得する(重要):
git worktree list
出力から worktree の絶対パスを読み取り、以降はその絶対パスを $WORKTREE_DIR として使う。
相対パスから絶対パスを推測してはならない。 Write ツールは存在しないパスにもファイルを作成するため、パスを間違えてもエラーにならず手戻りが発生する。
worktree から lefthook を install しておく(hook ファイルが欠落 / 古い場合の保険):
( cd "$WORKTREE_DIR" && lefthook install --force )
このリポジトリは core.hooksPath を 絶対パス ($(git rev-parse --path-format=absolute --git-common-dir)/hooks) に固定しているため、main で install したフックは worktree からも同じ実体を fire する。したがって本ステップは「環境が壊れていないことを確認する保険」であり、SessionStart hook が走っていれば実質 no-op。--force を付けているのは、lefthook 2.x が「core.hooksPath は自分が管理したい」挙動を持ち、固定済みパスを検出すると素の lefthook install を拒否するため。( cd ... ) で subshell に閉じてあるのは top-level cwd を汚さないため。失敗しても致命ではない(後段の wiki-lint で二重ガードしている)。
過去の罠: 以前は
core.hooksPath = .git/hooks(相対パス)だったため worktree では.gitがファイルとなりgit rev-parse --git-path hooksがNot a directoryで死に、lefthook installが必ず失敗していた。絶対パス化でこれを解消している。
worktree 内で記事ファイルを作成する:
$WORKTREE_DIR/content/posts/YYYY/MM/YYYY-MM-DD-<slug>.mdworktree 内で Hugo ビルド確認(cd を使わず --source で指定):
hugo --source "$WORKTREE_DIR" --gc 2>&1 | tail -5
worktree 内で wiki-lint を実行(CI と同じ fatal チェックをローカルで先取り):
python3 "$WORKTREE_DIR/.claude/skills/wiki-lint/scripts/wiki_lint.py"
wiki_lint.py はスクリプト自身の絶対パスからリポジトリルートを自動検出するため、worktree 内のスクリプトを直接呼べば worktree が root として評価される。exit code 0 を確認する。slug: 欠落や frontmatter 不備などはここで止める。
なぜここで明示的に走らせるのか: lefthook.yml の pre-push にも wiki-lint は登録されているが、core.hooksPath の設定不備 / lefthook の install 失敗 / fork 直後のクローン等で hook が fire しないことがある。SessionStart hook と手順 4 の lefthook install --force で二重に install を試みているが、それでも fire しなかった場合の最後の砦として skill 内から直接 wiki_lint.py を呼ぶ三重ガード構成にしている。
worktree 内でコミット・プッシュ(cd を使わず git -C で指定):
git -C "$WORKTREE_DIR" add content/posts/YYYY/MM/YYYY-MM-DD-<slug>.md
git -C "$WORKTREE_DIR" commit -m "Add blog post: <記事タイトル>"
git -C "$WORKTREE_DIR" push -u origin "$BRANCH_NAME"
PR を作成する(--head でブランチを明示指定し、cd を使わない):
PR 本文は worktree 内に書き出し、--body-file で渡す。worktree は .claude/ の外にあるため、Write ツールで直接書き込める。
PR 本文には必ず Closes #<issue_number> を含める(Issue URL がソースの場合)。マージ時に GitHub が自動で Issue を close する。
## Summary
<記事の要約 1〜3 文>
Closes #<issue_number>
## Test plan
- [x] hugo --gc でビルド成功
- [x] ファクトチェック完了
- [x] tech-writer / seo-advisor レビュー反映
# Write ツールで $WORKTREE_DIR/pr_body.md に PR 本文を書き出す
gh pr create --repo Eotel/blogs --head "$BRANCH_NAME" --title "Add blog: <記事タイトル>" --body-file "$WORKTREE_DIR/pr_body.md"
注意: cd "$WORKTREE_DIR" && gh pr create は使わないこと。 cd で始まるコマンドは Bash(gh:*) の許可パターンにマッチせず、毎回確認が求められる。--head フラグでブランチを指定すれば worktree 内にいる必要はない。
注意: --body "$(cat <<'EOF'...)" 方式は使わないこと。 HEREDOC 内の # 付き行がセキュリティチェック("quoted newline followed by #-prefixed line")に引っかかり、毎回確認が求められる。
PR の URL をユーザーに伝える
PR がマージされたら worktree を削除する。 ユーザーがマージを指示・確認した直後に git worktree remove --force "$WORKTREE_DIR" を実行する(pr_body.md 等の未追跡ファイルが残るため --force が必要)。
Issue URL がソースの場合、PR 本文の Closes #<issue_number> により PR マージ時に GitHub が自動で Issue を close する。手動コメント追記やリアクション付与は不要。
ブログ化を見送る場合(重複トピック等)は、Issue を手動で close + 理由をコメントで残す:
gh issue close <issue_number> --comment "重複のため見送り: #<別Issue>"
pr_body.md 等の未追跡ファイルが残るため --force が必要):
git worktree remove --force "$WORKTREE_DIR"
Wiki 取り込みは GitHub Actions(
.github/workflows/auto-wiki-ingest.yml)が週次でまとめて実行する。/blog 単体では Wiki 更新を行わない。手動で即時反映したい場合は/wiki-ingest <記事パス>を実行する。