sr-query
sr-query は、ストックルーム倉庫(Stockroom)——Cursor + Claude Code の履歴をキャプチャした単一ファイルの DuckDB データベース——に対して 読み取り専用の SQL を実行します。表面は設計上読み取り専用です。DuckDB はそこを通じた書き込みを全て拒否するため、クエリによってデータを壊すことはありません。
sr-query を使用すべき時
質問に 既知の形状 があり、名前を指定できるフィールドに対する正確または構造化された検索が答えである場合に sr-query を使ってください。
- ID による特定の行(
WHERE message_id = '…'、WHERE session_id = '…') - フィルター、カウント、ロールアップ(
COUNT(*)、GROUP BY harness、WHERE harness = 'claude'、トークン合計、日付範囲) - VIEW
session_token_usageを介したセッションごとのトークンロールアップ(手動でmessagesに対してSUMを書くよりもこちらを推奨) sessions/messages/tool_calls間の結合- 「いくつあるか」「どのセッションか」「ツール呼び出しをリストアップ」「どのモデルか」——名前付きカラムに対して SQL で表現できるもの全て
意味ベース の想起(「flaky tests に関する会話を探す」)には sr-query を使用しないでください。SQL の ILIKE はリテラル部分文字列マッチであり、意味検索ではありません。意味検索には sr-semantic スキルを使用してください。どちらが適切か判断できない場合は、その判断は sr-search スキルに委ねてください。
エンジンの呼び出し方
stockroom query "SELECT DISTINCT harness FROM sessions ORDER BY harness"
標準入力から SQL をパイプで渡すこともできます(- を使用):
echo "SELECT count(*) FROM messages" | stockroom query -
command -v stockroom が失敗した場合は、マシンがセットアップされていません。ユーザーに sr-initialize スキルを実行するよう伝え、それ以外の呼び出しは試みないでください。
出力規則: --format と --detail
デフォルトはエージェントにとって既に安全 です——ベアコールでもバウンドされパース可能な出力が得られます。そのため、状況が必要とする場合にのみフラグを使用してください。
--format — 出力形状、デフォルト tsv
| 値 | 形状 | 使用すべき時 |
|---|---|---|
tsv (デフォルト) | ヘッダー行 + タブ区切り行、カウントトレーラーなし | デフォルト。エージェントおよび unix パイプ(cut、awk、wc -l)に適したストリーム形式。 |
json | 単一の {"columns": [...], "rows": [...]} オブジェクト | ユーザー が構造化/機械出力を要求した場合、または jq に渡したい場合。 |
table | カラム整列された ASCII と (N rows) トレーラー | ユーザー が人間可読な表示やコピペ用コマンドを要求した場合。 |
デフォルトの tsv を先に使ってください。ユーザーが 人間可読または構造化出力を求めてきた場合にのみ、--format table や --format json を提案してください——「必要なら他の形式もありますよ」と。
--detail — フィールドごとの幅、デフォルト snippet
幅のある文字列フィールド(特に messages.text や tool_calls.tool_input)は、読み取り時に 切り詰められるため、1つの太いカラムがコンテキストを圧迫することはありません。切り詰めは表示のみで、全文は常に取得可能です。
| 値 | フィールドあたりの予算 | 使用すべき時 |
|---|---|---|
compact | 約40文字 | 多くの候補行を低コストでスキャンし、その後1つを選ぶ場合。 |
snippet (デフォルト) | 約120文字 | デフォルト。行をダンプせずに認識するのに十分。 |
full | 無制限、単一行 | フィールド全体の長さが必要で、単一行への折りたたみで問題ない場合(テーブル/TSVは安全)。 |
raw | 無制限、正確な空白 | 保存されたまま のテキストが必要な場合——改行や内部スペースもそのまま。--format json と組み合わせて推奨。 |
予算を超えたフィールドは、何文字隠されたかを示すマーカー(例: …(+2284) )とともに省略されます。このマーカーが「さらに存在する」というシグナルです。実際に必要であれば、そのフィールドだけを特定の行について --detail full(長さ)または --format json --detail raw(正確な空白)で再取得してください。ガードレールを参照。
ガードレール
このスキルが防止すべき障害モード:
- コンテキストを圧迫しないこと。 多くの行に対して
--detail full/rawでSELECT *(または幅広いtextカラム)を決して実行しないでください。代わりに、明示的なカラムリストとLIMITを使って--detail compact/snippetで狭くスキャンし、目的の行を特定してから、その行の幅広いフィールドだけを ID でWHEREして再取得してください。単一のSELECT text FROM messages WHERE message_id = '…'は低コストですが、SELECT * FROM messages --detail fullはコンテキスト爆弾です。正確な 空白(マークダウンテーブル、コードブロック)が必要な場合は、--format json --detail rawを使用してください。 - 読み取り専用です——書き込みは試みないでください。
INSERT/UPDATE/DELETE/CREATEは全て失敗します(query failed: …)。異なる表現で書き込みを再試行しないでください。表面は問い合わせのみを許可します。 tool_inputは異種 JSON です。 そのキーはツールごとに異なります(Shellはcommand、Readはpathなど)。そのため、素朴なtool_input->>'key'はキャストエラーを引き起こす可能性があります。サブクエリ/CTE で最初にtool_nameをフィルタリングし、明示的な関数を使って安全に抽出してください:
stockroom query "SELECT json_extract_string(tool_input, '\$.command') AS cmd
FROM (SELECT tool_input FROM tool_calls WHERE tool_name = 'Shell') LIMIT 5"
エラーを処理し、暴走を避ける
各障害はクリーンな stderr メッセージ + 終了コードとして現れます。それを読み、対応するアクションを取ってください。ループしないでください:
| メッセージ | 終了コード | 意味 / 次のアクション |
|---|---|---|
error: empty query (…) | 2 | SQL が渡されませんでした。ステートメントを提供してください。 |
error: no warehouse found at … — run \stockroom ingest` first` | 1 | 倉庫が構築されていません。ユーザーに stockroom ingest を実行するよう伝えてください(マシンが一度もセットアップされていない場合は sr-initialize)。クエリを再試行しないでください。 |
query failed: … | 1 | 無効な SQL、または書き込みが拒否されました。ステートメントを修正してください(DuckDB のメッセージを読んでください)——同じ SQL を再実行しないでください。 |
倉庫の中身
まずライブスキーマを発見してください——これによりスキーマの進化に正しく対応できます:
stockroom query "SELECT table_name, column_name, data_type
FROM information_schema.columns ORDER BY table_name, ordinal_position"
マイグレーション 0001–0008 時点の主要カラムのクイックリファレンス(上記のイントロスペクションクエリで確認してください):
sessions— 会話ごとに1行。harness('cursor'|'claude')、session_id、project_id(プロジェクトディレクトリスラッグそのもの——グループ化キー。Cursor CLI チャットの場合はチャットハッシュディレクトリ)、cwd(実パス、NULLの場合あり)、workspace_key(ハーネス横断パスロールアップ、NULLの場合あり)、entrypoint(既知の場合のハーネスからの表面出処、NULLの場合あり——リテラルを推測する代わりにSELECT DISTINCT entrypointを使用)、models(VARCHAR[]、Cursor のみ)、title、git_branch、is_subagent、parent_session_id、started_at、ended_at、source_mtime(最終取り込み時のソーストランスクリプトの mtime)、および4つのNULL許容セッショングレインのトークンBIGINT(input_tokens、output_tokens、cache_creation_tokens、cache_read_tokens——ハーネスが会話レベルの使用量を報告する場合にのみ入力されます。Claude/Cursor はこれらをNULLのままにします)。PK(harness, session_id)。messages— ターンごとに1行。message_id = '{session_id}#{ordinal}'(全ハーネスで統一)、parent_id、ordinal、role('user'|'assistant')、text(全体、思考はキャプチャされません)、model(メッセージごと、Claude のみ)、ts、first_seen_at(ストックルームがそのメッセージを最初に観測した時刻)、および4つのトークンBIGINT:input_tokens、output_tokens、cache_creation_tokens、cache_read_tokens(Claude メッセージ使用量。Cursor はNULL)。PK(harness, session_id, message_id)。session_token_usage— sessions + メッセージトークン合計の VIEW。会話ロールアップにはこちらを推奨。カラム:*_from_messages(SUM)、*_native(セッションカラム)、*_total(COALESCE(native, from_messages))、token_grain('session'|'message'|'none')。合計は報告されたフィールドの倉庫ロールアップであり、ベンダーインボイスではありません。*_totalに加えてメッセージトークンをさらにSUMしないでください。tool_calls— ツール 入力のみ(出力は含まれません)。message_id(それを発行したターン)、ordinal、tool_name、tool_input(異種JSON、全体として保存——ガードレールを参照)。PK(harness, session_id, message_id, ordinal)。embeddings— セマンティック検索用のチャンクごとのベクトル(owner_table、owner_id、chunk_index、embed_model、vector FLOAT[384])。これを直接クエリすることはほとんどありません——sr-semanticスキルを使用してください。_sync_state— 取り込み水位マークのブックキーピング。クエリ対象としては興味深くありません。
結合を書く前に知っておくべき1つの同一性ルール: 常に統一された message_id / (harness, session_id) で結合し、source_* 出処カラムでは決して結合しないでください。ハーネスごとに1つのグレインにしか存在しない値は、もう一方のハーネスでは正直に NULL になります(例: messages.model は Claude のみ、sessions.models は Cursor のみ。同じ二重グレインの正直さがトークンにも適用されます)。
実例
全て実際の倉庫で検証済みです。
# どのハーネスが存在するか?(tsv デフォルト)
stockroom query "SELECT DISTINCT harness FROM sessions ORDER BY harness"
# ハーネスごとのセッション数、ユーザー向けに人間可読:
stockroom query --format table "SELECT harness, count(*) AS n FROM sessions GROUP BY harness ORDER BY n DESC"
# 最も使用されたツール:
stockroom query "SELECT tool_name, count(*) AS calls FROM tool_calls GROUP BY tool_name ORDER BY calls DESC LIMIT 5"
# 候補メッセージを低コストでスキャンし、その後目的のものをフルで再取得:
stockroom query --detail compact "SELECT message_id, text FROM messages WHERE text ILIKE '%flaky test%' LIMIT 10"
stockroom query --format json --detail raw \
"SELECT text FROM messages WHERE message_id = '<スキャンで得たID>'"
# ユーザー/ツールが消費するための構造化出力:
stockroom query --format json "SELECT harness, session_id, title FROM sessions WHERE title IS NOT NULL LIMIT 5"
# セッションごとのトークンロールアップ(VIEW session_token_usage):
stockroom query --format table \
"SELECT harness, session_id, input_tokens_total, output_tokens_total, token_grain
FROM session_token_usage
ORDER BY input_tokens_total DESC NULLS LAST
LIMIT 10"
さらに多くのバリエーション(アクティビティウィンドウ付きの無制限ツール、日/ハーネスごとのトークンロールアップ、ハーネスごとのスキル使用 SQL)については、クックブック を参照してください。
クックブック
難解なスターター SQL: references/cookbook/。インデックスを開き、必要なレシピだけを開いてください。
人間への伝達
あなたはツールのオペレーターであり、その表示装置ではありません。SQL を実行し、結果を読み、自然言語でユーザーに答えてください。ユーザーが生の tsv を見たいと明示的に要求しない限り、人間に貼り付けないでください。ユーザーが生の出力や自分で実行できるコマンドを求めた場合は、--format table(読む用)または --format json(処理する用)のバリアントを渡してください。
システムの理解
これらの契約がなぜそのようになっているのかを理解するために——パッケージング、torch 契約、設計上の読み取り専用、切り詰めの原則、同一性/出処——共有システムモデルをお読みください: ../sr-search/references/system-model.md。