mcpField
S
swain-search

swain-search

github.com/cristoslc生産性2116 回インストール25 週間前に更新
リポジトリ

swain-search について

swain-searchは、再利用できるトローブに情報源を収集、正規化、キャッシュするリサーチ用スキルです。トローブの作成、拡張、更新、検索に対応し、リモートソースの証拠も検証します。

主な機能

  1. 01トローブの作成、拡張、更新、検索
  2. 02Web、文書、メディアソースの正規化
  3. 03既存トローブ内の関連調査を確認
  4. 04リモートソースのスナップショットを記録・検証

活用シーン

  1. 01調査テーマの情報源を収集
  2. 02既存トローブに新しい資料を追加
  3. 03テーマから収集済みの情報源を検索

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/cristoslc/swain --skill swain-search

インストール後、Claude Code で/swain-searchを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

CHANGELOG.md
README.md
references/
manifest-schema.md
normalization-formats.md
paywall-proxies.yaml
trovewatch-guide.md
wordlist.txt
scripts/
bootstrap.sh
convert-cookies.py
export-snapshot.sh
extract_frames.py
fetch_x_thread.py
log-snapshot-metadata.sh
migrate-to-troves.sh
ocr_frames.py
parse_vtt.py
resolve-proxy.sh
trovewatch.sh
verify-snapshot-evidence.sh
yt-dlp.sh
tests/
test-convert-cookies.py
test-export-snapshot.sh
test-resolve-proxy.sh

詳細

swain-search

ソース資料を収集、正規化、キャッシュして、swain-design の成果物から参照できる再利用可能なトローブにまとめます。

スクリプトの実行方法

スクリプトは、このスキルの scripts/ ディレクトリにあります。<SKILL_DIR> プレースホルダーは、この SKILL.md を格納しているフォルダーを指します。実行時に解決してください。インストール済みのスキルでは .claude/skills/swain-search/、swain リポジトリでは skills/swain-search/ です。

メディアまたは X スレッドのフローを実行する前に、セッションごとに一度ブートストラップを実行してください。

bash "<SKILL_DIR>/scripts/bootstrap.sh"

このスクリプトは、uv が PATH 上にあることを確認します。初回実行後は、~/.local/share/swain-search/.bootstrapped にあるマーカーファイルによって、以降の実行が短絡されます。ゼロ以外の値で終了した場合は、処理を中止し、不足しているものを担当者に伝えてください。

モードの判定

シグナルモード
トピックのトローブが存在しない、またはユーザーが「X を調査」/「ソースを収集」と依頼している作成 — 新しいトローブ
トローブが存在し、ユーザーが新しいソースを提供する、または「追加」/「拡張」と依頼している拡張 — 既存のトローブにソースを追加
トローブが存在し、ユーザーが「更新」と依頼している、またはソースが TTL を過ぎている更新 — 古くなったソースを再取得
ユーザーが「どのようなトローブがあるか」または「X に関するソースを探して」と依頼している検索 — タグで既存のトローブを検索

先行調査の確認

新しいトローブを作成したりウェブ検索を実行したりする前に、関連する内容がないか既存のトローブを確認します。これにより、調査の重複を防ぎ、過去の作業とのつながりを見つけられます。

フェーズ 1 — キーワードの完全一致

ソース名、URL の一部、著者名を検索します。

# Search trove manifests by tag
grep -rl "<keyword>" docs/troves/*/manifest.yaml 2>/dev/null

# Search trove source content
grep -rl "<keyword>" docs/troves/*/sources/**/*.md 2>/dev/null

# Search trove syntheses
grep -rl "<keyword>" docs/troves/*/synthesis.md 2>/dev/null
フェーズ 2 — トピックの意味的な一致

ソースを取得し、その内容を把握したら、ソースの 内容 からトピックキーワードを 3〜5 個抽出します(名前や URL だけから抽出しないでください)。次に、トピックで既存のトローブを検索します。

# Search trove tags for topic keywords
grep -l "<topic-keyword-1>\|<topic-keyword-2>\|<topic-keyword-3>" docs/troves/*/manifest.yaml 2>/dev/null

