sr-query
sr-query 對 stockroom 倉庫執行唯讀 SQL——這個單一檔案的 DuckDB 資料庫儲存了您擷取的 Cursor + Claude Code 歷史記錄。該介面天生是唯讀的:DuckDB 會拒絕任何透過它嘗試的寫入操作,因此您不會因查詢而損壞任何資料。
何時使用 sr-query
當問題具有已知形狀,且答案是對您能命名的欄位進行精確或結構化查詢時,請使用 sr-query:
- 依 ID 查詢特定行(
WHERE message_id = '…'、WHERE session_id = '…')。 - 篩選、計數和彙總(
COUNT(*)、GROUP BY harness、WHERE harness = 'claude'、Token 總和、日期範圍)。 - 透過 VIEW
session_token_usage進行每場對話的 Token 彙總(優先於在messages上手動撰寫SUM)。 - 跨
sessions/messages/tool_calls的聯結。 - “有多少個”、“哪些對話”、“列出工具呼叫”、“用了哪些模型”——任何你能以 SQL 表達並對應到已命名欄位的內容。
不要將 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 (預設) | 標題行 + 以 Tab 分隔的行,無計數結尾 | 預設。對您和 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)會在讀取時被截斷,以避免單一大型欄位充滿您的上下文。截斷僅影響顯示——完整文字隨時可擷取。
| 值 | 每個欄位的大小 | 使用時機 |
|---|---|---|
compact | 約 40 個字元 | 在選定一行之前,快速掃描多個候選行以節省成本。 |
snippet (預設) | 約 120 個字元 | 預設。足以辨識一行,而不必完全傾印它。 |
full | 無限制,單行 | 您需要欄位的完整長度,且單行折疊是可接受的(表格/TSV 仍保持安全)。 |
raw | 無限制,保留原始空白 | 您需要儲存狀態下的文字——保留換行和內部空白。建議搭配 --format json 使用。 |
超出預算的欄位會以標記省略,報告隱藏了多少字元,例如 …(+2284)。這個標記是您的信號,表示還有更多內容:如果您確實需要它,請以 --detail full(長度)或 --format json --detail raw(精確空白)僅針對該特定行的該欄位重新擷取——請參閱防護機制。
防護機制
以下是此技能旨在防止的失敗模式:
- 不要爆掉你的上下文。 永遠不要對多行執行
SELECT *(或寬的text欄位)並使用--detail full/raw。改為:以--detail compact/snippet搭配明確的欄位列表和LIMIT進行窄範圍掃描,找出您要的那一行,然後用WHERE條件依據其 ID 僅重新擷取該行的寬欄位。一個SELECT text FROM messages WHERE message_id = '…'是成本低廉的;SELECT * FROM messages --detail full則是一個上下文炸彈。當您需要精確的空白(Markdown 表格、程式碼區塊)時,請使用--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——每個對話一行。harness('cursor'|'claude')、session_id、project_id(直接取自專案目錄的 slug——分組鍵;對於 Cursor CLI 聊天,這是聊天雜湊目錄)、cwd(真實路徑,可能為NULL)、workspace_key(跨 harness 的路徑彙總,可能為NULL)、entrypoint(來自 harness 的表面來源,已知時提供;可能為NULL——請使用SELECT DISTINCT entrypoint而非猜測字面值)、models(VARCHAR[],僅限 Cursor)、title、git_branch、is_subagent、parent_session_id、started_at、ended_at、source_mtime(最後一次擷取時的來源轉錄檔修改時間),以及四個可為 null 的對話層級 TokenBIGINT欄位(input_tokens、output_tokens、cache_creation_tokens、cache_read_tokens——僅當 harness 報告對話層級使用量時才會填入;Claude/Cursor 會將其留為NULL)。主鍵(harness, session_id)。messages——每輪對話一行。message_id = '{session_id}#{ordinal}'(在所有 harness 中一致)、parent_id、ordinal、role('user'|'assistant')、text(完整內容;不包含思考過程)、model(每條訊息,僅限 Claude)、ts、first_seen_at(stockroom 首次觀察到該訊息時),以及四個 TokenBIGINT欄位:input_tokens、output_tokens、cache_creation_tokens、cache_read_tokens(Claude 的訊息使用量;Cursor 為NULL)。主鍵(harness, session_id, message_id)。session_token_usage——基於 sessions 與訊息 Token 總和的 VIEW。進行對話層級彙總時優先使用此檢視。欄位:*_from_messages(SUM)、*_native(session 欄位)、*_total(COALESCE(native, from_messages))、token_grain('session'|'message'|'none')。總數是倉庫對回報欄位的彙總,並非廠商發票。請勿在*_total之上再SUM訊息 Token。tool_calls——僅包含工具輸入(絕不包含輸出)。message_id(發出該呼叫的輪次)、ordinal、tool_name、tool_input(異質JSON,完整儲存——請參閱防護機制)。主鍵(harness, session_id, message_id, ordinal)。embeddings——用於語意搜尋的每個區塊向量(owner_table、owner_id、chunk_index、embed_model、vector FLOAT[384])。您很少直接查詢此表——請使用sr-semantic技能。_sync_state——擷取水印的記帳資訊;查詢時不感興趣。
在撰寫聯結前值得了解一個身分規則:始終在統一的 message_id / (harness, session_id) 上進行聯結,絕不使用 source_* 來源欄位。一個值如果在每個 harness 中只存在於一種粒度,那麼對另一種粒度來說它就是 NULL(例如 messages.model 僅限 Claude;sessions.models 僅限 Cursor;Token 欄位也遵循相同的雙粒度誠實原則)。
工作範例
所有範例均已在真實倉庫中驗證。
# 有哪些 harness 存在?(tsv 預設)
stockroom query "SELECT DISTINCT harness FROM sessions ORDER BY harness"
# 每個 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-from-the-scan>'"
# 為使用者/工具提供結構化輸出:
stockroom query --format json "SELECT harness, session_id, title FROM sessions WHERE title IS NOT NULL LIMIT 5"
# 每場對話的 Token 彙總(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"
更多變體(無界工具與活動時間窗、每日/harness Token 彙總、每個 harness 的技能使用 SQL):請參閱食譜。
食譜
進階入門 SQL:references/cookbook/。開啟索引,然後只找您需要的配方。
回報給人類
您是工具的操作者,而非顯示器。執行 SQL,讀取結果,然後以自然語言回答使用者。除非使用者要求,否則不要將原始 tsv 貼給人類。當他們確實想要原始輸出——或一個可自行執行的命令時——提供 --format table(供閱讀)或 --format json(供處理)的變體。
理解系統
想了解為什麼這些合約會是這樣的形式——打包方式、火炬合約、唯讀架構、截斷原則、身分/來源——請閱讀共用的系統模型:../sr-search/references/system-model.md。