wiki-dedup について
wiki-dedupは、Obsidian wikiの重複ページを検出して整理するスキルです。読み取り専用の監査、確認付きの統合、高信頼度候補の自動統合に対応し、破壊的な編集への対策も備えています。
主な機能
- 01タイトルとメタデータによる候補検出
- 02監査・対話型統合・自動統合
- 03代表ページの選定と内容の統合
- 04リダイレクトページ作成とリンク一括置換
- 05書き込み前のGitスナップショット保護
活用シーン
- 01異なる名前で同じ概念を扱うページの検出
- 02vaultを変更せず重複候補を確認
- 03リンクを維持しながら重複ページを統合
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ar9av/ obsidian-wiki --skill wiki-dedupインストール後、Claude Code で/wiki-dedupを入力して読み込まれたか確認します。
詳細
Wiki Dedup — ID 解決とページ単位の重複排除
異なる名前で同じ概念を扱っている Wiki ページを見つけて統合します。これは書き込みが多く、破壊的な操作になる可能性のあるスキルです。ページの統合は自動では元に戻せません。慎重に作業し、マージモードで実行する前に確認してください。
llm-wiki/SKILL.md の検索プリミティブ表に従ってください。 候補検出の処理では、フロントマターとタイトルのみを使用します(低コスト)。確認済みの候補ペアについてのみ、ページ本文全体を開いてください。
開始する前に
執筆プロファイル: 自然言語の Markdown を作成または書き換える前に、llm-wiki/SKILL.md の Writing Profile Resolution セクションを読み、適用してください。フレームワークのスキーマ、出典、安全性、および操作固有の要件が優先されます。
WRITING.md の設定は、新たに作成または書き換える自然言語の Markdown にのみ適用されます。原文の内容と構造化レコードは保持してください。
- 設定を解決する —
llm-wiki/SKILL.mdの Config Resolution Protocol(インラインの@nameによる上書き → CWD から上位ディレクトリをたどって.envを検索 → グローバル設定 → セットアップを促す)に従います。これによりOBSIDIAN_VAULT_PATHとOBSIDIAN_LINK_FORMATが得られます。 index.mdを読み、1 行の説明とタグが付いた全ページの一覧を取得します。log.mdに目を通します。重複排除の実行が直近にあった場合は、すでに統合されたページを確認します。
モード
| モード | フラグ | 動作 |
|---|---|---|
| 監査 | (既定) | 候補のみを報告します。書き込みは行いません |
| マージ | --merge | 確認済みのペアを一つずつ表示し、統合前に確認を求めます |
| 自動マージ | --auto | 信頼度の高いペア(score ≥ 0.90)をすべて、対話なしで統合します |
ユーザーが指定しない場合は、監査モードで実行し、続行するかどうかを尋ねる前に結果を提示してください。
ステップ 1:ページレジストリを作成する
ボルト内のすべての .md ファイルを Glob で検索します(_archives/、_raw/、.obsidian/、index.md、log.md、hot.md、_insights.md、およびフロントマターに redirects_to: を含むファイルを除外します。これらはすでに統合済みのリダイレクト用スタブです)。
残りの各ページについて、フロントマターから以下を抽出します。
node_id—.mdを除いた、ボルトルートからの相対パスtitle— フロントマターのtitleフィールドaliases— フロントマターのaliasesリスト(ない場合もあります)tags— フロントマターのtagsリストcategory— ディレクトリのプレフィックス
検索テーブルを作成します:node_id → {title, aliases, tags, category, summary}。
ステップ 2:候補ペアを検出する
レジストリ内のすべてのページペアについて、以下のシグナルを使って類似度スコアを計算します。
2a. タイトルの類似度シグナル
| シグナル | 評価方法 | 最大寄与度 |
|---|---|---|
| トークンの重複 | 小文字化したタイトルの単語トークン(スペース、ハイフン、アンダースコア、句読点で分割)の Jaccard 類似度 | 0.65 |
| 編集距離 | 小文字化したタイトルの正規化編集距離:1 - (edits / max(len_a, len_b)) | 0.40 |
| 部分文字列の包含 | 一方のタイトルが他方の部分文字列である(例:"RSC" ⊂ "React Server Components") | 0.50 |
| エイリアスの相互一致 | ページ A のタイトルがページ B の aliases に含まれる、またはその逆 | 0.65 |
タイトルの複合スコア = min(max(token_overlap, edit_distance, substring), 0.65) + alias_cross_bonus。
厳密な計算は不要です。類似度の高さを確信を持って判断してください。
タイトル抽出に関する注意: YAML のブロックスカラー(title: >- や title: |)を使っているページもあります。title: の値が >-、>、|、または |- の場合、実際のタイトルは次のインデントされた行にあります。タイトルとして文字列 >- を比較してはいけません。
2b. 意味的シグナル(低コストの処理)
| シグナル | ポイント |
|---|---|
同じ category ディレクトリ | +0.10 |
| タグの重複が 3 個以上 | +0.15 |
| タグの重複が 2 個以上 | +0.05 |
| 同じ最初のタグ(主要タグ) | +0.05 |
2c. 閾値
複合スコアが 0.75 以上のペアに候補のフラグを付けます。0.90 以上のペアは高信頼度です。
スコア範囲 → 信頼度ラベル:
| スコア | ラベル |
|---|---|
| ≥ 0.90 | HIGH — ほぼ確実に同じ概念です |
| 0.75–0.89 | MEDIUM — 同じ可能性が高いため、確認してください |
| 0.60–0.74 | LOW — 略称または特殊化の可能性があります。ユーザーから依頼がない限りスキップしてください |
HIGH と MEDIUM の候補のみをステップ 3 に進めます。
2d. 早期終了ルール
Vault のページ数が 10 未満の場合、ペアのループをスキップし、「意味のある重複を見つけるには Vault が小さすぎます」と報告します。ページ数が 500 を超える場合、候補を 50 ペアずつ処理し、バッチごとに一時停止して進捗を報告します。
ステップ 3:意味の判定
候補ペアごとに(スコアの降順で):
- 両方のページを全文読みます(候補の数は少ないため、ページ全体の読み取りが妥当です)。
- これらのページが同じ概念を扱っているのか、それとも別個の概念なのかを判断します。
次の 3 つの判定のいずれかを付けます:
| 判定 | 意味 |
|---|---|
merge | 同じ概念です。名称、略称、別名が異なるか、誤って重複しています。安全に統合できます。 |
keep-separate | 関連はありますが別個です。たとえば「Server Actions」と「Server Components」は関連する React の機能ですが、重複ではありません。 |
needs-review | 曖昧です。大きな重複がある一方で、意味のある違いもあります。ユーザーが判断できるようにフラグを付けます。 |
各判定に短い理由を添えます(1 文)。これはレポートとログに表示されます。
ステップ 4:監査レポート
マージ/自動マージモードでも、必ずこのレポートを作成します(ユーザーが実行内容を確認できるようにするためです):
## Wiki Dedup Report
### High-Confidence Candidates (score ≥ 0.90): N pairs
| Score | Page A | Page B | Verdict | Reason |
|---|---|---|---|---|
| 0.95 | `concepts/rsc.md` | `concepts/react-server-components.md` | merge | "RSC" is the abbreviation; both pages cover identical material |
| 0.91 | `entities/vaswani-2017.md` | `references/attention-is-all-you-need.md` | keep-separate | One is a person stub, one is a paper reference |
### Medium-Confidence Candidates (score 0.75–0.89): N pairs
| Score | Page A | Page B | Verdict | Reason |
|---|---|---|---|---|
| 0.82 | `concepts/fine-tuning.md` | `concepts/finetuning.md` | merge | Same concept, hyphenation variant |
### Needs Human Review: N pairs
| Score | Page A | Page B | Reason |
|---|---|---|---|
| 0.78 | `concepts/agents.md` | `concepts/autonomous-agents.md` | Substantial overlap but "agents" may intentionally be broader |
### Summary
- Pages scanned: N
- Candidate pairs found: M
- Recommended merges: X
- Keep separate: Y
- Needs review: Z
監査モードでは、ここで停止し、「--merge を実行して推奨されたペアを対話形式でマージするか、--auto を実行して高信頼度のペアをすべて自動的にマージしますか?」と尋ねます。
ステップ 5:マージ
書き込み前のスナップショット — 最初のファイル書き込みの前に、Vault 自体が Git リポジトリのルートかどうかを確認します。より大きなリポジトリのサブディレクトリであるだけでは条件を満たしません。そこで git add -A を実行すると、無関係なファイルまで取り込む可能性があるためです。Vault が独立した Git リポジトリでない場合は、この手順を黙ってスキップします。催促したり、git init を提案したりしてはいけません。
VAULT_REAL_PATH=$(cd "$OBSIDIAN_VAULT_PATH" && pwd -P)
VAULT_GIT_ROOT=$(git -C "$OBSIDIAN_VAULT_PATH" rev-parse --show-toplevel 2>/dev/null || true)
SNAPSHOT_SHA=""
if [ -n "$VAULT_GIT_ROOT" ] && [ "$VAULT_GIT_ROOT" = "$VAULT_REAL_PATH" ]; then
if git -C "$OBSIDIAN_VAULT_PATH" diff --quiet \
&& git -C "$OBSIDIAN_VAULT_PATH" diff --cached --quiet \
&& [ -z "$(git -C "$OBSIDIAN_VAULT_PATH" ls-files --others --exclude-standard)" ]; then
SNAPSHOT_SHA=$(git -C "$OBSIDIAN_VAULT_PATH" rev-parse HEAD)
else
if ! git -C "$OBSIDIAN_VAULT_PATH" add -A; then
echo "Pre-write snapshot failed; abort the skill without writing any vault files." >&2
exit 1
fi
if ! git -C "$OBSIDIAN_VAULT_PATH" commit -m "pre-wiki-dedup snapshot" --quiet; then
echo "Pre-write snapshot failed; abort the skill without writing any vault files." >&2
exit 1
fi
SNAPSHOT_SHA=$(git -C "$OBSIDIAN_VAULT_PATH" rev-parse HEAD)
fi
fi
クリーンなリポジトリの分岐では、意図的に git commit を呼び出さないため、「コミットするものがありません」はエラーとして扱われません。git add または git commit が失敗した場合は、Vault を編集する前に停止します。約束したスナップショットを作成せずに処理を続けてはいけません。
SNAPSHOT_SHA が空でなく、スキルがファイルを書き込む場合は、最終レポートに SHA を含めます。実行全体を破棄するには、後から加えられた変更のうち残す価値のあるものがないことを確認してから、ユーザーは次を実行できます:
git -C "$OBSIDIAN_VAULT_PATH" reset --hard "$SNAPSHOT_SHA"
git -C "$OBSIDIAN_VAULT_PATH" clean -fd
merge 判定のペアごとに(マージモードまたは自動マージモードの場合):
マージモードでは、ペアと判定を表示してから、「[Page A] を [Page B] にマージしますか?(yes/skip/review)」と尋ねます。yes 以外の場合はスキップします。
自動マージモードでは、高信頼度(score ≥ 0.90)のマージのみを、確認を求めずに処理します。
5a:正規ページの選択
次の基準を上から順に適用し、優先するページを決定します:
- 被リンク数が多い — Vault 内で
[[node_id]]の参照を grep し、件数の多い方を選びます - 内容が充実している — ページ本文が長い(行数が多い)方を選びます
- ソース数が多い —
sources:リストの項目数が多い方を選びます - タイトルが長い — より長く、具体的なタイトルを選びます(例:「React Server Components」は「RSC」より優先されます)
- アルファベット順 — タイトルが先の方を選びます
正規ページが残すページになります。もう一方のページは二次ページとなり、内容を統合した後、リダイレクト用のスタブに置き換えます。
5b: コンテンツを正規ページに統合する
両方のページを読みます。正規ページを更新します。
aliases:— 副ページのタイトルとすべてのエイリアスを追加します(重複は追加しません)。tags:— 両方のタグリストを統合します(重複を除き、ドメインタグは最大5つ、システムタグは別枠とします)。sources:— 両方のソースリストを統合します(重複を除きます)。relationships:— 両方の関係リストを統合します(ターゲットごとに重複を除き、型付きの項目を型なしの項目より優先します)。base_confidence— ソースの和集合とllm-wiki/SKILL.mdの計算式を使って再計算します。updated— 現在の日時に設定します。summary:— 副ページによって新しい内容が加わった場合、統合後の範囲を網羅するように書き換えます。- 本文 — 副ページの固有のセクションと箇条書きを統合します。単純に末尾へ追加せず、内容を組み込みます。正規ページにすでにある主張との重複を避けます。統合による推論が必要な場合は
^[inferred]マーカーを使用します。 provenance:— 統合後に再計算します。
5c: 副ページのパスにリダイレクトスタブを書く
---
title: <secondary page title>
redirects_to: "[[<canonical node_id>]]"
aliases: [<secondary aliases>]
category: <secondary category>
tags: []
created: <secondary original created>
updated: <obsidian-wiki memory now>
---
This page has been merged into [[<canonical page title>]].
redirects_to: フィールドは、このページを読むスキルに、コンテンツとして扱わずリダイレクト先をたどるよう指示します。
5d: Vault 全体の Wiki リンクを書き換える
Vault 全体を grep して、副ページのスラッグを指すすべてのリンクを探します。
[[secondary-slug]]→[[canonical-slug]][[secondary-slug|display text]]→[[canonical-slug|display text]]OBSIDIAN_LINK_FORMAT=markdownの場合:[text](../path/to/secondary.md)→[text](../path/to/canonical.md)
安全ルール:
- コードブロック(``` フェンスまたは
inline code)内は絶対に書き換えないでください。 - リダイレクトスタブ自体は絶対に書き換えないでください(旧スラッグを判読可能な形で残す場所はここだけです)。
rmや破壊的なシェル操作は絶対に使用しないでください。Edit/Write ツールのみを使用します。- ファイルは一度に1つずつ書き換え、次に進む前にそれぞれを確認します。
- 該当箇所がないファイルはスキップします。
5e: トラッキングファイルを更新する
index.md と hot.md は、ステップ6の memory sync 呼び出しで再生成されます。ページウォーカーはリダイレクトスタブをスキップするため、副ページの項目は消え、正規ページの項目には統合後の概要が反映されます。
.manifest.json — 副ページのソース項目それぞれに "merged_into": "<canonical node_id>" を追加します。正規ページには、副ページの pages_created と pages_updated のリストを統合します。
5f: 最終確認
すべての統合が終わったら、Vault を grep して、残っている [[secondary-slug]] 参照がないか確認します(スタブ以外のファイルが対象です)。残っている場合は報告します。書き換え手順で標準外のリンク形式を見落とした可能性があります。
ステップ6:ログを記録する
1回のロック付き呼び出しで、ログ行の書き込みとインデックスおよびホットキャッシュの整合を行います。
obsidian-wiki memory sync DEDUP \
mode=<audit|merge|auto-merge> pages_scanned=<N> pairs_found=<M> \
merged=<X> kept_separate=<Y> needs_review=<Z> \
wikilinks_rewritten=<W> \
--takeaways "Merged N duplicate pairs; canonical pages updated."
監査モード(統合なし)では、代わりに obsidian-wiki memory log DEDUP ... を使用します。読み取り専用の実行では、インデックスやホットキャッシュを書き換えてはいけません。
並行して書き込む処理が更新を失わないよう、コマンドがロックを取得します。index.md、log.md、hot.md を手動で編集してはいけません。
手順の詳細は .skills/llm-wiki/references/MEMORY.md を参照してください。
リダイレクトスタブの扱い
他のスキルは、リダイレクトスタブを次のように扱います。
wiki-export— フロントマターにredirects_to:があるページはスキップします。コンテンツノードではありません。wiki-query— 検索結果がリダイレクトスタブの場合は、redirects_to:をたどり、代わりに正規ページを読みます。wiki-lint— すべてのredirects_to:Wiki リンクが、存在するスタブではないページに解決されることを検証します(スタブがスタブを指すリダイレクトチェーンはエラーです)。cross-linker— リダイレクトスタブをリンク先の候補として扱いません。スタブページを指す新しい[[wikilink]]を追加してはいけません。
ヒント
- 必ず最初に監査してください。 自動マージモードでも、監査レポートは表示されます。結果を信頼する前に確認してください。
needs-reviewは最後に確認してください。 これらは難しいケースです。明らかなマージ対象とまとめて処理しないでください。- 略語が最もよくあるケースです。 「GPT」/「GPT-4」/「GPT4」、「RSC」/「React Server Components」、「LLM」/「Large Language Models」は、部分文字列の包含度が高く、ほぼ常に安全にマージできます。
- バージョンが異なるものは重複ではありません。 「GPT-3」と「GPT-4」は関連していますが、別個のものです。「fine-tuning」と「fine-tuning-llms」も別個の可能性があります(手法と特定の用途)。
- 重複排除の後に
cross-linkerを実行してください。 リダイレクト用スタブによってグラフにわずかな不整合が残ります。Cross-linker を実行すると、それらが整理されます。
Vault への書き込み後の QMD 更新
QMD は検索インデックスであり、信頼できる情報源ではありません。$QMD_WIKI_COLLECTION が空または未設定の場合は、この手順をスキップしてください。このスキルが Vault の Markdown を書き込んだ、または書き換えた後にのみ実行してください。QMD の更新に失敗しても、Vault の変更をロールバックしないでください。QMD の状況は別途報告してください。
$QMD_CLI が設定されている場合はそれを使用し、そうでなければ qmd を使用してください。
${QMD_CLI:-qmd} update
出力にベクトルが必要、または埋め込みが古くなっている可能性があると示されている場合は、次を実行してください。
${QMD_CLI:-qmd} embed
次のいずれかを使用してコレクションを検証してください。
${QMD_CLI:-qmd} ls "$QMD_WIKI_COLLECTION"
または、特定のページパスが分かっている場合は、次を使用してください。
${QMD_CLI:-qmd} get "qmd://$QMD_WIKI_COLLECTION/<page>.md" -l 5
次のいずれかを記録してください。
QMD refreshed: update + embed + verifiedQMD refreshed: update only + verifiedQMD skipped: QMD_WIKI_COLLECTION unsetQMD skipped: qmd CLI unavailableQMD failed: <short error summary>