# Search synthesis summaries for topic keywords
grep -l "<topic-keyword-1>\|<topic-keyword-2>\|<topic-keyword-3>" docs/troves/*/synthesis.md 2>/dev/null

トピックキーワードには、ソースの 名前 ではなく、ソースの 内容 を記述してください。たとえば、Claude Code 用のメモリーシステムを実装する「Cog」という名前のリポジトリの場合、トピックキーワードは agent-memory、memory-architecture、claude-code、persistent-memory などとし、cog や marciopuga にはしないでください。

ソースをまだ取得していない場合(URL のみの呼び出し)は、URL やタイトルから得られるトピック情報を使い、ソースの取得後にトピック全体との照合を行ってください。

判定ゲート

「作成」または「拡張」モードに進む前に、判定結果を見える形で出力してください。

先行調査の確認: フェーズ 1 では[N 件の一致/一致なし]が見つかりました。フェーズ 2 では[N 件の一致/一致なし]が見つかりました:[trove-id(タグ:x、y)、...]。 判定: [理由]により、[trove-id]を拡張します/新しいトローブ[slug]を作成します。

これにより、トローブの振り分け判断を監査可能にできます。いずれかのトローブで 2 つ以上のトピックキーワードが一致する場合、トピックが実際に異なる(隣接しているが、主題が異なる)のでなければ、デフォルトで「拡張」モードを選択してください。

一致した場合の対応

既存のトローブに関連するソースが含まれている場合:

  1. 見つかった内容を報告する — トローブ ID、一致したソースのタイトル、関連する抜粋を示します
  2. 新規作成より拡張を提案する — 既存のトローブが同じトピックを扱っている場合は、並行するトローブを新規作成せず、既存のトローブを拡張します
  3. 相互リンクを追加する — トピックが隣接しているものの別個である場合は、新しいトローブを作成し、synthesis.md に関連するトローブを記載します

この手順は、すべてのモード(作成、拡張、検索)で、ウェブ検索の前に実行します。既存のトローブの内容は、必ず最初に確認してください。

スナップショット証拠ゲート(SPEC-220)

リモートソースを収集済みの証拠として扱う前に、実行時に未加工のスナップショットを作成し、.agents/search-snapshots/metadata.jsonl にメタデータの台帳エントリを記録する必要があります。

リモートソースに必要なフロー:

  1. まず未加工のスナップショットをエクスポート/ダウンロードします。
    • bash "<SKILL_DIR>/scripts/export-snapshot.sh" --url "<source-url>" --out-dir ".agents/search-snapshots/raw"
  2. writing-skills または skill-creator を使って、ダウンロードしたファイルを正規化します(要約のみのブラウザメモは使用しないでください)。
  3. メタデータを記録します。
    • bash "<SKILL_DIR>/scripts/log-snapshot-metadata.sh" --source-url "<source-url>" --export-mode "<mode>" --raw-path "<raw-path>" --normalized-path "<normalized-path>" --normalization-skill "<writing-skills|skill-creator>"
  4. 公開前に検証します。
    • bash "<SKILL_DIR>/scripts/verify-snapshot-evidence.sh" --source-url "<source-url>"

検証に失敗した場合は、ソースを未検証として扱い、下流に公開せず、警告をオペレーターに報告してください。

作成モード

一から新しいトローブを構築します。

ステップ 1 — 入力情報の収集

次の情報をユーザーに尋ねるか、コンテキストから推測します。

  1. Trove ID — トピックを表すスラッグ(例:websocket-vs-sse)。コンテキストから明らかな場合は候補を提案します。
  2. タグ — 検索用キーワード(例:real-time、websocket、sse)
  3. ソース — 次の任意の組み合わせ:
    • ウェブ検索クエリ(「WebSocket と SSE の比較を検索」)
    • URL(ウェブページ、フォーラムスレッド、ドキュメント)
    • 動画/音声の URL
    • ローカルファイルのパス
  4. 鮮度 TTL のオーバーライド — 任意。ほとんどのトローブではデフォルトのままで問題ありません。

swain-design から呼び出された場合(例:スパイクが Active に移行するとき)は、アーティファクトのコンテキストからトピック、タグ、場合によっては初期ソースが提供されます。

ステップ 2 — 収集と正規化

各ソースについて、適切な機能を使用します。ソースの種類ごとの正確な Markdown 構造については、references/normalization-formats.md を参照してください。

ウェブ検索クエリ:

  1. ウェブ検索機能を使って関連する結果を探します。
  2. 関連性の高い上位 3~5 件の結果を選びます。
  3. 各結果について、ページを取得し、ウェブページ形式に従って Markdown に正規化します。
  4. ウェブ検索機能を利用できない場合は、ユーザーに伝えてスキップします。

ウェブページ URL:

  1. ブラウザーまたはページ取得機能を使ってページを取得します。
  2. 定型コンテンツ(ナビゲーション、広告、サイドバー、Cookie バナー)を取り除きます。
  3. ウェブページ形式に従って Markdown に正規化します。
  4. 取得に失敗した場合は、マニフェストに failed: true フラグを付けて URL を記録し、次に進みます。

認証が必要なサイト(Cookie のサポート):

対象サイトで認証またはサブスクリプションアクセスが必要な場合は、ブラウザーからエクスポートした Cookie を指定します。

  1. 対象サイトにログインしているブラウザーから Cookie をエクスポートします。

    • Firefox:「cookies.txt」拡張機能を使用するか、DevTools → Storage → Cookies → 右クリック → 「Export All」からエクスポートします。
    • Chrome:「EditThisCookie」拡張機能を使用するか、DevTools → Application → Cookies → 右クリック → 「Export」からエクスポートします。
    • Host raw、Name raw、Content raw、Path raw、Expires raw、Send for raw、This domain only raw フィールドを含む Cookie オブジェクトの JSON 配列を生成する任意のツールを使用できます。
  2. エクスポートした JSON ファイルを export-snapshot.sh に渡します。

    bash "<SKILL_DIR>/scripts/export-snapshot.sh" \
      --url "<source-url>" \
      --out-dir ".agents/search-snapshots/raw" \
      --cookies "path/to/cookies.json"
    

    --cookies フラグを指定すると、convert-cookies.py を使って Netscape 形式に変換し、Cookie jar が curl リクエストに添付されます。エクスポートモードは <mode>-with-cookies として記録されます。

  3. Cookie が指定されていない場合は、認証なしで取得を続行します(動作は従来どおりです)。

  4. ブラウザーベースのページ取得経路(MCP ブラウザーツール)では、ブラウザー自身のセッションの Cookie が自動的に利用されるため、Cookie ファイルを明示的に指定する必要はありません。このフラグは、curl ベースのスナップショットエクスポートに便利です。 Google Docs/Drive のようなドキュメント:

  5. まず生のコンテンツをエクスポートします(必須):

    • bash "<SKILL_DIR>/scripts/export-snapshot.sh" --url "<source-url>" --out-dir ".agents/search-snapshots/raw"
  6. API のエクスポートモード(google-doc-export、google-slides-export、google-drive-download)を優先します。

  7. API エクスポートに失敗した場合は、利用可能な場合に限り、ブラウザーヘルパーを代替手段として使用します。

  8. エクスポートしたファイルを writing-skills または skill-creator で正規化します。

  9. メタデータを .agents/search-snapshots/metadata.jsonl に記録します。

  10. ソースを trove の出力に含める前に、verify-snapshot-evidence.sh で検証します。

ペイウォールプロキシによる代替手段:

Web ページを取得した後、その URL のドメインでペイウォールプロキシが利用できるか確認します:

  1. bash "<SKILL_DIR>/scripts/resolve-proxy.sh" <url> を実行します
    • 終了コード 1:プロキシが設定されていません。直接取得したコンテンツをそのまま使用します
    • 終了コード 0:PROXY:<name>:<proxy-url> と SIGNAL:<text> の行が出力されます
  2. 終了コードが 0 の場合、取得したコンテンツに各 SIGNAL のテキストが含まれているか確認します(大文字と小文字を区別した文字列一致)
  3. いずれかのシグナルが一致した場合(または記事本文が約 200 語未満の場合):
    • 「<url> でペイウォールを検出しました。プロキシによる代替取得を試します」とログに記録します
    • ページの取得に使用したものと同じ機能を使って、各 PROXY URL から順に取得を試します
    • 実質的なコンテンツ(切り詰められた元の内容よりも多いもの)を返した最初のプロキシを採用します
    • マニフェストのエントリーに proxy-used: <name> と notes: "Full article retrieved via <name> proxy" を設定します
  4. シグナルが一致しない場合は、直接取得したコンテンツをそのまま使用します(プロキシは不要です)
  5. すべてのプロキシが失敗した場合は、元の切り詰められたコンテンツを保持し、notes: "Paywalled; proxies exhausted — content from direct fetch only" を設定します

レジストリは references/paywall-proxies.yaml にあります。新しいドメインやプロキシはここに追加してください。スキルファイルを変更する必要はありません。

動画/音声の URL(YouTube、Instagram、ポッドキャスト):

以下の段階的な手順に従います。各段階で /tmp/swain_search_media_transcript.txt に書き込みます。そのファイルを references/normalization-formats.md のメディア形式に従って正規化します。出力先は sources/<source-id>/<source-id>.md です。

  1. 字幕とメタデータを取得します。yt-dlp を 1 回だけ呼び出します:

    bash "<SKILL_DIR>/scripts/yt-dlp.sh" --write-auto-sub --sub-lang en --write-info-json --skip-download --sub-format vtt -o "/tmp/swain_search_media" "<URL>"
    

    Instagram の URL の場合は、--cookies-from-browser <browser>(chrome/safari/firefox/brave)を追加します。YouTube 上にないポッドキャストやカンファレンスの URL の場合は、まず Web 検索機能でタイトルから YouTube のリンクを特定します。

  2. VTT による方法(推奨)。/tmp/swain_search_media.en.vtt が存在し、空でない場合:

    test -s /tmp/swain_search_media.en.vtt && uv run "<SKILL_DIR>/scripts/parse_vtt.py"
    

    スクリプトは [HH:MM:SS] line 形式のセグメントを書き込みます。ソースのフロントマターで transcript-source: vtt を設定します。

  3. キャプションによる代替方法。VTT がない場合は、/tmp/swain_search_media.info.json を読み込み、description フィールドを取得します。#\w+ タグを削除します。先頭と末尾の空白を削除します。残ったテキストが 100 文字を超える場合は、/tmp/swain_search_media_transcript.txt に書き込みます(1 行につき 1 段落)。transcript-source: caption を設定します。タイムスタンプは省略します。

  4. フレーム抽出による代替方法(オペレーターの承認が必要)。キャプションが短すぎる場合(100 文字以下)は、停止して次のように確認します:

    字幕または使用可能なキャプションが見つかりませんでした。フレームを抽出し、画面上のテキストを読み取って文字起こしを作成できます。この処理には opencv-python-headless(約 30 MB、uv 経由)が必要です。続行しますか?

    承認された場合:

    • 動画をダウンロードします:bash "<SKILL_DIR>/scripts/yt-dlp.sh" -o "/tmp/swain_search_video.mp4" "<URL>"。Instagram の場合は --cookies-from-browser <browser> を追加します。
    • フレームを抽出します:uv run --with opencv-python-headless python3 "<SKILL_DIR>/scripts/extract_frames.py" /tmp/swain_search_video.mp4。シーン変化検出により、/tmp/swain_search_frame_000.png 以降のファイルが保存されます。
    • 視覚認識を確認します。/tmp/swain_search_frame_000.png に対して Read ツールを使用します。表示されているテキストを読み取れるか試します。読み取れた場合は手順 5 に進みます。読み取れない場合は手順 6 に進みます。
  5. ビジョン OCR(推奨)。残りの各フレームで Read ツールを使用します。表示されているテキストをすべて抽出します。隣接するフレーム間で重複する内容は除外します。テキストを /tmp/swain_search_media_transcript.txt に書き込みます。transcript-source: vision-ocr を設定します。タイムスタンプは付けません。

  6. ローカル OCR によるフォールバック。ビジョンが利用できない場合は、次のように尋ねます。

    ビジョンを利用できません。EasyOCR を使ったローカル OCR に切り替えます(初回実行時に約 400 MB をダウンロードします)。続行しますか?

    承認されたら:

    uv run --with "easyocr,opencv-python-headless" python3 "<SKILL_DIR>/scripts/ocr_frames.py"
    

    transcript-source: local-ocr を設定します。タイムスタンプは付けません。

  7. 正規化してソースを書き込む。動画のタイトルからソース ID のスラッグを導き出します。小文字、数字、ハイフンのみを使用します。メディア形式に従って sources/<source-id>/<source-id>.md に書き込みます。フロントマターに transcript-source を追加します。ステップ 2 を実行した場合に限り、duration、speakers、YouTube のディープリンクを追加します。

どのティアも成功しなかった場合は、マニフェストに failed: true と reason: <tier> を設定してソースを記録します。

X/Twitter のスレッド:

URL パターン:(x|twitter|fxtwitter|fixupx).com/.+/status/\d+。公開 fxtwitter API を使って展開します。認証は不要です。

  1. スレッドを取得します。

    uv run "<SKILL_DIR>/scripts/fetch_x_thread.py" "<URL>"
    

    スクリプトは 2 つのファイルを書き込みます。/tmp/swain_search_thread.json には生のレスポンスが、/tmp/swain_search_thread_transcript.txt には引用投稿のブロッククォートを含む、つなぎ合わせたトランスクリプトが保存されます。また、標準出力にメタデータ JSON オブジェクトを出力します。次のフィールドを取得します:author_name、author_handle、author_url、published_date、tweet_count、title_guess、source_url、post_urls、cited_posts。

  2. 展開できないスレッド。スレッドの冒頭投稿に対してスクリプトが「only 1 post returned」で終了した場合、上流のデプロイ環境にはアカウントプロキシがありません。failed: true と reason: x-thread-unrollable を設定してエントリーを記録し、次に進みます。

  3. ソース ID は <author_handle>-<first-few-title-words> とします。小文字、数字、ハイフンのみになるようサニタイズします。@ は削除します。

  4. references/normalization-formats.md の X スレッド形式に従って正規化します。

    • フロントマター:共通フィールドに author-handle、author-name、published-date、tweet-count を追加します。
    • 本文:すべての投稿を番号付きリストにして、原文のまま記載します。各番号に、その投稿の URL へのハイパーリンクを設定します。先頭の自動メンション列を削除します。これは、返信に X が追加する @handle の接頭辞です。本文中の @メンション は [@handle](https://x.com/handle) に、ハッシュタグは [#tag](https://x.com/hashtag/tag) にします。
    • 引用投稿:各 cited_posts エントリーを、引用元の投稿の下にインデントしたブロッククォートとして記載します。内容のある自分への返信は最大 3 件まで、続きとして追加します。URL のみの自分への返信は省略します。これらはすでに external_links に記載されています。自分への返信が 3 件を超える場合は、リンクを設定します。
  5. sources/<source-id>/<source-id>.md に保存します。

ローカルファイル:

  1. ドキュメント変換機能(PDF、DOCX など)を使用するか、すでに Markdown 形式の場合は直接読み込みます。
  2. writing-skills または skill-creator を使って、ドキュメント形式に従って正規化します。
  3. Markdown ファイルの場合は、フロントマターのみを追加し、内容はそのまま保持します。

フォーラムのスレッド/ディスカッション:

  1. フォーラム形式に従って取得し、正規化します(時系列順、投稿者を明記)。
  2. ネストされたスレッドは、返信先のコンテキストを付けて時系列順にフラット化します。

リポジトリ:

  1. リポジトリをクローンするか、内容を読み込みます。
  2. 元のディレクトリ構造を sources/<source-id>/ 以下に再現します。
  3. デフォルトでは、ツリー全体を再現します。数千ファイルに及ぶ大規模なリポジトリでは、対象を選択して取り込み、マニフェストエントリーに selective: true を設定します。
  4. 最も重要なファイルのパス(source-id ディレクトリからの相対パス)を highlights 配列に記入します。

ドキュメントサイト:

  1. ドキュメントサイトをクロールするか、取得します。
  2. セクション階層を sources/<source-id>/ 以下に再現します。
  3. デフォルトでは、サイト全体を再現します。大規模なサイトでは、対象を選択して取り込み、selective: true を設定します。
  4. 最も重要なページのパスを highlights 配列に記入します。
  5. 可能な限り、内部リンク構造を保持します。

CLI ツール: まず、対象が CLI ツールかどうかを検出します。以下の条件を確認します。

  • 対象が PATH 内に存在する(command -v <tool-name> を実行)
  • 名前が CLI のパターンに一致する(小文字、ハイフンを含む、スペースなし)
  • 文脈からコマンドラインツールだと判断できる

対象が CLI ツールの場合は、以下のキャプチャ手順を実行します。

  1. マニュアルページのキャプチャ:

    • man <tool-name> を実行
    • 成功した場合は cli-manpage タイプとして保存
    • マニュアルページがない場合は、ヘルプのキャプチャに進む
  2. プライマリヘルプのキャプチャ:

    • <tool-name> --help を実行
    • 失敗した場合は <tool-name> -h を試す
    • cli-help タイプとして保存
  3. サブコマンドの検出:

    • ヘルプ出力で以下のパターンを探します。
      • 「Commands:」または「Subcommands:」の見出し
      • 「Usage:」セクション内のインデントされたコマンド一覧
      • <tool> <command> [options] のようなコマンドパターン
    • コマンド以外のものを除外します。
      • 1語またはハイフンでつながれた文字列は含める
      • Usage 行にある位置引数は含める
      • - で始まるもの(フラグ)は除外する
  4. サブコマンドの再帰的なキャプチャ:

    • 各サブコマンドについて、<tool-name> <subcommand> --help を実行
    • depth: 1 の cli-subcommand-help として保存
    • サブコマンド自体にサブコマンドがある場合は、もう1段階深く進む
    • 最大深度:2
  5. 失敗時の処理:

    • マニュアルページがない場合は、ヘルプのキャプチャのみを使用
    • --help と -h の両方が失敗した場合は、マニフェストに記録
    • すべてのキャプチャが失敗した場合は、failed: true としてマークし、続行

各キャプチャは個別のソースになります。

  • sources/<tool>-manpage/<tool>-manpage.md(タイプ:cli-manpage)
  • sources/<tool>-help/<tool>-help.md(タイプ:cli-help)
  • sources/<tool>-<subcommand>-help/<tool>-<subcommand>-help.md(タイプ:cli-subcommand-help)

正規化された各ソースには スラッグベースのソース ID を付与し、ソースごとにディレクトリを分けて配置します。

  • フラットソース(web、forum、media、document、local):sources/<source-id>/<source-id>.md
  • 階層型ソース(repository、documentation-site):sources/<source-id>/ 内に元のツリー構造を複製

ソース ID の生成:

  • ソースタイトルまたは URL からスラッグとしてソース ID を導出します(例:mdn-websocket-api、strangeloop-2025-realtime)
  • スラッグが既存のソース ID と衝突した場合は、references/wordlist.txt のランダムな2語を使い、__word1-word2 を追加します
  • 単語リストがない場合は、代わりに __ に続けて16進数4文字を追加します(例:__a3f8)
ステップ 3 — マニフェストの生成

references/manifest-schema.md のスキーマに従って manifest.yaml を作成します。以下を含めます。

  • トローブのメタデータ(id、作成日、タグ)
  • ソースタイプごとのデフォルトの鮮度 TTL
  • 出所情報(URL/パス、取得日、コンテンツハッシュ、タイプ)を含む、ソースごとのエントリ

コンテンツハッシュは、正規化された Markdown コンテンツの SHA-256 ダイジェストをプレフィックスなしの16進数で計算します。

shasum -a 256 sources/mdn-websocket-api/mdn-websocket-api.md | cut -d' ' -f1
ステップ 4 — 統合結果の生成

すべてのソースにわたる重要な知見を構造的に抽出した synthesis.md を作成します。

統合結果はソースごとではなく、テーマごとに構成します。関連する知見をまとめ、ソース ID を引用し、以下を明らかにします。

  • 主な知見 — ソース全体がトピックについて述べていること
  • 一致点 — ソース間で見解が一致している箇所
  • 相違点 — ソース間で意見が対立している箇所や、異なる選択肢を示している箇所
  • 不足点 — 重要となり得るものの、ソースが扱っていない内容

簡潔にまとめます。統合結果は出発点であり、網羅的なレポートではありません。ユーザーまたは成果物の作成者が内容を調整します。

ステップ 5 — コミットと記録

デュアルコミットパターン(swain-design のライフサイクルスタンプと同じ)を使用して、trove に到達可能なコミットハッシュを付与します。

コミット A の前 — コミットハッシュのプレースホルダー -- を含む history エントリを manifest.yaml に追加します。

history:
  - event: created
    date: 2026-03-09
    commit: "--"
    sources: 3

コミット A — trove の内容をコミットします。

git add docs/troves/<trove-id>/
git commit -m "research(<trove-id>): create trove with N sources

Co-Authored-By: <model-name-from-system-prompt> <noreply@unknown>"
TROVE_HASH=$(git rev-parse HEAD)

コミット B — history エントリにコミットハッシュを反映し、参照元アーティファクトのフロントマター(存在する場合)を更新します。

# Replace "--" with the real hash in the history entry
# Update artifact frontmatter: trove: <trove-id>@<TROVE_HASH>
git add docs/troves/<trove-id>/manifest.yaml
git add docs/<artifact-type>/<phase>/<artifact-dir>/   # if artifact exists
git commit -m "docs(<trove-id>): stamp history hash ${TROVE_HASH:0:7}

Co-Authored-By: <model-name-from-system-prompt> <noreply@unknown>"

参照元アーティファクトがまだ存在しない場合(独立した調査)、コミット B では引き続き history エントリにスタンプを付けます。後で参照できるよう、ハッシュを報告してください。

プッシュ — コミット B の後、trove をほかのエージェントやセッションですぐに利用できるよう、必ず origin/trunk にプッシュしてください。これは必須であり、省略できません。

git push origin trunk

合成ファイルの URL を導出 — 最終レポート用に、合成ファイルへの安定したパーマリンクを作成します。

REMOTE_URL=$(git remote get-url origin | sed 's/\.git$//' | sed 's/[email protected]:/https:\/\/github.com\//')
SYNTHESIS_URL="${REMOTE_URL}/blob/${TROVE_HASH}/docs/troves/<trove-id>/synthesis.md"
ステップ 6 — 報告

作成した内容をユーザーに伝えます。

trove <trove-id> を作成しました。N 件のソースがあり、<TROVE_HASH:0:7> としてコミットされています。

  • docs/troves/<trove-id>/manifest.yaml — 出典情報とメタデータ
  • docs/troves/<trove-id>/sources/ — 正規化済みのソースファイル N 件
  • docs/troves/<trove-id>/synthesis.md — テーマ別の要約:<SYNTHESIS_URL>

アーティファクトから参照する場合:trove: <trove-id>@<TROVE_HASH:0:7>

レポートには必ず合成ファイルの URL を含めてください。1 回の実行で複数の trove を作成した場合は、それぞれの合成ファイルの URL を記載してください。

拡張モード

既存の trove に新しいソースを追加します。

  1. 既存の manifest.yaml を読みます
  2. 新しいソースを収集し、正規化します(作成ステップ 2 と同じ)
  3. 同じ ID 生成ルールに従い、新しいソースにスラッグベースのソース ID を割り当てます
  4. manifest.yaml に新しいエントリを追加します
  5. refreshed の日付を更新します
  6. すべてのソース(既存分と新規分)を取り込んで synthesis.md を再生成します
  7. event: extended と commit: "--" のプレースホルダーを含む history エントリを追加します
  8. 作成ステップ 5 と同じデュアルコミットパターンでコミットし、スタンプを付けます。
    • コミット A:`git commit -m "research(): extend with N new sources

Co-Authored-By: noreply@unknown"`

  • TROVE_HASH=$(git rev-parse HEAD) を取得します
  • コミット B:history エントリにハッシュを反映し、参照元アーティファクトのフロントマター(アーティファクトが存在する場合)を更新します
  • プッシュ(必須):git push origin trunk
  • SYNTHESIS_URL を導出します(作成ステップ 5 と同じ方法)
  1. 追加した内容を、新しいコミットハッシュと合成ファイルの URL を含めて報告します

更新モード

古くなったソースを再取得し、変更された内容を更新します。

  1. manifest.yaml を読みます
  2. 各ソースについて、fetched の日付から freshness-ttl が経過したか確認します
  3. 古くなったソースについて、次の処理を行います。
    • 元のコンテンツを再取得します
    • Markdown に再正規化します
    • 新しいコンテンツハッシュを計算します
    • ハッシュが変わった場合:ソースファイルを置き換え、マニフェストのエントリを更新します
    • ハッシュが変わっていない場合:fetched の日付のみを更新します
  4. マニフェストの refreshed の日付を更新します
  5. 内容に変更があった場合は、synthesis.md を再生成します
  6. event: refreshed、sources-changed: M、および commit: "--" のプレースホルダーを含む history エントリを追加します
  7. 作成ステップ 5 と同じデュアルコミットパターンでコミットし、スタンプを付けます。
    • コミット A:`git commit -m "research(): refresh N sources (M changed)

Co-Authored-By: noreply@unknown"`

  • TROVE_HASH=$(git rev-parse HEAD) を取得します
  • コミット B:history エントリにハッシュを反映し、参照元アーティファクトのフロントマターを更新します。依存するすべてのアーティファクトについて、マニフェストの referenced-by を確認してください
  • プッシュ(必須):git push origin trunk
  • SYNTHESIS_URL を導出します(作成ステップ 5 と同じ方法)
  1. 次のように報告します。「N 件のソースを更新しました。M 件は内容が変更され、K 件は変更がありませんでした。新しいハッシュ:<TROVE_HASH:0:7>。合成ファイル:<SYNTHESIS_URL>」

freshness-ttl: never のソースは、更新時にスキップしてください。

検索モード

ユーザーがトピックに関連する既存のトローブを見つけられるようにします。

  1. すべてのトローブについて、docs/troves/*/manifest.yaml をスキャンします
  2. 次の方法でユーザーのクエリと照合します:
    • タグの照合 — トローブのタグにクエリのキーワードが含まれている
    • タイトルの照合 — トローブ ID のスラッグにクエリのキーワードが含まれている
  3. 一致した各トローブについて、トローブ ID、タグ、ソース数、最終更新日、参照元の一覧を表示します
  4. 一致するものがない場合は、新しいトローブの作成を提案します

