Imported from aidotters/claude-code-wiki-maker (
.claude/skills/llm-wiki/SKILL.md). Install upstream withnpx skills add aidotters/claude-code-wiki-maker --skill llm-wiki. Copyright stays with the author.
llm-wiki(個人 Claude Code 知識ハブ)
目的: 進化の速い Claude Code の知見を「検索ではなくコンパイル」して永続知識ベースに蓄積し、 引用付きの派生成果物(チートシート/Tips 集)を生成・維持する。
引数: $ARGUMENTS
使用方法:
/llm-wiki init # ボールト初期化・wiki-vault リンク案内・設定整備
/llm-wiki ingest <path-or-url> # ソースを取り込みコンパイル(既存)
/llm-wiki ingest <path-or-url> --type=practice # practice ページとして取り込み(raw/notes/ 想定)
/llm-wiki ingest <path-or-url> --feature=<slug> # source 生成 + 指定 feature ページ更新
/llm-wiki ingest <url> --watch # Tier B URL を watchlist 登録(source ページに watch:true)して取り込み
/llm-wiki ingest <url> --feed=<rss_url> # Tier B フィード登録(source ページに feed_url を立て discover-watchlist の巡回対象に)
/llm-wiki ingest --feed=notion-medium-db:default # document-less ソース登録(Phase 4・feed_registry に append・raw/source ページ生成なし・path-or-url 省略)
/llm-wiki ingest --feed=claude-blog-sitemap:default # document-less ソース登録(Phase 4c・公式ブログ claude.com/blog を sitemap 経由で discover-watchlist の巡回対象に・path-or-url 省略)
/llm-wiki query <質問> # Wiki から引用付きで回答(読み取り専用)
/llm-wiki synthesize <テーマ> # チートシート/Tips 集等を生成/再生成
/llm-wiki lint [--check=<csv>] # 17 検査(Phase 2a 7 + Phase 2b 4 + Phase 3a 1 + Phase 3c 1 + Phase 3f 2 + Phase 3g 1 + Phase 5 1、#11 のみ承認制で書き込み)
/llm-wiki refresh-tier-a [--dry-run] # Tier A 日次自動再取得(cron / launchd 経由・対話実行も可)
/llm-wiki discover-tier-a [--no-prompt|--dry-run] # Tier A 未取り込み URL 自動発見 + 承認制 ingest(--no-prompt で cron 用)
/llm-wiki refresh-watchlist [--dry-run] # Tier B watchlist(watch:true)の日次自動再取得(cron / launchd 経由・mode F の Tier B 版)
/llm-wiki review [--dry-run] # 会話 hook が貯めた URL を triage(承認制 ingest・対話専用)
/llm-wiki discover-watchlist [--no-prompt|--dry-run] # Tier B フィード登録済みサイトの新着 URL 自動発見 + stage-1/stage-2 フィルタ + 承認制 ingest(--no-prompt で cron 用)
不変条件(CLAUDE.md「設計上の不変条件」が最上位の正)
このスキルは CLAUDE.md の不変条件に従う。齟齬時は CLAUDE.md > SKILL.md / schema.md。
データ規約(ページタイプ・フロントマター必須フィールド・命名/[[wikilink]]/tier 判定・schema_version・
co-evolution・責務境界)の唯一の正は references/schema.md であり、本ファイルでは再記述しない。
ページ本文の雛形は references/page-templates.md。
要点(詳細は上記参照):
- raw は不変スナップショット。すべての主張は raw を引用する。
- 既存ページと矛盾する主張は黙って上書きせず「矛盾」セクションに両論併記。
- 操作後は index.md / log.md を更新し、ボールト側 Git に操作単位でコミットする。
- ボールトパスはハードコードせず、本リポジトリ直下の設定ファイル
.llm-wiki.jsonから解決する。
ステップ0: 引数パースとモード分岐
$ARGUMENTSの第 1 トークンをモードとして取り出す。残りを引数とする。- 分岐:
init→ モード Aingest→ モード B(第 2 引数 = path-or-url。無ければ使用法表示で停止。例外(Phase 4・§5.2b):--feed=notion-*:スキームが指定され path-or-url が省略された場合は停止せず mode B の registry append 特例に進む=document-less ソース登録のため path-or-url 不要)query→ モード C(残り全体 = 質問。無ければ使用法表示で停止)synthesize→ モード D(残り全体 = テーマ。無ければ使用法表示で停止)lint→ モード L(Phase 2a 7 + Phase 2b 4 + Phase 3a 1 + Phase 3c 1 + Phase 3f 2 + Phase 3g 1 + Phase 5 1 の 17 検査)refresh-tier-a→ モード F(Phase 3a・Tier A 日次自動再取得。--dry-run任意)discover-tier-a→ モード G(Phase 3c・Tier A 未取り込み URL 自動発見 + 承認制 ingest。--no-prompt/--dry-run任意)refresh-watchlist→ モード W(Phase 3f・Tier B watchlist〔watch: true〕の日次自動再取得。mode F の Tier B 版。--dry-run任意)review→ モード H(Phase 3e・会話 hook が貯めた URL を triage して承認制 ingest。対話専用。--dry-run任意)discover-watchlist→ モード I(Phase 3g・Tier B 登録済みフィード(feed_url保持 source ページ)の新着 URL 自動発見 + stage-1 keyword フィルタ + stage-2 LLM 判定 + capped バッチ opt-out 承認制 ingest。--no-prompt/--dry-run任意)- 上記以外 / 引数なし → 上の「使用方法」を表示して停止。
init以外のモードは、最初に ボールト前提チェック(ステップ0.5)を行う。
ステップ0.5: ボールト前提チェック(init 以外)
-
本リポジトリ直下の設定ファイル
.llm-wiki.jsonを Read。無ければ 「先に/llm-wiki initを実行してください」と案内して停止。 -
設定の
vault_relative(./wiki-vault)の存在とリンク有効性を Bash で確認 (test -e ./wiki-vault)。不在/リンク切れなら同様に init を案内して停止。 -
ボールト側に未コミット変更があれば(
git -C ./wiki-vault status --porcelain)、 状態を提示し続行可否を AskUserQuestion で確認する。- モード F(refresh-tier-a)は対話モードではないため、§F-2 で自動 skip に分岐する(AskUserQuestion を起動しない)。
-
minitools_path 解決(Phase 4・Medium 機能の availability gate):
.llm-wiki.jsonのminitools_pathを読む。- キー不在 or ディレクトリ不在(
test -d <minitools_path>が false)ならminitools_available = falseをフラグし、Medium 関連機能(mode B step 3 の medium 分岐=4a・mode I I-3 のfeed_registryNotion 巡回=4b)のみ無効化する。他モード・他経路は通常動作(clean failure・gh 不在と同型)。本チェック自体は停止理由にしない(Medium を使わない利用者はminitools_path未設定のまま全機能を使える)。 - キーがありディレクトリも存在すれば
minitools_available = true・解決済みパスを各モードに伝搬する。 - 各モードでの「無効化」の具体挙動は per-fetcher availability check(§モード B 4a・§モード I I-3)に従う(
uv不在 /minitools_path未設定 / CLI exit 1 を当該経路でのみ clean failure とし、ingest / discover 全体は壊さない)。
- キー不在 or ディレクトリ不在(
ステップ0.6: lock 取得(書き込みモード共通)
.llm-wiki.lock は 書き込みモード(B / D / F / G / W / H / I、および lint #11 承認制決着の ## 矛盾 編集部分)でのみ取得する。query(モード C)は読み取り専用のため不要。lint 通常実行(17 検査レポート+ wiki/log.md 追記まで)も不要(design.md §6 carve-out)。
- ファイル: vault 直下
.llm-wiki.lock(/llm-wiki initで vault.gitignoreに追加済)。 - フォーマット: 1 行 JSON
{"pid": <PID>, "started_at": "<ISO8601>", "mode": "ingest|synthesize|refresh-tier-a|discover-tier-a|refresh-watchlist|review|discover-watchlist|lint-resolve"} - atomic 取得:
Bash('set -C; echo "{\"pid\":'$$',\"started_at\":\"'$(date -Iseconds)'\",\"mode\":\"<mode>\"}" > ./wiki-vault/.llm-wiki.lock')でset -C(noclobber)により既存ファイルへの書き込みは exit non-zero で失敗。失敗時は スタール判定 へ進む。 - スタール判定: 既存
.llm-wiki.lockを Read し、started_atから 既定 1 時間経過 ANDBash('kill -0 <pid> 2>/dev/null')が non-zero(プロセス死亡)の 両方 が真のときのみ強制奪取(誤奪取防止)。一方だけ真なら他プロセス稼働中扱いとして該当モードの skip 手順に進む。 - 解放: モード完了時に
Bash('rm -f ./wiki-vault/.llm-wiki.lock')。例外時もtrap相当で削除を試みる。
各モードでの lock 取得タイミングは各モード本文の冒頭で指定する。
モード A: /llm-wiki init
ボールトを初期化し、本リポジトリ側の設定を整える。
-
設定ファイル確認
.llm-wiki.jsonが既存なら内容を提示し、再初期化するか AskUserQuestion で確認。
-
ボールト実体パスの確定
./wiki-vaultが既に有効なシンボリックリンクならその実体を採用。- 無ければ実体パス(既定候補
~/Documents/claude-code-wiki)を AskUserQuestion で確認し、ln -s <実体パス> ./wiki-vaultを案内し、承認を得て実行する。
-
ボールト骨格生成
references/schema.md§3 のディレクトリ規約に従い、raw/とwiki/のサブディレクトリ、wiki/index.mdwiki/log.mdwiki/overview.mdwiki/current-baseline.mdを雛形生成する (既存ファイルは上書きしない)。- 各雛形フロントマター/構成は
references/schema.md・references/page-templates.mdに従う。
-
current-baseline.md の初期
claude_code_version確定(決定 イ)- WebFetch 対象 URL(確定):
https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md- 取得不可時フォールバック URL:
https://github.com/anthropics/claude-code/releases
- 取得不可時フォールバック URL:
- 取得テキストを
raw/docs/<取得日 YYYY-MM-DD>-claude-code-release.mdに保存し、 原文 URL・取得日時・取得手段・Tier(references/schema.md§4 によりこの取得元は A)を メタとして記録する。 current-baseline.mdはこの raw を引用してclaude_code_versionを確定する (不変条件「すべての主張は raw を引用」を init でも満たす)。- WebFetch 失敗時のみ:
claude --versionの実行をユーザーに案内(! claude --version)し、 申告値をセット。raw 引用が無いためcurrent-baseline.mdに 「ソース: 手動入力(暫定)・取得日」と明記し、フロントマターはreferences/schema.md§2 に従いstaleを真として記録、 次回オンライン時に Tier A 取得で上書き提案する旨を本文に残す。
- WebFetch 対象 URL(確定):
-
schema 軽量ポインタ記録(決定 ウ)
current-baseline.mdにreferences/schema.md§6 の軽量ポインタを記録する (schema_version/ 本リポジトリの該当 commit ハッシュ=git rev-parse HEAD/ 規約 1〜2 行サマリ)。 schema 全文は複製しない。
-
本リポジトリ側の整備
.gitignoreにwiki-vault行が無ければ追記する(誤コミット防止)。.gitignoreに.llm-wiki-inbox.jsonl行が無ければ追記する(会話 URL hook の vault 外 inbox・誤コミット防止・Phase 3e)。minitools_pathの対話設定(Phase 4・任意): Medium 取り込み(4a content routing / 4b Notion DB discovery)に使う外部リポジトリ minitools のパスをAskUserQuestionで確認する(既定候補/Users/tak/Projects/minitools)。「設定しない(Medium 機能を使わない)」を必ず併置する。「設定しない」が選ばれた場合はminitools_pathキー自体を.llm-wiki.jsonに書かない(キー不在=Medium 機能のみ無効化・他モード通常動作・§ステップ0.5)。パスが選ばれた場合のみminitools_pathキーを生成する。ハードコード禁止(外部リポジトリパスのため設定必須)。.llm-wiki.jsonを生成する。MVP は相対パス./wiki-vaultを正、絶対パスは参考値、schema_versionはreferences/schema.mdの値を転記、minitools_pathは上記対話で設定した場合のみ含める:{ "vault_relative": "./wiki-vault", "vault_absolute": "<実体絶対パス>", "schema_version": "<references/schema.md の schema_version>", "minitools_path": "<minitools 実体パス・設定時のみ>" }
-
ボールト側
.gitignore整備- vault 直下
.gitignoreを Read。存在しなければ作成。 .llm-wiki.lock行が無ければ追記(書き込みモード排他制御の lock ファイルが誤コミットされないため)。.DS_Store等の既存除外行は維持。
- vault 直下
-
ボールト側 Git 初期化
git -C ./wiki-vault init(未初期化時)→add -A→ 初回コミット (メッセージ例chore: llm-wiki init (schema vX.Y.Z))。
-
完了サマリ(生成パス・確定バージョン・schema_version)を提示する。
-
session-start hook 設定例の案内(参考・自動インストールしない)
references/session-start-hook.example.jsonが同梱されていることを利用者に案内する。- この設定例は Claude Code の
SessionStarthook でwiki/current-baseline.md(現在の正=claude_code_version/updated/last_tier_a_refresh/migration_pending)を ambient context にロードするためのもの。/llm-wiki queryを呼ぶ前に「いま何が真か」を起動時から把握しておく目的。 - 利用者が 手動で
.claude/settings.json(project local 推奨・グローバル~/.claude/settings.jsonへの登録は他プロジェクトでも発火するため非推奨)にマージする想定。/llm-wiki initは自動マージしない(read-only context preload とはいえ利用者設定ファイルの書き換えは行わない方針)。マージ対象はhooksキーのみ(example の_comment/_notesは説明用フィールドなので.claude/settings.jsonにはコピーしない。Claude Code の settings parser が未知の root key をどう扱うかは保証されないため)。 commandは[ -L ./wiki-vault ] && cat ./wiki-vault/wiki/current-baseline.md 2>/dev/null || trueで CWD = リポジトリルート前提・vault 不在時は無音終了。matcher: "*"は startup / resume / clear / compact 全部で再注入。narrow したい場合は example の_notes参照。
-
会話 URL hook 設定例の案内(参考・自動インストールしない・Phase 3e)
references/conversation-url-hook.example.json(UserPromptSubmithook の settings スニペット)とreferences/conversation-url-hook.example.sh(URL 抽出スクリプト本体)が同梱されていることを利用者に案内する。- この hook は会話中のプロンプトに含まれる URL を検出し、repo ルート(CWD)直下 vault 外
.llm-wiki-inbox.jsonlに append する(スキル起動・vault 書き込み・lock 取得は伴わない=検出のみ)。貯めた URL は後で/llm-wiki review(モード H)で triage して承認制 ingest する。 - 利用者が 手動で
.claude/settings.json(project local 推奨・グローバルは他プロジェクトでも発火するため非推奨)にマージ対象はhooksキーのみでマージする想定(/llm-wiki initは自動マージしない・session-start hook 同流儀)。スクリプトは冒頭の[ -L ./wiki-vault ] || exit 0で CWD = リポジトリルート前提・vault 不在時は無音終了。 - 2 つの example の使い分け:
references/conversation-url-hook.example.jsonは既存 settings にhooksスニペットだけ手動マージする教材版(.example.shを参照・_comment/_notes付き)。一方このリポジトリ直下の.claude/settings.example.jsonはこの repo をそのままクイック有効化する全文設定(active 実体references/conversation-url-hook.shを参照)で、cp .claude/settings.example.json .claude/settings.jsonでコピーするだけで有効化できる。.claude/settings.jsonは.gitignore済=コミットされないため、クローン/参照しただけの人にUserPromptSubmithook が自動適用されることはない(opt-in)。
モード B: /llm-wiki ingest [--type=practice|--feature=]
ソースを取り込み、コンパイルする。
lock: ステップ 0.5 ボールト前提チェック直後に取得(ステップ 0.6 参照)。本モード全体を排他制御し、ステップ 9 のコミット後に解放する。例外時も解放を試みる。
- lock 取得(ステップ 0.6 の手順)。失敗時はステップ 0.6 の skip 手順に従う。
0.a --feed=<document-less-source>: registry append 特例(Phase 4 4b / Phase 4c 登録・§5.2b・document-less ソース登録): 引数の --feed=<v> を先に検査し、v が <source>:<selector> スキーム(<source> が既知 document-less source={notion-medium-db, claude-blog-sitemap} のいずれか・http(s) URL ではない・例 notion-medium-db:default / claude-blog-sitemap:default)かつ path-or-url が省略されている場合、通常 ingest pipeline を short-circuit する:
- step 0.b(migration_pending 提案)/ step 1〜8.5(raw 確保・同一 source 判定・矛盾検出・overview 更新)をすべて skip(raw を作らず source ページも生成しないため
references/schema.md§4.5 の衝突 3 件=lint sources-empty / 不変条件 #2 引用 / mode B raw 前提 pipeline がいずれも発生しない)。 vを<source>:<selector>に分解(source=:前=discovery source 種別〔現状notion-medium-db/claude-blog-sitemapの 2 種〕、selector=:後〔省略時はdefault〕)。未知 source 種別(上記 2 種以外)はエラーで中断し許容種別 2 種を案内。Read("./wiki-vault/wiki/current-baseline.md")のfeed_registry[]を取得。同一{source, selector}の既存エントリがあれば no-op(重複 append しない・logfeed_registry: already registered <source>:<selector>を 1 行追記して step 9 相当へ)。- 無ければ
feed_registry[]末尾に{source: <source>, selector: <selector>, label: <利用者に AskUserQuestion で確認した人間可読ラベル・既定 "<source> (<selector>)">, added: <今日>}を append で Edit(references/schema.md§2.1)。 wiki/log.mdにfeed_registry: register <source>:<selector> (<日付>)を追記し、ボールト Git でcurrent-baseline.md(+ log.md)を 1 commitchore: register feed <source>:<selector>。- lock 解放(step 10 相当)して終了(以降の step に進まない)。
--watch/--feed非伝播ルールには影響しない(本特例は利用者の直接呼び出しのみ・§5.4)。 - 注:
--feed=<http(s)_rss_url>(http スキーム)は本特例に当たらず通常 ingest(source ページにfeed_urlを立てる既存 3g 挙動・step 1 以降)に進む。短絡するのは<source>が既知 document-less source(notion-medium-db/claude-blog-sitemap)かつ path-or-url 省略時のみ。claude-blog-sitemapのselectorは実用的意味を持たない(default固定運用)が、スキーマ統一のため{source, selector, label, added}形を踏襲する。
0.c --backfill-dates 遡及補完特例(Phase 5・一回限りバッチ・§5.2c): 引数に --backfill-dates があり path-or-url が省略されている場合、通常 ingest pipeline を short-circuit し、既存 raw の日付メタを後追い補完する一回限りのバッチを実行する(恒久 cron mode ではない・lint には置かない=lint は frontmatter を変更しない不変条件。ingest が raw frontmatter 書き込みの正規の場=schema §3 source_url 補完前例):
- step 0.b(migration_pending 提案)/ step 1〜8.5 を skip(個別 ingest pipeline に乗らない)。
- 対象抽出:
Glob('./wiki-vault/raw/**/*.md')で全 raw を列挙し、各 raw をRead(offset=0, limit=30)でフロントマター取得。published_atとlast_modifiedが両方とも存在しかつunknown以外の raw は skip(補完済み)。少なくとも一方が欠落 orunknownの raw を対象にする。 - 経路別補完(本文・
fetched_at・順序は不可触=E1): 各対象 raw のsource_url(無ければ skip + logbackfill: no source_url <path>)の host を判定し、本文を再取得せず軽量経路で日付のみ取得:github.comblob →gh api "repos/{o}/{r}/commits?path={p}&sha={ref}&per_page=1"のcommitter.date→last_modified=gh-commit。medium.com→ skip(backfill は gh/feed/sitemap/WebFetch の軽量経路のみ。Medium は Playwright 再取得〔scrape-medium・対話・重い〕が必要なため backfill では補完しない。日付が要るなら当該 URL を再ingestすれば mode B step 3--emit-metaで乗る。logbackfill: medium skipped (needs Playwright re-ingest) <path>)。- その他 http(s)(blog / 一般記事 / docs を含む) → WebFetch で (1) 構造化メタ
article:*_time/JSON-LD →html-meta、(2) 取れなければ本文可視日付の保守パース →html-body(references/schema.md§3「html-body保守パース規約」)。claude.com/blog/<slug>の本文Date: <Month DD, YYYY>はこの (2) でpublished_at=html-bodyとして回収する(claude.com/sitemap.xmlは<lastmod>を持たないため sitemap 経路は使わない=dry-run 実証)。なお<lastmod>を持つ sitemap のソースがあればsitemap-lastmodも可(claude.com は非該当)。 - 取得不能・保守ルール非充足は当該 raw を
unknownのまま据え置き(無理に埋めない)。
- frontmatter 追記のみ: 取得できた日付 4 フィールドを当該 raw frontmatter に Edit で追記(既存本文・
fetched_at・他フィールドは不可触)。 - index 代表鮮度日の更新: 補完で日付が入った raw を
sources:に含むwiki/sources/*.mdページを特定し、wiki/index.mdの当該行(鮮度: …)を再計算・更新(モード B step 9 と同仕様)。 - commit / log:
wiki/log.mdにbackfill-dates: scanned=S targets=T filled=F unknown=U (YYYY-MM-DD)を追記し、raw 群 + index.md + log.md を 1 commitchore: backfill raw date metadata (<日付>)。個別 raw の失敗は skip + log で batch 継続。 --dry-run: Edit / commit せず対象件数・経路別内訳・would-fill <path>: last_modified=<date>(<source>)をレポート。- lock 解放(step 10 相当)して終了(以降の step に進まない)。
--backfill-datesは他フラグ(--type/--feature/--watch/--feed)と併用不可(併用はエラーで中断)。
0.b migration_pending 提案(書き込み前・lock 取得直後): モード L §ステップ 2.5 の手順を同一ロジックで適用(候補 1 件でも「適用しない」併置、multiSelect: true、上位 4 件、承認分は 共通 surface 経由で new_url を本モード step 3 (ii) に渡して新規 raw を ingest し既存 source ページに統合+当該 migration_pending エントリ削除+ 1 commit。Phase 3d 共通 surface 設計)。0 件選択は通常 ingest 続行。
-
引数パース
- 第 2 トークン:
path-or-url。 - 残りトークンは
--key=value形式(--type=practice/--feature=<slug>/--feed=<rss_url>)と 値なしフラグ--watch(Phase 3f)/--backfill-dates(Phase 5・path-or-url 省略の一回限りバッチ・step 0.c で short-circuit・他フラグと併用不可)。許容はこの 5 種のみ。未知キーはエラーで中断(メッセージで許容キーを案内)。 --type=practiceと--feature=<slug>を同時指定した場合はエラーで中断 (practice は主型、feature は派生型として意味が衝突するため)。--watch(Phase 3f・watchlist 登録フラグ)は--type/--feature/--feedと併用可。 利用者の直接/llm-wiki ingestでのみ有効で、共通 surface の内部呼び出し(mode G G-6 / mode H H-5 / mode F migration / mode I I-6)からは渡されない(後述「--watch非伝播」)。--feed=<rss_url>(Phase 3g・フィード登録フラグ): 指定時、生成/更新する source ページのフロントマターにfeed_url: <rss_url>を立て、mode I(discover-watchlist)の巡回対象に登録する。利用者の直接/llm-wiki ingestでのみ有効(共通 surface 内部呼び出しからは渡されない=後述「--feed非伝播」)。--watchとの併用可(--feed= サイト全体の RSS 登録・--watch= 当該 site-url の watchlist 登録として共存。ただしfeed_urlはサイト全体に付けるのが想定ユースケース)。retrofit(既存 source への後付け登録)は frontmatter にfeed_urlを手動追記すれば足り、3g では別コマンドを新設しない。- document-less スキーム(
--feed=<source>:<selector>で<source>∈{notion-medium-db, claude-blog-sitemap}・path-or-url 省略)は本 step に到達せず step 0.a の registry append 特例で処理済み(feed_registry[]に登録・source ページにfeed_urlを立てない・§5.2b)。短絡するのは http(s) ではないこの 2 種のみ。 --feed=notion-*:<selector>スキーム(Phase 4 4b・document-less ソース登録): 値が http(s) URL ではなくnotion-*:スキーム(例notion-medium-db:default)の場合は step 0.a の registry append 特例で処理済み(本 step には到達しない)。この形式は source ページにfeed_urlを立てずcurrent-baseline.md.feed_registry[]に登録する(§5.2b)。
- document-less スキーム(
- 第 2 トークン:
-
引数判定: URL(
http(s)://)か ローカルパスかを判定。 -
raw 確保(URL は host 別 routing・Phase 4 4a)
- URL: host を判定して取得経路を分岐する(design §5.1・F-1 gh api routing と同型)。取得後は
raw/<種別>/<取得日 YYYY-MM-DD>-<slug>.mdに保存し、原文 URL・取得日時・取得手段・tier(references/schema.md§4 で判定)をメタ記録。取得失敗時はユーザーに手動 raw 保存を案内して中断(黙って空ページを作らない)。medium.com/*.medium.com(Phase 4 4a・新設) → minitoolsscrape-medium --cdp経路:- availability check: ステップ0.5 の
minitools_available(minitools_path設定済み+ディレクトリ存在)を確認。false(未設定 or 不在)なら「Medium 取得には.llm-wiki.jsonにminitools_pathの設定が必要です」と案内し、当該 URL を中断(raw 未作成・他ソース継続=clean failure・空ページを作らない)。uv不在も同様に検出して案内・中断。 Bash('uv run --directory <minitools_path> scrape-medium --url "<url>" --cdp --emit-meta')を実行(--emit-metaで stdout 先頭に日付 YAML frontmatter を前置・続けて英語原文 Markdown 本文=memory: minitools-phase4-cli-interface / Phase 5 Phase 2)。- exit 0 → stdout を frontmatter ブロックと本文に分離してから
raw/articles/<取得日>-<slug>.mdに保存する。- 分離規約: stdout の 1 行目が
---なら、その次の---行までを emit frontmatter ブロックとみなし、ブロック内のpublished_at:/last_modified:/published_at_source:/last_modified_source:を読む。2 つ目の---の次行以降を本文とする(emit frontmatter を本文に混入させない)。1 行目が---でない(--emit-meta非対応 minitools 等)場合は stdout 全体を本文とみなし、日付 4 フィールドをunknownにフォールバック(clean degradation・例外にしない)。本文先頭が---(hr)になる稀な記事も、2 つ目---終端の成立で分離が破綻するなら unknown 退避(偽日付を出さない・design リスク表)。 - raw 本文 = 上記で分離した本文(英語原文 Markdown)。
- raw frontmatter:
source_url: <正規化前の原文 URL>/fetched_at/fetched_via: minitools-playwright/tier: B/note: English original scraped via minitools Playwright CDP (verbatim, untranslated)(既存・不変=references/schema.md§3fetched_via語彙・verbatim 原則 D2)。日付メタ(Phase 5・Phase 2 で解禁)は emit frontmatter の 4 フィールドを転記する(scrape-medium --emit-metaが JSON-LDdatePublished/dateModifiedを正本に抽出=出所html-meta。値域YYYY-MM-DD | unknown・出所html-meta | unknown=schema §3 と一致)。frontmatter 不在フォールバック時は 4 フィールドともunknown。
- 分離規約: stdout の 1 行目が
- exit 1 → stderr を提示し当該 source を中断(Cloudflare ブロック / ログイン切れ / 404 等。raw 未作成・空ページを作らない=既存「取得失敗は中断」規約)。
- availability check: ステップ0.5 の
github.comの blob URL → mode F F-4a の gh api 経路に準じる(WebFetch では JS シェルしか返らないため。詳細は §F-4a)。- それ以外 →
WebFetchで取得(既存挙動)。 - mode H(review・会話 URL)が Medium URL を ingest する場合も本 step 3 を通るため 4a routing が自動適用される(対話経路なので Playwright OK)。
- 日付メタ抽出(Phase 5・経路別・ベストエフォート): raw 保存時、取得経路ごとに元ドキュメント自身の日付を抽出し frontmatter の
published_at/last_modified/published_at_source/last_modified_sourceに記録する(値域・出所 enum はreferences/schema.md§3「raw フロントマター日付メタ」)。取得不能は当該日付をunknown・対応する*_sourceもunknown。fetched_atやコンパイル日で代替しない。- gh api 経路(
github.comblob):gh api "repos/<owner>/<repo>/commits?path=<path>&sha=<ref>&per_page=1"の[0].commit.committer.date(ISO8601)をYYYY-MM-DDに切り詰めlast_modified=gh-commit。published_atは通常unknown(CHANGELOG はエントリ日付があるが Phase 5 は commit 日で統一=取りこぼし無し)。 - WebFetch 経路(一般 URL・blog 含む): まず構造化メタ
<meta property="article:published_time">→published_at、<meta property="article:modified_time">→last_modified、無ければ JSON-LDdatePublished/dateModifiedを抽出し出所html-meta。構造化メタが取れない場合は本文可視日付の保守パース(出所html-body)にフォールバック(references/schema.md§3「html-body保守パース規約」=publication-context 限定・曖昧なら unknown・偽日付防止)。claude.com/blogのDate: <Month DD, YYYY>等もこの経路でpublished_at=html-bodyとして回収する(sitemap にlastmodが無いため)。いずれも取れなければunknown。 - feed / sitemap 由来(mode I 経由の内部 ingest): mode I I-4 が抽出した日付を ingest にキャリーする(本 step では個別記事を再パースしない)。RSS/Atom の
pubDate/updated/published→出所feed-pubdate(published→published_at・updated→last_modified)、sitemap のlastmod→last_modified=sitemap-lastmod。 - Medium 経路(4a):
scrape-medium --emit-metaが JSON-LD(datePublished/dateModified)を正本に抽出した日付を出力先頭 frontmatter で受け取り、published_at/last_modified=出所html-metaとして転記する(上記 step 3 の分離・転記規約を参照)。取得不能フィールドはunknown(Phase 5 の Phase 2・本フェーズで解禁)。 - 抽出値が日付として parse 不能な場合は
unknownに正規化(偽日付を入れない)。抽出失敗は例外にせず続行しwiki/log.mdにdate extraction: unknown (<raw path>, <route>)を残す。
- gh api 経路(
- ローカルパス: 既存 raw ファイルとして検証。無ければエラーで中断
(raw は人間が追加するもの。ingest が raw を新規創作しない)。
--type=practice指定時に raw が未存在の場合、raw/notes/への事前配置を案内して中断 (practice ページのsources:必須を満たすため)。
- URL: host を判定して取得経路を分岐する(design §5.1・F-1 gh api routing と同型)。取得後は
3.5. 同一 source 判定(共通 surface・Phase 3d)
raw 保存後・wiki 更新前に、入力 URL(URL 指定の場合)から既存 source ページとの突合を行う。ローカルパス入力の場合は本 step を skip し既存 (iii) 新規 source 作成パスに進む。
URL 正規化(フル仕様・Phase 3e・本節が単一正本): 突合前に URL を次のルールで正規化する。この正規化規約は mode G G-4 / mode H H-3 からも参照される唯一の正本であり、それらの節では再記述しない。適用順:
- host を lowercase 化(全 URL に安全)。
- fragment 除去(
#...を削除・全 URL に安全)。 - 末尾スラッシュを 1 個まで除去(
/page/→/page、/単独はそのまま)。 - tracking param 除去(curated denylist): query string のうち denylist キーのみ除去する。denylist =
utm_source/utm_medium/utm_campaign/utm_term/utm_content/fbclid/gclid/ref/ref_src/mc_cid/mc_eid/source(Phase 4 追加・Medium の?source=email-...等 tracking param)。denylist 以外の param(?page=2/?id=123/?v=xxx等)は保持する。 - host allowlist 正準化(Tier A 4 host・任意): schema §4 の 301 移転元(
docs.anthropic.com/en/docs/claude-code/*→code.claude.com/docs/en/*、docs.anthropic.com/en/api/*→platform.claude.com/docs/en/*)は移転先 host に正準化してよい(突合の取りこぼし防止)。
denylist 方式の根拠(strip-all-query 不採用): Phase 3e は任意の Tier B host を dedup 経路に初めて載せる。Tier B には
?page=2等の意味のある query param が多く、strip-all-query はこれらを畳んで別 URL を同一視し content を silent に取りこぼす。tracking param のみを curated denylist で除去するのが correctness 上の正解。fragment 除去・host lowercase は全 host に安全。
normalize-on-compare(移行不要): raw のフロントマター
source_urlは原文 URL を保持(E1 不変・正規化前)。突合は (i)/(ii) いずれも比較時に両辺を本ルールで正規化して行うため、本正規化を導入しても既存 rawsource_urlの書き換え(移行)は不要。一方current-baseline.md.pending_discoveries[].urlは正規化済みで格納されるため、mode G G-4 冒頭で一度だけ再正規化する(mode G 側に記載)。
3 段判定: 正規化後の URL を次の優先順で判定する:
| 優先 | 判定 | アクション |
|---|---|---|
| (i) | 既存 source ページの最新 raw の source_url と完全一致(双方を正規化して比較)。解決はモード F F-3 step 3〜4 と同じ走査: 各 wiki/sources/*.md の sources: 末尾(YYYY-MM-DD プレフィックス最大)の raw を Read し、その raw のフロントマター source_url を取得する。source ページ自身のフロントマターに source_url は無い(schema §2 共通フィールドに含まれず、source_url は raw のフロントマターキー=schema §3)ため、必ず raw を辿る。source_url を欠く raw は突合対象外。 |
既存 source ページに統合(step 6 へ。本 raw を sources: 末尾 append、再コンパイル、updated 進行) |
| (ii) | current-baseline.md.migration_pending[].new_url のいずれかと一致 |
該当エントリの source_slug で示される旧 source ページに統合(新 raw=source_url=new_url を sources: 末尾 append することで F-3 走査が以降 new_url を最新 URL として解決する。source ページ自身に source_url フィールドは無いため書き換え対象は無い)、migration_pending エントリを削除(step 6 / step 9 内で同一 commit)。tier は旧ページの値を継承(再判定しない) |
| (iii) | 上記いずれにも一致しない | 新規 source ページを作成(既存挙動・step 6 (a)/(b)/(c) と同じ) |
判定結果(i/ii/iii とマッチした既存ページがあれば slug)を以降の step に伝搬する。
-
既出チェック
wiki/log.mdを Grep し同一ソースの既出を確認。既出なら再取り込み可否を AskUserQuestion で確認(強制しない)。
-
全文読込・要点確認
- raw を全文読込し要点を抽出 → ユーザーに要点を提示し AskUserQuestion で確認。
-
ページ生成/更新(引数による分岐)
(a) 引数無指定(既存挙動・変更なし):
references/schema.mdのページタイプ規約・references/page-templates.mdの雛形に従い、sourceページ(必須)+関連concept/entity/comparisonを生成/更新する。
(b)
--type=practice:- 主たる生成型を
practiceに切り替え、wiki/practices/<slug>.mdをreferences/page-templates.mdの practice テンプレで生成する (セクション「試した内容 / 文脈・前提 / 結果 / 結論 / 関連 / 矛盾」)。 - source ページも併存生成する(raw の要約として
wiki/sources/<slug>.md)。 practice は raw に対する「実践と効果の記録」として別ページ。 - 関連 concept/entity の派生は通常 ingest と同じく必要に応じて生成。
(c)
--feature=<slug>:- 通常の source 生成フローを実行(source + 派生 concept/entity)。
wiki/features/<slug>.mdの存在を確認:- 存在しない →
references/page-templates.mdの feature テンプレで新規作成。 - 存在する → 「バージョン別仕様差分」セクションに今回 source の知見を追記。
既存記述と矛盾する場合は schema.md §「黙って上書きしない」に従い
## 矛盾セクションへ。
- 存在しない →
- feature ページのフロントマター
claude_code_version/updatedを新 source の値で更新 (version 進行を反映)。本文の他部分は触らない。 - バージョン逆行ケース(新 source の
claude_code_versionが既存 feature ページより古い)は## 矛盾セクション追記+AskUserQuestion で続行可否を確認(黙って上書きしない)。
いずれの分岐でも、フロントマターは
references/schema.md§2 の必須フィールドを全て充填する (tier 判定は §4。判定ロジックは Phase 2a だが値の保持は MVP から必須)。 本文に raw 引用と [[wikilink]] を含める(記法はreferences/schema.md§3)。--watch指定時の watchlist 登録(Phase 3f): 利用者が--watchを指定した場合、生成/更新する source ページのフロントマターにwatch: trueを立てる(フィールド定義はreferences/schema.md§2)。これにより mode W(refresh-watchlist)の日次自動再取得対象になる。- Medium URL は
--watchを reject(Phase 4・D8・§5.3): 入力 host がmedium.com/*.medium.comかつ--watch指定の場合、watchを立てず「Medium は Playwright(scrape-medium --cdp)取得のため watchlist 自動 refresh(mode W = WebFetch 既定経路)に非対応です。最新版は再ingestしてください」と案内してwatch無しで続行する。理由: mode W は WebFetch 既定で mode B step 3 の host routing を通らず Playwright に乗れないため、Medium を watch すると毎晩 Cloudflare/paywall で fail しfetch_status: failed(lint #15)が永続する。この reject はtier: B判定(下記)より先に評価する。 tier: B専用: 解決した tier が A の場合はwatchを立てず、「Tier A はrefresh-tier-a(mode F)が既に日次 refresh するため watchlist 登録は不要です」と警告してwatch無しで続行する(黙って立てない・mode F と mode W の二重 refresh を避ける)。- 既存 source ページに統合(step 3.5 (i)/(ii))するケースでも
--watch指定なら当該 source ページにwatch: trueを立てる(後付け登録に相当)。tierは (ii) で旧ページ値を継承するため、その値が A なら上記 tier=A 警告分岐に従う。
--feed指定時のフィード登録(Phase 3g): 利用者が--feed=<rss_url>を指定した場合、生成/更新する source ページのフロントマターにfeed_url: <rss_url>を立てる(フィールド定義はreferences/schema.md§2)。これにより mode I(discover-watchlist)の日次フィード巡回対象になる。- 既存 source ページに統合(step 3.5 (i)/(ii))するケースでも
--feed指定なら当該 source ページにfeed_urlを立てる(後付け登録に相当)。 --watchとの併用可:feed_url(サイト全体の RSS 登録)とwatch: true(単一 URL の watchlist 登録)は同一 source ページに共存できる。
共通 surface 追記事項(Phase 3d・step 3.5 の判定が (i) または (ii) の場合):
- sources: 末尾 append(F-6・時系列保証): 既存 source ページのフロントマター
sources:末尾に新 raw のパスを append。順序は時系列保証(新しい raw が末尾)。既存 raw は不変保持(E1)で削除・並べ替えしない。 - (ii) の追加処理: マッチした旧 source ページへ新 raw(フロントマター
source_url= new_url・正規化前の原文 URL を保持)をsources:末尾 append する。これにより F-3 走査(sources:末尾 raw → その raw のsource_url)が以降 new_url を最新 URL として解決する(source ページ自身にsource_urlフィールドは無いため書き換えは行わない/不要。旧記述「旧 source ページのフロントマターsource_urlを書き換え」は存在しないフィールドを指していた誤りで Phase 3c carve-out 実機検証で是正)。tierは旧ページの値を継承(不可触)。title等の手動編集領域は不可触。updatedを今日付に進める。 - (ii) の migration_pending エントリ削除:
current-baseline.md.migration_pending[]から該当エントリ(source_slugで特定)を削除する。これは step 9 のコミット内で同一 commit に含める。- anchor 戦略:
old_stringには当該エントリ 4 キー全体(- old_url: ...からsource_url: ...まで・前後の\nを含む 5 行)を厳密マッチで指定しreplace_all: false。複数エントリがあってもold_urlで一意に特定されるため衝突しない。
- anchor 戦略:
-
矛盾検出(一段目・同一トピックのみ)
- 新主張が触れる [[wikilink]] 先の既存ページのみを読み、矛盾があれば当該ページに
## 矛盾セクションを追記し両論併記(黙って上書きしない)。 - トピック横断の矛盾は MVP では検出しない(Phase 2b lint #5 へ委譲)。
- 日付補助注記(Phase 5・b1): 新旧両側の出典 raw の
last_modifiedがいずれもunknown以外なら、## 矛盾セクションに(last_modified: <旧>→<新>)を併記する(ingest 時は新旧 raw が手元にあるため追加 Read 0)。配置は #11 パーサと衝突しない形に固定: 両論 bullet(- **<ラベル>(<version>・Tier <X>>)**: …)でも評価:行でも決着(行でもない、- **で始まらない独立行として追記する(lint #11 は- **始まりの行のみ両論候補として解釈するため、本注記は #11 のパース失敗・両論誤検出を引き起こさない=references/lint-rules.md#11 パース仕様)。決着の自動適用はしない(「黙って上書きしない」維持・lint #11 の承認制決着が正経路)。Tier A 優先は不変=日付で公式の権威を覆さない。片側でもunknownなら日付注記を付けない。
- 新主張が触れる [[wikilink]] 先の既存ページのみを読み、矛盾があれば当該ページに
-
Tier B のバージョン乖離提案
- tier が B(
references/schema.md§4)かつ当該知見の前提バージョンがcurrent-baseline.mdと乖離する場合、current-baseline.md更新を AskUserQuestion で承認制提案する(自動更新しない)。
- tier が B(
8.5. overview 自動更新(Phase 3d・C)
wiki/overview.md の ## 現状 セクションを更新する。詳細仕様は references/schema.md §8 参照。
- 統計取得:
Glob('wiki/sources/[!_]*.md')等で各タイプの 5 ディレクトリ(sources / concepts / syntheses / practices / features)を count(_fixture-*除外)。 - 日付: 「最終 ingest」は本日付、「最終更新」も本日付(後段の値変化ガードで差替検査)。
- 値変化ガード:
wiki/overview.mdを Read し## 現状セクションの現状の 5 件 + 2 日付をパース。すべて同値なら Edit を skip しwiki/log.mdにoverview unchanged (<日付>)を 1 行追記、step 9 へ進む。 - 1 つでも変化があれば:
## 現状セクション(見出し直後から空行 or 次見出しまで)を Edit で置換し、step 9 のコミットに含める。 - 境界判定失敗時(
## 現状見出しが見つからない): overview Edit を skip し log.md にoverview update skipped: section boundary not found (<日付>)を warning 追記。step 9 へ。 - 更新対象が無い場合: step 6 で source ページ更新なしのケース(dry-run 相当)は overview 更新も skip。
-
index/log 更新・コミット
wiki/index.mdに各生成/更新ページの主要主張サマリ(1〜2 行)を維持 (Phase 2 横断矛盾スキャンの前提)。- 代表鮮度日の併記(Phase 5・b4): 各生成/更新ページのサマリ行末尾に
(鮮度: YYYY-MM-DD)を付す/更新する(畳み込み・最大値の定義はreferences/schema.md§3「鮮度日(畳み込み)」「index.md 代表鮮度日記法」)。各 raw の鮮度日はlast_modifiedがunknown以外ならそれ、不在ならpublished_at、両方unknownなら除外(unknownは文字列センチネル=null 合体??ではなく明示的に読み飛ばす)。代表値はsources:全 raw の鮮度日のうちunknownを除いた最大値(全て鮮度日なしなら(鮮度: unknown))。新規 (iii) は当該 raw のみで算出(追加 Read 0)。既存統合 (i)/(ii) は再 ingest で古い記事を足しても鮮度日が後退しないよう、当該ページのsources:raw frontmatter を読んで最大値を取り直す(1 ページ分・bounded・step 6 で読んだ分は再利用)。lint #17 と b3 はこの index トークンを再利用する。 wiki/log.mdに操作(日時・ソース・生成/更新ページ)を追記。- step 6 共通 surface (ii) で
current-baseline.md.migration_pendingエントリ削除が発生した場合は同一 commit に含める。 - step 8.5 で overview Edit が発生した場合は同一 commit に含める。
- 呼び出し元が discover-tier-a(モード G)由来で
current-baseline.md.pending_discoveriesエントリ削除をステージしている場合は、それも同一 commit に含める(モード G G-6 が ingest 本体呼び出し前後にステージする・「ingest と候補削除を同一 commit」契約の担保)。上記 migration_pending / overview と同じく「呼び出しコンテキスト由来の付随ステージ変更を per-source commit に同梱する」一般規約として扱う。 - ボールト側 Git に操作単位でコミット。
-
lock 解放(ステップ 0.6 の手順)。例外時も
trap相当で削除を試みる。
--watch 非伝播(Phase 3f・advisor 指摘・誤発火防止)
--watch(watchlist 登録フラグ)は 利用者の直接 /llm-wiki ingest 呼び出しのみで有効。共通 surface(モード B step 3〜9)を内部呼び出しする以下の経路は --watch を渡さない=default-off を維持し、discover / 会話 / migration 由来の URL が誤って watchlist 登録されるのを防ぐ:
| 呼び出し元 | 経路 | --watch |
|---|---|---|
| モード G(discover-tier-a) | G-6 → mode B ingest 本体 | 渡さない |
| モード H(review・会話 URL) | H-5 → mode B ingest 本体 | 渡さない |
| モード I(discover-watchlist) | I-6 → mode B ingest 本体 | 渡さない |
| モード F migration(mode L §2.5 migration_pending 提案 / mode B step 0.b 承認後の migration ingest) | mode B(migration case) | 渡さない |
--feed 非伝播(Phase 3g・誤発火防止)
--feed(フィード登録フラグ)も --watch 同様に非伝播とする。共通 surface 内部呼び出し(モード G G-6 / モード H H-5 / モード I I-6 / モード F migration)は --feed を渡さない。mode I の I-6 が mode B を呼ぶときも --feed を渡さない(発見された各記事 URL に feed 登録は不要)。
| 呼び出し元 | 経路 | --feed |
|---|---|---|
| モード G(discover-tier-a) | G-6 → mode B ingest 本体 | 渡さない |
| モード H(review・会話 URL) | H-5 → mode B ingest 本体 | 渡さない |
| モード I(discover-watchlist) | I-6 → mode B ingest 本体 | 渡さない |
| モード F migration | mode B(migration case) | 渡さない |
retrofit(既存 source の後付け登録・Phase 3f)
既に取り込み済みの source ページを後から watchlist に載せたい場合は、当該ページのフロントマターに watch: true を手動追記すれば足りる(mode W が次回走査で拾う)。3f では retrofit 専用コマンドを新設しない。ingest 後に毎回 AskUserQuestion で登録可否を問う案は、共通 surface での毎回 prompt・誤発火リスクのため不採用(design D5)。
モード C: /llm-wiki query <質問>
Wiki から引用付きで回答する。読み取り専用(ボールトに書き込まない)。
wiki/index.mdを読み、質問に関連するページを特定する。- 関連ページのみを選択読込(全 wiki 走査禁止=コンテキスト圧迫回避)。
- 引用付きで回答を統合する(各主張に出典ページ/raw を併記)。
- Wiki に不足がある場合は
WebSearch/WebFetchで補完し、その箇所を⚠️ Wiki 外(Web 検索)と明示する。 - 補完で有用な未取り込みソースが見つかれば
/llm-wiki ingest <url>を提案する (提案のみ。query では取り込まない)。
モード D: /llm-wiki synthesize <テーマ>
テーマ横断の派生成果物(チートシート/Tips 集等)を生成/再生成する。
lock: ステップ 0.5 直後に取得(ステップ 0.6 参照)、最終コミット後に解放する。
- lock 取得(ステップ 0.6 の手順)。
wiki/index.md→ テーマ関連ページ群を選択読込。references/page-templates.mdのsynthesis雛形に従いwiki/syntheses/<slug>.mdを生成、または既存があれば再生成する。 フロントマターはreferences/schema.md§2 に従い充填。- すべての項目は元 Wiki ページ/raw を引用する。Wiki 外で補完した箇所は
⚠️ Wiki 外(Web 検索)と明示し、ingest を提案する。 3.4. 鮮度反映(Phase 5・b3): 引用元ページの代表鮮度日(wiki/index.mdの(鮮度: …)トークンを再利用・無ければ当該ページsources:raw から算出)を成果物に反映する:- 引用元の鮮度日が 180 日超(
unknownは対象外)の項目には⚠️ 古い情報(鮮度: <date>)を併記する。 - 同列の選択肢/手法を列挙する箇所は、鮮度日の新しい順に並べる(
unknownは末尾)。 3.5. overview 自動更新(Phase 3d・C): モード B step 8.5 と同じ仕様(references/schema.md§8)。統計 5 件 + 2 日付を計算しwiki/overview.mdの## 現状セクションを Read、全値同値なら Edit を skip し log.md にoverview unchanged (<日付>)を追記、差分があれば Edit して step 4 のコミットに同梱する。境界判定失敗時は skip + log warning。
- 引用元の鮮度日が 180 日超(
wiki/index.mdのサマリ更新(synthesis ページ行に代表鮮度日(鮮度: YYYY-MM-DD)を併記=当該 synthesis のsources:全 raw の鮮度日の最大値〔unknown除外・references/schema.md§3 定義〕・モード B step 9 と同仕様)・wiki/log.md追記・ボールト側 Git コミット(overview Edit が発生していれば同一 commit に含める)。- lock 解放(ステップ 0.6 の手順)。
モード L: /llm-wiki lint [--check=]
Phase 2a 機械判定 7 検査(#1 孤立 / #2 更新日 30 日超 / #3 claude_code_version 乖離 /
#4 stale:true 監査 / #6 信頼度 / #7 index 同期 / #9 current-baseline.md 鮮度)に加え、
Phase 2b 意味解釈 4 検査(#5 cross-topic / #8 synthesis-stale / #10 three-way /
#11 version-resolve、うち #11 は AskUserQuestion 承認制で ## 矛盾 末尾に決着注記を追記)と、
Phase 3a/3c/3f/3g 機械判定 5 検査(#12 last-tier-a-refresh / #13 last-discover-tier-a-run /
#14 last-refresh-watchlist-run / #15 watch-fetch-failed / #16 last-discover-watchlist-run)と
Phase 5 機械判定 1 検査(#17 source-date-stale=代表鮮度日 180 日超で情報・index.md 再利用・書き込みなし)を実行する(計 17 検査・詳細は references/lint-rules.md)。
書き込みは wiki/log.md への結果サマリ追記+ #11 承認時のみ ## 矛盾 末尾 1 行追記のみ。
本文・フロントマター・index.md への自動補完は一切しない(不変条件「黙って上書きしない」)。
lock 扱い(design.md §6 carve-out): 通常 lint(17 検査レポート+ log.md 追記まで)は lock を 取得しない(log.md は append-only で refresh と並行しても順序問題のみ・実害なし)。#11 承認制決着の ## 矛盾 編集部分のみステップ 0.6 の lock を取得し書き込み完了後に解放する。
書き込み副作用境界(17 検査 × 書き込み有無 × lock)
| # | id | 書き込み | lock |
|---|---|---|---|
| 1 | orphan | なし(対話レポート+log.md 集計のみ) | 不要 |
| 2 | updated | なし | 不要 |
| 3 | version | なし | 不要 |
| 4 | stale | なし | 不要 |
| 5 | cross-topic | なし | 不要 |
| 6 | confidence | なし | 不要 |
| 7 | index | なし | 不要 |
| 8 | synthesis-stale | なし(detail に手動再生成コマンドを併記) | 不要 |
| 9 | baseline | なし | 不要 |
| 10 | three-way | なし | 不要 |
| 11 | version-resolve | 承認時のみ 該当ページ ## 矛盾 末尾に 1 行追記+ log.md に決着行追記 |
取得(決着適用部分のみ) |
| 12 | last-tier-a-refresh | なし | 不要 |
| 13 | last-discover-tier-a-run | なし | 不要 |
| 14 | last-refresh-watchlist-run | なし | 不要 |
| 15 | watch-fetch-failed | なし | 不要 |
| 16 | last-discover-watchlist-run | なし | 不要 |
| 17 | source-date-stale | なし | 不要 |
検査項目の判定ロジック・しきい値・走査戦略・## 矛盾 パース仕様・決着注記の正規記法は
references/lint-rules.md を参照(SKILL.md では再記述しない)。
-
引数パース
- 第 2 トークン以降から
--check=<csv>を抽出。<csv>はorphan|updated|version|stale|confidence|index|baseline|cross-topic|synthesis-stale|three-way|version-resolve|last-tier-a-refresh|last-discover-tier-a-run|last-refresh-watchlist-run|watch-fetch-failed|last-discover-watchlist-run|source-date-staleのサブセット(カンマ区切り)。 - 未指定なら全 17 検査。未知キーはエラーで中断し、上記 17 キー一覧を案内。
- 第 2 トークン以降から
-
ステップ 0.5 ボールト前提チェックを継承(init 以外と同じ手順)。ステップ 0.6 lock は通常 lint では取得しない(design.md §6 carve-out)。
2.5. migration_pending 提案(書き込み前・通常 lint 検査群の手前。Phase 3d 共通 surface 経由に再定義):
Read("./wiki-vault/wiki/current-baseline.md")でフロントマターmigration_pendingを取得(後続の走査で再利用するためメモリ保持)。- 配列が空なら通常検査群(ステップ 3)に進む。非空なら以下の AskUserQuestion を起動:
question: 「Tier A docs ホスト移転を承認しますか?」options: 各migration_pendingエントリ 1 件ごとに<source_slug>: <old_url> → <new_url>ラベル、および**「適用しない」を必ず併置**(候補 1 件のときも・Phase 2b 振り返り由来)。multiSelect: true、候補 5 件超はdetected_on古い順で上位 4 件に絞り、残りは次回提示。
- 承認分の処理(Phase 3d 共通 surface 経由・旧仕様「URL 書き換え単独」は廃止):
- (i) 承認された各エントリの
new_urlをモード B(ingest)の共通 surface に渡して新規 raw を ingest する。モード B step 3.5 の同一 source 判定 (ii) がmigration_pending[].new_url一致を検出し、旧 source ページに統合(新 raw=source_url=new_url を sources: 末尾 append することで F-3 走査が以降 new_url を最新 URL として解決する=source ページ自身にsource_urlフィールドは無いため書き換えは不要、再コンパイル、updated進行、tier 不可触、title 等の手動編集領域は不可触)+当該 migration_pending エントリ削除(モード B step 6 内・anchor 戦略はモード B 内に記載)+ 1 commit が成立する。 - (ii) 当該 raw の
source_urlは変更しない(raw 不変条件 E1)。旧 raw(old_url 由来)は不変保持され、新 raw のみ追加される。 - (iii) モード B が
.llm-wiki.lockの取得・解放を自身で行うため、本サブステップで lock 取得は不要。モード B が 1 ソース 1 commit をchore: migrate source_url <slug> <old>→<new>相当のメッセージで作成する(共通 surface (ii) ケース判定の commit メッセージはモード B 内で決まる)。
- (i) 承認された各エントリの
- 0 件選択は何もせず通常検査群へ進む。
-
走査(
references/lint-rules.md§走査戦略の通り)Glob("wiki/{sources,concepts,entities,comparisons,syntheses,practices,features}/*.md")で全ページ列挙(1 回)。- 各ページを
Read(offset=0, limit=50)でフロントマター部分のみ取得(本文不読)。 集約マップにsources/linksを含める(#8 用)。 Read("wiki/index.md")(1 回・サマリ行と [[wikilink]] を抽出)。Read("wiki/current-baseline.md")(1 回)。- Phase 2b 追加 Read(
--checkで対象検査が含まれる場合のみ):- #10 用:
Read("CLAUDE.md")/Read(".claude/skills/llm-wiki/SKILL.md")/Read(".claude/skills/llm-wiki/references/schema.md")を 1 回ずつ(CWD 起点)。 - #11 用:
Bash("grep -l '^## 矛盾' wiki/{sources,concepts,entities,comparisons,syntheses,practices,features}/*.md")で## 矛盾保持ページを抽出 → ヒットページのみ該当セクションを限定 Read (Read(offset=<セクション開始行>, limit=80)程度)。全ページ本文 Read はしない。 - #12 用: 既存の
current-baseline.mdRead を再利用する(追加 Read 0 回)。 - #13 用: 同じく既存の
current-baseline.mdRead を再利用する(追加 Read 0 回・last_discover_tier_a_runフィールドを参照)。 - #14 用: 同じく既存の
current-baseline.mdRead を再利用する(追加 Read 0 回・last_refresh_watchlist_runフィールドを参照)。 - #15 用: Phase 2a step 2 の全ページ frontmatter 集約マップ(
fetch_statusフィールド)を再利用する(追加 Read 0 回)。 - #16 用: 同じく既存の
current-baseline.mdRead を再利用する(追加 Read 0 回・last_discover_watchlist_runフィールドを参照)。
- #10 用:
- 以上のデータをメモリ上の集約マップに保持し、以降は追加 Read を行わない。
-
検査実行
references/lint-rules.mdの判定ロジック節(Phase 2a 7 検査 + Phase 2b 4 検査 + Phase 3a #12 + Phase 3c #13 + Phase 3f #14/#15 + Phase 3g #16)に従い、--checkで指定された検査を実行。severity は「要対応 / 警告 / 情報」の 3 段。- #5 / #8 / #10 / #12 / #13 / #14 / #15 / #16 はレポートのみ。
- #11 は候補検出後にステップ 8 の承認制 UX を起動(書き込みは承認後のみ)。
-
対話出力
- Markdown 表(カラム: file / check / severity / detail)で全 17 検査の結果を統合出力。
-
wiki/log.md追記### lint 結果(YYYY-MM-DD HH:MM)セクションを追記。- severity 別集計(要対応 N / 警告 M / 情報 K)と検査別件数を 1 行ずつ記録:
検査別件数: orphan=A, updated=B, version=C, stale=D, confidence=E, index=F, baseline=G, cross-topic=H, synthesis-stale=I, three-way=J, version-resolve=K, last-tier-a-refresh=L, last-discover-tier-a-run=M, last-refresh-watchlist-run=N, watch-fetch-failed=O, last-discover-watchlist-run=P - 全件詳細は対話のみ(log.md 肥大化防止)。
-
レポートコミット
- ボールト Git で
wiki/log.mdの差分のみをgit addしchore: llm-wiki lint (YYYY-MM-DD)でコミット。 - lint は他のファイルを変更しないため、log.md 以外の差分は発生しない。 もし発生していたら不変条件違反としてユーザーに報告し中断(自動書き込みの取り残し検知)。
- ボールト Git で
-
#11 承認制 UX(候補が 1 件以上ある場合のみ)
ステップ 5–7 の検査全件レポート出力+レポートコミット後に起動する。
overview 更新なし(Phase 3d): 本サブステップは
## 矛盾末尾 1 行追記+ log.md 決着行追記のみで、wiki/overview.mdは不可触(source ページ統計に変化なしのため。references/schema.md§8.4 更新タイミング表参照)。書き込みモードであっても overview 自動更新は走らない。- 候補の選別: #11 の時系列 supersession 候補をリスト化する。5 件超なら上位 4 件のみ提示 (AskUserQuestion の選択肢上限 4 件)。ソート順: severity 高い順(要対応 > 情報)→ version 差大きい順 (major 桁差 > minor 桁差)。残りは対話レポートに「次回 lint で再検出可」と明示。
- AskUserQuestion を起動:
question: 「次の N 件に決着注記を追記しますか?(適用したいページを選択。何も選ばなければ追記なしで終了)」options: 候補ページごとに 1 件、ラベル形式は<slug> (v_old=X Tier B → v_new=Y Tier A)multiSelect: true- 「いずれも適用しない」専用オプションは置かない(0 件選択がそのまま「適用しない」を意味する)
- 選択 0 件 → 追記なし・コミットなしで終了。候補は対話レポートに残る。
- 選択ページごとの末尾アンカー手順:
a. 該当ページの
## 矛盾セクション末尾を Read で確認(既に限定 Read 済みデータを使用可)。 b. 既に決着行(references/lint-rules.md§「#11 決着注記の正規記法」の二重追記回避規約)が セクション内にあればスキップ。 c. セクション末尾の最終非空行を取得し、Edit でold_string= 最終非空行(そのまま)new_string= 最終非空行 +\n+ 決着行(記法はreferences/lint-rules.md§「#11 決着注記の正規記法」の正規記法に従う。SKILL.md では再記述しない)replace_all: false厳守 d.wiki/log.mdのサマリ直下に決着行を 1 行追記する(記法はreferences/lint-rules.md§「#11 決着適用時の log.md 追記フォーマット」を参照)。
- 決着適用コミット: 追記が 1 件以上発生したらボールト Git で
chore: llm-wiki lint resolve (YYYY-MM-DD)でコミット(## 矛盾編集と log.md 追記をまとめる)。 レポートコミット(ステップ 7)とは分離する。
エラーハンドリング(lint)
references/lint-rules.md §エラーハンドリング表(Phase 2a/2b 共通+ Phase 2b 追加)を参照
(再記述しない)。要点:
| 事象 | 扱い |
|---|---|
--check 未知キー |
中断し 17 キー一覧を案内 |
| フロントマター YAML パース失敗 | 該当ページのみ「要対応: フロントマター不正」表示し他検査継続 |
sources: 空 |
「要対応: sources 空(schema.md §2 違反)」表示 |
current-baseline.md 不在 |
#3/#9/#12 をスキップ |
| ボールト未コミット変更あり | AskUserQuestion で続行可否 |
## 矛盾 セクション構造不正(grep ヒットだが Read で該当行なし) |
「要対応: ## 矛盾 セクション構造不正」表示、他検査継続 |
| #10 で本リポジトリ側 3 文書のいずれかが Read 不能 | #10 をスキップし要対応表示 |
| #11 候補ページが Read できない | 該当候補のみスキップ、他候補は続行 |
| AskUserQuestion で 0 件選択 | 追記なし・コミットなしで終了 |
| #11 候補が 5 件超 | 上位 4 件のみ承認確認、残りは次回 lint で再検出 |
## 矛盾 末尾に既に「決着(」行がある |
二重追記を回避してスキップ |
#8 で synthesis の sources: が空 |
Phase 2a sources: 空エラー扱いに委ね、#8 はスキップ |
#12 last_tier_a_refresh 未設定 / parse 不能 |
要対応として個別レポート、他検査継続(詳細は references/lint-rules.md §Phase 3a 追加) |
migration_pending が YAML として parse 不能 |
「要対応: current-baseline.md の migration_pending 破損」を表示し、本フローはスキップして通常検査群へ進む |
モード F: /llm-wiki refresh-tier-a [--dry-run]
Tier A(公式 docs / 公式 GitHub)ソースの日次自動再取得を行う非対話モード。launchd / cron から
claude --print '/llm-wiki refresh-tier-a' --allowedTools=Read,Write,Edit,Bash,Grep,Glob,WebFetch 形式で起動される。AskUserQuestion は一切起動しない(対話シェルから force-run でも同じコードパス)。
lock: ステップ F-1 で取得、ステップ F-7 で解放。
F-1. lock 取得
ステップ 0.6 の atomic 取得手順に従い mode: refresh-tier-a で取得。失敗時は wiki/log.md に
refresh-tier-a: locked, skipped (YYYY-MM-DD) を 1 行追記し exit 0。スタール判定で奪取した場合は
refresh-tier-a: stale lock recovered (pid=X, started=Y) を追記してから F-2 へ進む。
F-2. vault dirty-state チェック
Bash("git -C ./wiki-vault status --porcelain -- ':!wiki/log.md'") の出力が非空なら、wiki/log.md に
refresh-tier-a: vault dirty, skipped (YYYY-MM-DD) を追記、log.md だけの 1 commit chore: log refresh-tier-a skipped (dirty) を作成し、lock を解放して終了(stash / 自動 commit 禁止)。
Phase 3d(F-3)修正: 判定式は git pathspec :!wiki/log.md で wiki/log.md のみを除外する。log.md は schema.md §3「log.md dirty 状態 append 規約」で agent 完全所有・追記のみと規定されており、dirty 状態でも append + commit 可。これにより skip 時の log append commit が成功し、dirty escalation ループ(log.md 追記が次回 refresh で dirty 検知して skip → さらに append → …)が解消される。grep -v 'wiki/log.md' ベース実装は wiki/log.md.bak 等の false negative を生むため pathspec exclusion を採用する。
F-3 と F-4 の独立論点訂正(2026-05-24 brainstorm の旧フレーミングを 2026-05-29 brainstorm で訂正): F-3 は cron dirty escalation の特殊事象(本 F-2 で単独解消)、F-4 は通常 ingest と同じ append 規約(モード B 共通 surface で扱う)。両者は独立論点であり「同じ log.md append 規約として束ねる」旧フレーミングは廃止。
F-3. 対象 Tier A ソース集合の決定
references/schema.md §2.1 / §3 の規約に従って次の手順で機械的に決める(シードリスト/別途のクロール設定は持たない):
Glob('./wiki-vault/wiki/sources/*.md')でページ列挙、各ページをRead(offset=0, limit=50)でフロントマター取得。- フロントマター
tier: Aのページのみ候補。 - 各候補ページの
sources:末尾(ファイル名プレフィックスYYYY-MM-DDが最大のもの)を最新 raw として解決。 - 当該 raw のフロントマター
source_urlを取得。source_urlが無い/tier: Aでない raw は skip+log(refresh-tier-a: no source_url <slug>)。 - 結果は
(source_slug, latest_raw_path, source_url)の三つ組リスト(source_slugの ASCII 昇順で決定論的に並べる)。 - 孤立 raw(対応する source ページが無い)は対象外。lint #1 と責務分離。
F-4. per-source ループ
各三つ組について以下を順に実行(失敗ソースは skip+log し commit せず次へ・git push しない):
F-4a. 取得(経路 routing):
source_urlがhttps://github.com/{owner}/{repo}/blob/{ref}/{path}パターンにマッチする場合は gh api 経路 を使う(WebFetch では JS レンダリング前の HTML シェルしか返らず本文取得不能なため):Bash('gh api "repos/{owner}/{repo}/contents/{path}?ref={ref}" --jq .content | base64 -d')を実行。{ref}はブランチ名/タグ名/sha のいずれでもよい。- 取得経路フラグ
fetch_method = gh-apiを立て、F-4c のフロントマター生成に伝搬する。 - 失敗時はそのソースを skip し
wiki/log.mdにfail <slug>: <error>行を追記、次のソースへ(gh CLI 未インストール/gh auth切れ/404 などはエラーハンドリング表参照)。
- それ以外の URL は WebFetch 経路:
WebFetch(source_url, "<取得テキスト全文を要約せず可能な限り原文に近い形で抽出>")を試行。- 取得経路フラグ
fetch_method = webfetchを立てる。 - 失敗時はそのソースを skip し
wiki/log.mdにfail <slug>: <error>行を追記、次のソースへ。
F-4b. 301 リダイレクト検出(WebFetch 経路のみ発火・gh api 経路では skip):
- 取得結果のメタが redirect を示す or status が 301 の場合:
current-baseline.mdフロントマターmigration_pendingを Read。- 当該
source_slugのエントリが既に存在すれば skip+ log(suppressed: pending migration <slug>)。再 append しない。 - 存在しなければ
migration_pendingに 1 エントリ append:{old_url: source_url, new_url: <リダイレクト先>, detected_on: <今日>, source_slug: <slug>}。 current-baseline.mdを 1 commitrefresh(tier-a): migration_pending append <slug>。- 当該ソースの本文再コンパイルは行わず次のソースへ(古い URL のままで運用継続)。
F-4c. 通常取得成功時の raw 追加保存:
- 保存先:
raw/<種別>/<取得日 YYYY-MM-DD>-<slug>.md(種別は raw の既存配置に揃える。docs / articles / github 等)。 - 既存ファイル上書き禁止(E1 不変条件)。同日 2 回目以降は末尾に
-2,-3, ... の連番を付与:Bash('ls ./wiki-vault/raw/<種別>/<取得日>-<slug>*.md 2>/dev/null | wc -l')で件数を取得し、N>0なら<slug>-(N+1)を使う。
- フロントマター:
source_url/fetched_at/tier: Aを必ず記録。fetched_viaとnoteは F-4a の取得経路で分岐:fetch_method = webfetchの場合:fetched_via: WebFetch/note: WebFetch 要約(逐語性ポリシーは memory/webfetch-raw-snapshot-policy.md 参照)fetch_method = gh-apiの場合:fetched_via: "gh api repos/{owner}/{repo}/contents/{path}?ref={ref} (verbatim / 逐語コピー)"/note: "原文逐語スナップショット(gh api 経由で base64 デコード)。要約ではない。再検証は source_url を参照。"
- 日付メタ(Phase 5・経路別・ベストエフォート): frontmatter に
published_at/last_modified/published_at_source/last_modified_sourceを記録(references/schema.md§3)。fetch_method = gh-api:Bash('gh api "repos/{owner}/{repo}/commits?path={path}&sha={ref}&per_page=1" --jq ".[0].commit.committer.date"')の ISO8601 をYYYY-MM-DDに切り詰めlast_modified=gh-commit。published_atはunknown。fetch_method = webfetch: 構造化メタarticle:modified_time/article:published_time・JSON-LDdateModified/datePublishedを抽出しhtml-meta、取れなければ本文可視日付の保守パース →html-body(schema §3 規約)。Tier A docs(code.claude.com等)の.md逐語はメタも publication-context 本文日付も持たないことが多く、その場合はunknown(偽日付を入れない・dry-run 実証)。- 取れない/parse 不能は
unknown。失敗は例外にせず logdate extraction: unknown (<raw path>, <route>)。
F-4d. 差分判定:
- 新 raw の
fetched_at> 該当wiki/sources/<slug>.mdのupdatedなら F-4e 再コンパイルへ進む。 - 等しい/古い場合は raw 追加のみで wiki 更新スキップ。
wiki/log.mdにunchanged <slug>を追記。
F-4e. 再コンパイル:
- 既存
ingest(モード B)と同じ「同一トピック [[wikilink]] 先のみ照合・矛盾は## 矛盾追記」の経路を踏襲。横断矛盾は lint #5 委譲。 wiki/sources/<slug>.mdのフロントマターclaude_code_version/updatedを新 raw の値で更新。- sources: 末尾 append(F-6・時系列保証): 新 raw のパスを
wiki/sources/<slug>.mdのフロントマターsources:末尾に append(モード B step 6 共通 surface 追記事項と同じ仕様)。既存 raw は不変保持で削除・並べ替えしない。 - 本文は最小改変(要約差分の反映のみ)。
confidenceは維持(既存値を尊重)。 - 代表鮮度日の更新(Phase 5・b4): 新 raw の日付メタで当該ページの代表鮮度日(
sources:全 raw の鮮度日の最大値〔unknown除外・references/schema.md§3 定義〕)が進む場合、wiki/index.mdの当該行の(鮮度: YYYY-MM-DD)を更新(モード B step 9 と同仕様・per-source commit に同梱)。
F-4f. current-baseline.md の baseline フィールド更新:
- 当該ページに
claude_code_version更新が含まれていればcurrent-baseline.mdのclaude_code_version/updatedを更新。 schema_version/schema_repo_commit/schema_summaryは不可触(schema 軽量ポインタは co-evolution 経路のみ)。
F-4g. overview 自動更新(Phase 3d・C):
- モード B step 8.5 と同じ仕様(
references/schema.md§8)。 - 統計 5 件 + 2 日付を計算し
wiki/overview.mdの## 現状セクションを Read。全値同値なら Edit を skip し log.md にoverview unchanged (<日付>)を 1 行追記。1 つでも差分があれば Edit。 - 境界判定失敗時は skip + log warning。
--dry-runでは Edit / commit せずwould-update overview <fields>をレポート。
F-4h. per-source commit:
- ボールト側 Git で当該ソース関連の差分(raw 追加・wiki/sources/.md 更新・overview 更新があれば含む)のみを
git addし、refresh(tier-a): <slug> at <取得日>でコミット。 git push しない。
F-5. 全ソース処理後の last_tier_a_refresh 更新
current-baseline.mdフロントマターの現在のlast_tier_a_refreshを Read。- 値が本日付と等しい場合(同日 2 回目以降の force-run): Edit / commit をスキップし、
wiki/log.mdにlast_tier_a_refresh unchanged (<date>)を 1 行追記して F-6 へ進む(空 commit ガード)。 - 値が本日付と異なる場合: フロントマターを本日付に Edit。さらに overview 自動更新を inline で実行(モード B step 8.5 と同じ仕様・値変化ガード付き)。両編集を 1 commit
refresh(tier-a): last_tier_a_refresh = <date>に同梱する(F-4g の per-source commit が既に overview 値を最新化している可能性があるため、F-5 では値変化ガードで skip されるのが典型ケース)。 --dry-runモードではいずれの分岐も commit を行わず、would-update last_tier_a_refresh <old> -> <today>またはlast_tier_a_refresh unchanged (<date>)を標準出力にレポートするのみ(overview の差分予測も含める)。
F-6. サマリ追記
wiki/log.mdに 1 行refresh-tier-a: ok=N skip=M fail=K (YYYY-MM-DD)を追記。- 1 commit
chore: log refresh-tier-a summary。
F-7. lock 解放
ステップ 0.6 の解放手順に従う。例外時も trap 相当で削除を試みる。
--dry-run モード
- F-1(lock 取得)/ F-2(dirty-state)/ F-3(対象集合決定)を実行。
- F-4 ループは per-source の判定結果(
unchanged/would-update/would-append-migration-pending/fail 予測)を標準出力にレポートするのみ。 - raw 追加・wiki 更新・current-baseline 更新・git commit を一切行わない。
- F-5 / F-6 の commit も行わない。
- F-7 lock 解放のみ実行(dry-run でも並行実行禁止)。
モード F のエラーハンドリング
| 事象 | 扱い |
|---|---|
.llm-wiki.lock を他プロセスが取得済み(生存中) |
F-1 で skip log を追記して終了 |
.llm-wiki.lock がスタール(1h 経過+kill -0 fail) |
強制奪取し stale lock recovered ログ追記して通常実行 |
| vault dirty | F-2 で vault dirty, skipped ログ追記して終了(lock 解放) |
| WebFetch 失敗(個別ソース) | F-4a で当該ソース skip、log に fail <slug>: <error> 行 |
gh: command not found(gh CLI 未インストール) |
F-4a gh api 経路で当該ソース skip、log に fail <slug>: gh CLI not installed 行。launchd EnvironmentVariables の PATH に gh を含めるよう案内 |
gh auth status 失敗(認証切れ/token 失効) |
F-4a gh api 経路で当該ソース skip、log に fail <slug>: gh auth required 行。次回 refresh 前に対話セッションで gh auth login |
| gh api が 404(ref/path 不在・リポジトリ rename) | F-4a gh api 経路で当該ソース skip、log に fail <slug>: gh api 404 <ref>/<path> 行。連続 fail の場合は手動で source ページの sources: 末尾の source_url を新パスへ更新 |
| 301 リダイレクト(WebFetch 経路のみ) | F-4b で migration_pending append(既出はサプレッション)、当該ソース再コンパイルなし。gh api 経路では F-4b 自体が発火しない |
| 同日 raw 衝突 | F-4c で -2, -3, ... 連番付与(既存上書き禁止) |
last_tier_a_refresh 既に本日付(同日 2 回目以降の force-run) |
F-5 で Edit / commit を skip し log に last_tier_a_refresh unchanged (<date>) を 1 行追記して F-6 へ(空 commit ガード) |
| 再コンパイル中の wikilink 解決例外 | 当該ソース skip し log に recompile fail <slug> 行 |
current-baseline.md Read 失敗(破損) |
全実行中止し log に baseline unreadable, aborted 追記、lock 解放 |
migration_pending YAML 破損 |
current-baseline.md Read 失敗扱いに準じる |
対象規模・recurring cost
想定対象は Tier A 主要 docs +公式 GitHub リリースで一桁〜十数件。日次 WebFetch 10〜15 リクエスト/日。 Phase 3a では per-source の weekly/daily 切替は実装しない。
モード G: /llm-wiki discover-tier-a [--no-prompt|--dry-run]
Tier A 公式 docs / 公式 GitHub の未取り込み URL を自動発見し、current-baseline.md の pending_discoveries[] に登録、承認制で共通 surface(モード B)経由 ingest する。手動 ingest の初期登録コストを下げるのが目的(Phase 3c)。
discovery scope = α 厳格(CLI 中心):
- docs:
https://code.claude.com/docs/en/*(英語のみ・agent-sdk/*含む。翻訳版 11 言語は除外) - GitHub:
anthropics/claude-coderepo ルートのCHANGELOG.md+README.md(repo にdocs/は存在しない=公式 docs はcode.claude.comに移管済。plugins/**/examples/**は本 phase 対象外) - β/γ scope(
platform.claude.com/docs/en/api/*1422 URL・Agent SDK 別 repo 等)は本 phase 対象外。手動ingest経路で対応。
discovery scope ≠ refresh scope(Y 案): discover-tier-a は発見 + 候補リスト化 + 承認制 ingest のみ。refresh-tier-a(モード F)の §F-3 対象集合は不可触。発見 URL は承認 → モード B ingest → wiki/sources/<slug>.md 作成 → §F-3 の対象集合決定で初めて refresh 対象になる。この順序で Phase 3a の cost assumption(日次 10〜15 req)は崩れない。
実行モード:
| モード | フラグ | 動作 | 想定起動元 |
|---|---|---|---|
| 既定(対話) | なし | discovery → append → AskUserQuestion 承認 → 共通 surface ingest | 対話シェル |
| 非対話 | --no-prompt |
discovery → append のみ。AskUserQuestion 不発火・ingest なし | launchd / cron |
| dry-run | --dry-run |
discovery レポートのみ。pending_discoveries / last_discover_tier_a_run 更新も skip・副作用ゼロ | 対話シェル(preview) |
--no-prompt と --dry-run は併用可(discovery 結果のみレポート・副作用ゼロ)。
lock: ステップ G-1 で取得、ステップ G-8 で解放。ingest 中も mode G が lock を保持し続ける(G-6 はモード B の ingest 本体 step 3〜9 のみを呼び、モード B の step 0 lock 取得 / step 10 lock 解放は呼ばない=lock 再入防止)。
G-1. lock 取得
ステップ 0.6 の atomic 取得手順に従い mode: discover-tier-a で取得。失敗時は wiki/log.md に discover-tier-a: locked, skipped (YYYY-MM-DD) を 1 行追記し終了。スタール判定で奪取した場合は discover-tier-a: stale lock recovered (pid=X, started=Y) を追記してから G-2 へ。
G-2. vault dirty-state チェック
モード F の F-2 と同じ判定式 Bash("git -C ./wiki-vault status --porcelain -- ':!wiki/log.md'") を使う(log.md は agent 完全所有・追記のみのため除外)。出力が非空なら wiki/log.md に discover-tier-a: vault dirty, skipped (YYYY-MM-DD) を追記、log.md だけの 1 commit を作成し、lock を解放して終了(stash / 自動 commit 禁止)。
G-3. discovery(sitemap + gh api fetch)
- docs:
Bash("curl -sL --max-time 30 https://code.claude.com/sitemap.xml")で sitemap XML を取得し、<loc>タグを抽出してhttps://code.claude.com/docs/en/で始まる URL のみフィルタ(翻訳版除外)。WebFetch は sitemap.xml を要約してしまうため使わない(curl で逐語取得)。 - GitHub:
Bash("gh api repos/anthropics/claude-code/git/trees/main?recursive=1 --jq '.tree[] | select(.type==\"blob\") | .path'")で全 path を列挙し、repo ルートのCHANGELOG.mdとREADME.mdのみ抽出(docs/は repo に存在しないため対象外・plugins/**/examples/**/.claude/**は除外)。URL 化規約はhttps://github.com/anthropics/claude-code/blob/main/{path}(main決め打ち)。 - いずれかの経路が失敗しても他経路は続行(gh api 失敗でも docs 側は続行・エラーハンドリング表参照)。
- キャッシュは持たない(起動の度に毎回 fetch・α scope は docs 142 + GitHub 2 件で recurring cost は許容範囲)。
G-4. 突合 + 正規化
- URL 正規化規約は モード B step 3.5(フル仕様・単一正本)を参照(host lowercase + fragment 除去 + 末尾スラッシュ除去 + tracking param denylist 除去 + Tier A host allowlist 正準化)。本節では再記述しない。
pending_discoveries[].url再正規化 migration(Phase 3e・idempotent): 既存pending_discoveries[].urlは旧最小正規化(host lowercase + 末尾スラッシュのみ)で格納されている可能性がある。本 step 冒頭で各urlをフル正規化し直し、変化したものがあればcurrent-baseline.mdを Edit(再正規化済み=変化なしなら no-op)。これにより以降の dedup(正規化後 url キー)が phantom duplicate を生まない。--dry-runでは Edit せずwould-renormalize <old> -> <new>をレポート。再正規化で Edit が発生した場合は、新規 append 件数 N=0 でも必ず commit する(chore: discover-tier-a: renormalize pending_discoveries (YYYY-MM-DD)・G-5 で N>0 の append commit が立つ場合はそれに同梱可)。commit しないと未コミット Edit が vault dirty を生み、次回 refresh/discover が F-2/G-2 dirty-check で skip する。なお Tier A の docs/github URL は utm/fbclid/fragment を持つことが稀のため、本 migration は通常 no-op(低頻度)。- 既存
Glob("./wiki-vault/wiki/sources/*.md")の各 source ページについて、モード F F-3 step 1〜4 と同じ走査で既存 URL を解決する: 各ページのフロントマターsources:末尾(YYYY-MM-DDプレフィックス最大)の raw を Read し、その raw のフロントマターsource_urlを取得して既存 URL 集合に入れる。source_urlを欠く raw は skip。source ページ自身のフロントマターにsource_urlは無い(schema §2 共通フィールドに含まれず、source_urlは raw のフロントマターキー=schema §3)ため、source ページから直接読まず必ず raw を辿る(旧記述「各ページsource_url(フロントマター)を収集」は誤りで、Phase 3c carve-out 実機検証で既存集合が空になり取り込み済み URL が誤って再候補化するバグを修正)。 current-baseline.md.pending_discoveries[]とcurrent-baseline.md.migration_pending[].new_urlを Read。- 発見 URL をフル正規化し、次のいずれかに含まれるものを除外 → 未取り込み候補集合。すべて正規化後に突合する:
既存 source_url(取り込み済み)migration_pending[].new_url(移転保留)pending_discoveries[]のうちdeclined: trueのエントリのurl(既知の却下=再候補化しない・Phase 3e stuck candidates 対策・§G-6 / schema §2.1 参照)
G-5. pending_discoveries append(dedup ルール)
current-baseline.md.pending_discoveries[]を Read。- 未取り込み候補のうち、既存
pending_discoveries[].url(正規化後)に含まれないもののみ append(dedup キー = 正規化後 url)。 - 既存エントリありなら append skip(
detected_onは古い方を保持=最初に発見した日付)。これにより cron で日次--no-prompt起動しても sitemap 不変なら 1 度 append すれば以後は no-op となり、リスト爆発を防ぐ。 - エントリ形式:
{url: <正規化後 URL>, source_kind: docs|github, detected_on: <今日>}(references/schema.md§2.1)。 - append 後の配列で
current-baseline.mdを Edit。 --no-promptの場合: ここまでで 1 commitchore: discover-tier-a: N new candidates (YYYY-MM-DD)に集約(per-URL commit にしない・migration_pending append 流儀と整合)。その後 G-7 へ(G-6 の ingest は skip)。--dry-runの場合: Edit / commit せずwould-append <url>を標準出力にレポート。
G-6. 共通 surface ingest(既定モードのみ・Phase 3e: capped バッチ opt-out)
--no-prompt / --dry-run では本ステップを skip。
承認モデル(Phase 3e amendment): Tier A 公式 docs は信頼ソースのため、利用者の手間を減らすべく opt-out(既定取り込み・除外を選択) とする。ただし 142 件規模の無人一気 ingest を避けるため 1 run あたり ingest 上限 N(既定 20) の機械的ペース制限を置く(人間ゲートではない)。AskUserQuestion は opt-in 仕様(選択 = 対象)のため「除外する候補を選択」と反転提示する。空 submit と dialog 中止が同一の「未回答」結果になり opt-out の『0 件選択=全件取り込み』が判別不能なため、各バッチに sentinel ✅ 全件取り込む(除外なし) を必ず併置し、全件取り込みの意思を明示できるようにする(未回答は安全側で「確定しない」扱い・下記 step 2.2/2.3)。
- 候補抽出:
pending_discoveries[]からdeclined: trueを除いたエントリをdetected_onASC → URL ASCII 昇順 fallback で並べ、先頭 min(N, 残数) 件を本 run の処理対象とする(N = per-run cap・既定 20)。 - ラウンド処理(
AskUserQuestionの選択肢上限 4 件のうち sentinel「全件取り込む(除外なし)」が毎バッチ 1 枠を占有するため、対象を 3 件ずつのバッチに分割し、最大 ⌈N/3⌉ ラウンド繰り返す)。各ラウンド:- 遅延概要取得: 当該バッチの最大 3 件のみ
WebFetch(url, "<タイトルと要旨を 1〜2 文で。要約>")で軽量に概要を取得(取得失敗時は概要なし=url のみ)。全 142 件の先取りはしない(コスト集中回避・idea.md「discover 時先取り」からの逸脱は design §5.2 に記録)。 - AskUserQuestion(
multiSelect: true)で提示:question: 「取り込みたくない候補を選択してください(選択 = 除外)。すべて取り込むなら『全件取り込む(除外なし)』を選択。何も選ばず submit / ダイアログ中止は『今回は確定しない』扱いで候補は次回 run に残ります」options: バッチの各候補(最大 3 件)ごとに<source_kind>: <概要 or url>ラベル(選択 = 除外)+ 末尾に sentinel✅ 全件取り込む(除外なし)を必ず併置(合計最大 4 選択肢)。sentinel を常設することで「除外なしで全件取り込む」意思を明示でき、AskUserQuestionが空 submit と dialog 中止を同一の「未回答」結果として返す制約(opt-out の『0 件選択=全件取り込み』が判別不能)を回避する。
- 選択の解釈(precedence):
- sentinel のみ選択 → バッチ全候補を取り込む(除外 0)。
- 個別候補が 1 件以上選択(sentinel 併選を含む)→ 選択された個別候補を除外指定として扱い、sentinel 選択は無視する(個別除外が優先)。残りの候補を取り込む。
- 未回答(空 submit / dialog 中止) → 本バッチは確定せず ingest も
declined化もしない。候補はpending_discoveries[]に残置し次回 run で再提示する。「全件取り込み」と解釈してはならない(誤 ingest 防止=書き込み副作用の安全側)。
- 除外指定された候補:
pending_discoveries[]の当該エントリにdeclined: trueを立て
- 遅延概要取得: 当該バッチの最大 3 件のみ
Truncated - read the full file at https://github.com/aidotters/claude-code-wiki-maker/blob/b854ab2d11e7a8a8ce00dbf36c11cf5b0224da14/.claude/skills/llm-wiki/SKILL.md.