機能が利用できない場合の対応

このスキルでは、機能を一般的な形で参照しています。機能が利用できない場合は、次のように対応します:

機能代替対応
ウェブ検索検索によるソースの取得をスキップします。ユーザーに「ウェブ検索機能を利用できません。URLを直接指定するか、検索用の MCP を追加してください」と伝えます。
ブラウザー/ページ取得ツール基本的な URL 取得を試みます。失敗した場合は「この URL を取得できません。内容を貼り付けるか、ローカルファイルを指定してください」と伝えます。
リモート文書のスナップショット出力出力に失敗し、ヘルパーも存在しない場合は、ソースを未検証としてマークし、下流には公開せず、正確な URL と失敗の内容を報告します。
メディアの文字起こし「文字起こし機能を利用できません。事前に作成した文字起こしファイルを指定するか、メディア変換ツールを追加してください」と伝えます。
文書変換「このファイル形式を変換できません。Markdown 版を指定するか、文書変換ツールを追加してください」と伝えます。
ペイウォールプロキシ省略された内容はそのまま残します。マニフェストに「ペイウォールあり。プロキシを使い果たしました」と記載します。ユーザーに手動で内容を提供するよう提案します。

1つの機能が利用できないために、処理全体を失敗させてはいけません。可能な範囲で情報を収集し、できないことはスキップして、状況を明確に報告します。

機能の検出

ソースを収集する前に、利用可能な機能を確認します。次のパターンに一致するツールを探します。正確なツール名はインストール環境によって異なります。

  • ウェブ検索:名前に「search」を含むツール(例:brave_web_search、bing-search-to-markdown)
  • ページ取得:名前に「fetch」、「webpage」、「browser」を含むツール(例:fetch_content、webpage-to-markdown、browser_navigate)
  • メディアの文字起こし:名前に「audio」、「video」、「youtube」を含むツール(例:audio-to-markdown、youtube-to-markdown)
  • 文書変換:名前に「pdf」、「docx」、「pptx」、「xlsx」を含むツール(例:pdf-to-markdown、docx-to-markdown)
  • CLI ツールの取得:組み込みの bash 機能(man、コマンド実行)。POSIX システムでは常に利用できます

収集を開始する際に利用可能な機能を報告し、何ができて何ができないかをユーザーに知らせます。

成果物からのリンク

成果物では、フロントマターでトローブを参照します。

trove: websocket-vs-sse@abc1234

形式は <trove-id>@<commit-hash> です。コミットハッシュによってトローブを特定のバージョンに固定します。ソースの追加や更新に伴ってトローブは変化しますが、このハッシュによって再現性が確保されます。

Create のステップ 5、Extend のステップ 8、Refresh のステップ 7 にある二重コミットのワークフローが、これを自動的に処理します。コミット A でトローブの内容を記録し、コミット B で履歴エントリーと参照元の成果物のフロントマターにハッシュを記録します。この処理をオペレーターに先送りしてはいけません。

コメント