mcpField
N
nexusmem

nexusmem

ローカル
nexus-mem.vercel.appメモリ・ナレッジ154 週間前に更新

nexusmem について

nexusmemは、シェル履歴、Gitの変更、プロジェクト文書をローカルSQLiteに保存し、コーディングエージェント向けに順位付けして検索します。コマンドの終了コードと出所を記録し、失敗と後の修正を関連付けることもできます。

主な機能

  1. 01コマンド・終了コード・Git差分・文書を索引化
  2. 02関連度と新しさによる検索順位付け
  3. 03コマンド失敗時に過去の試行を提示
  4. 04失敗と後の修正を任意で関連付け
  5. 05ローカルSQLiteへの保存

活用シーン

  1. 01失敗したコマンドの再発見
  2. 02コーディングエージェントへのプロジェクト履歴提供
  3. 03失敗コマンドと修正の追跡

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"yaminbkk-nexusmem": {
"command": "npx",
"args": [
"-y",
"nexusmem"
]
}
}
}
起動コマンドnpx -y nexusmem

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
npmnexusmem0.10.5

ツール

6

掲載時のツール一覧 · 2 週間前に更新

パラメーター型必須説明
asOfstringいいえISO-8601 の日時。この時刻以前に記録されたノードに結果を限定します。「その時点で記憶に何があったか」であり、「その時点で何が起きたか」ではありません。省略すると通常の制限なし読み取りになります。
querystringはい自由記述の質問または検索語
budgetintegerいいえ返されるコンテキストブロックの最大トークン数。デフォルトは 2000。
allProjectsbooleanいいえprojectRoot だけでなく、このマシンで NexusMem が実行されたすべてのリポジトリを検索します。答えが別のプロジェクトにある可能性がある場合に使用してください(別の場所で解決済みのパターン、以前に同じ形で失敗したツールなど)。各結果にはリポジトリがタグ付けされます。
projectRootstringはいリポジトリルートの絶対パス

詳細

NexusMem

CI npm npm downloads License: MIT Node yaminbkk/NexusMem MCP server Listed on AiList

NexusMem:init、sync --github、およびこのリポジトリ自身の履歴に対するクエリ。実際の issue、それを解決した PR、そしてリリースされたコミットを表示します

コーディングエージェントは git log を読むことができます。しかし、先週の火曜日に試してうまくいかなかった4つのことは読めません。

NexusMem は、実際にマシン上で起きたこと(シェルコマンドとその終了コード、変更した各ファイルのパッチまで含む Git 履歴、プロジェクトのドキュメント、任意でアシスタントのトランスクリプト)をローカルの SQLite データベースに記録し、必要に応じてランク付けされ、トークン予算に合わせた履歴の一部を返します。すべてディスク上に保存されます。アカウントも、クラウドも、テレメトリーもありません。

注目すべきなのはシェル履歴です。Git はエージェントに何がリリースされたかを伝えます。シェル履歴は、何がどの順序で試され、どのコマンドがゼロ以外の終了コードで終了したかを伝えます。この情報はほかのどこにも存在せず、ターミナルのスクロールバックが上書きされると消えてしまいます。

NexusMem の仕組み:Git のコミット、実際の終了コードを伴うシェルコマンド、ドキュメント、GitHub のスレッドがローカルの SQLite インデックスに取り込まれます。そこからランク付けされ、トークン予算に合わせた履歴の一部が返されるため、コーディングエージェントは推測ではなく実際の引用を添えて回答できます

目次: 試してみる · 正確なシェル記録 · 失敗から修正へのつながり · 検索の仕組み · セッションの要約 · GitHub の issue と PR · エージェントから使う · 必要なコスト · 古さと出所 · 動作しない場合 · コマンド · プロジェクトをまたいだ想起 · ディスク上のデータ · 開発

試してみる

任意の Git リポジトリ内で、次を実行します。

npx nexusmem init
npx nexusmem sync

次に、何か質問してみてください。このリポジトリからの実際の出力です。5 件中、上位 2 件を表示しています。

$ nexusmem query "windows spawn failure"

Relevant history for: windows spawn failure

- 2026-08-09 [observed] fix: distinguish a failed git spawn from "not a git repository"
  readRepoInfo collapsed three unrelated failures into one error: git running and reporting
  the path is not a work tree, git not being installed, and the process failing to spawn at
  all. Dogfooding hit the third case in two separate sessions...
- 2026-08-09 [authored] README.md — Before a tagged release
  - [ ] Retry on transient process-spawn failures on Windows

[observed]/[authored] は出所タグです(鮮度と出所を参照)。 コミットは直接観測されたイベントであり、ドキュメントのセクションは古くなる可能性のある記述です。

指定したトークン予算の範囲内で、コミットとドキュメントのセクションが同じ基準でランク付けされます。 出力の過程でモデルが要約することはありません。ランカーが送信しない情報を決めるだけです。(任意のソースであるセッションの要約ではローカルモデルが実行されますが、それは取り込み時だけであり、出力時には実行されません。検索対象は常に保存されたテキストです。)

実際に何が蓄積されるのかを見てみましょう。2 日後のこのリポジトリで nexusmem status を実行した結果です。

527 node(s)  2026-08-08 .. 2026-08-09
       321  shell_command
       130  conversation_turn
        60  doc_section
        16  git_commit

コミットは 16 件です。シェルコマンドは 321 件です。コミットは、ターミナルを使えるエージェントならすでに取得できていました。それ以外は取得できませんでした。

この conversation_turn の行が表示されるのは、--conversation を指定してこのコーパスを同期したためです。アシスタントのトランスクリプトは、デフォルトではオフで、明示的に有効にするまでオフのままになる唯一のソースです。貼り付けられた認証情報が含まれている可能性が最も高いためです。デフォルトのインストールでは、Git のコミットとその差分、シェル、ドキュメントがインデックス化されます。

必要条件:Node 22 以降と Git。better-sqlite3 には Node 20 用のビルド済みバイナリがなく、Node 20 は 2026 年 4 月にサポート終了となるため、Node 20 は動作しません。Ollama は任意で、セマンティック検索にのみ影響します(以下を参照)。

オプション:シェルコマンドを正確に記録

履歴ファイル(PSReadLine、.bash_history、.zsh_history)をスクレイピングしても、コマンドのテキストが得られるだけで、それ以外の情報はほとんど得られません。フックを使うと、作業ディレクトリ、終了コード、実際のタイムスタンプも記録できます。

nexusmem hook install

既存の PowerShell プロンプトを置き換えずにラップし、冪等に動作します。また、nexusmem hook remove で問題なく元に戻せます。

この機能をインストールする価値があるのは、終了コードが得られるからです。失敗したコマンドは成功したコマンドより強いシグナルであり、フックがなければ両者を見分ける方法はありません。

オプション:コーディングエージェントが試したこと

上記のシェルフックで取得できるのは、あなたが入力したコマンドだけです。コーディングエージェントが実行するコマンドは独自のツールを経由するため、対話型プロンプトには届きません。つまり、エージェントが遭遇した行き詰まり、最も覚えておく価値のあることを、NexusMem は把握できませんでした。

nexusmem agent install     # writes Claude Code's hooks; --project keeps it to this repo

その後、Claude Code を再起動してください。それ以降、NexusMem を実行したことのあるリポジトリでは、次のようになります。

  • 記録されます。エージェントが実行した各コマンド、その結果、およびその直前に編集されたファイルを記録するため、試行は「a.ts を編集した後、npm test が失敗した」と記録され、「npm test が 2 回失敗した」とはなりません。
  • コマンドが失敗すると、そのコマンドがここで以前にも失敗したことがあるか、毎回何が編集されたか、最終的に何が解決したかを、その時点でエージェントに伝えます。
  • セッションが始まると、バックグラウンドで同期し、修正が記録されていない失敗コマンドの名前を知らせます。

それ以外のときは静かなままです。一致しなければメッセージはありません。失敗ごとにセッションあたり最大 1 件、セッションあたり最大 5 件の通知であり、この処理では埋め込み、モデル、ネットワーク呼び出しはありません。NexusMem が見つからない場合や壊れている場合、フックは何も出力せずに 0 で終了し、エージェントはインストールされていないかのように処理を続けます。

コマンドは書き込まれる前に、シェルフックとまったく同じように秘匿情報が取り除かれます。また、照合は未加工のコマンドのハッシュに対して行われるため、秘匿情報だけが異なる 2 つのコマンドが同じものとして扱われることはありません。

nexusmem agent status ではフックがインストールされているかどうかを確認でき、nexusmem agent remove では NexusMem 自身のエントリーだけを削除し、ほかのツールのフックには手を加えません。

Claude Code が実行されるのと同じ環境からインストールしてください。設定ファイルに書き込まれるのは、Node 実行ファイルの絶対パスと、この NexusMem のコピーへの絶対パスを含むコマンドそのものであり、Claude Code はその文字列を自身の環境にあるシェルに渡します。Claude Code が Windows バイナリなのに WSL 内でインストールした場合(またはその逆の場合)、フックは設定され、インストール済みに見えますが、何も取得できません。フックを実行する側でパスを解決できず、起動できないフックはどこにもエラーを出さないためです。nexusmem agent status は、インストールされたコマンド内に存在しないパスがあれば、そのパスを要求したマシン上で表示します。また、参照先の NexusMem を移動した場合、別の場所にアップグレードした場合、またはアンインストールした場合にも、そのように表示されます。

失敗 → 修正の連鎖(オプトイン)

nexusmem sync --link-failures

通常の同期後、これは失敗した shell_command(終了コードが 0 以外)をすべて調べ、後から解決したものを探します。独立した 2 つのヒューリスティックを使います。1 つ目は、同じプロジェクトかつ同じ作業ディレクトリで、正規化されたテキストが完全に一致し、24 時間以内に終了コード 0 で実行された後続コマンドを探します(同一コマンドの再試行)。2 つ目は、近接する会話ターンまたはセッションの要約の中から、失敗したコマンドの重要な単語がすべて含まれているものを探し、1 語だけの一致では不十分とします(会話ブリッジ)。失敗は、どちらか一方、両方、またはどちらにもリンクされないことがあります。

どちらのリンクもクエリ結果に表示されます。会話ブリッジのヒューリスティックは当初、共有する単語が 1 つでもあれば一致としていましたが、このリポジトリの実際の履歴で試したところ、リンクの約半数が誤りでした。「npm」のような一般的な単語を 1 つ共有するだけで、無関係な議論がリンクされてしまっていたのです。重要な単語をすべて要求するようにしたことで、これが解消されました。同じコーパスで再度試したところ、生成されたリンクはすべて(サンプルではなく、生成されたリンク全件)、要約だけでなく全文を手動で確認した結果、正しいことが確認されました。

リンクされた失敗が結果セットに含まれると、その修正も直後に表示され、修正自身でクエリに一致する必要はなく、失敗自身の関連度スコアを引き継ぎます。これが目的です。何かが失敗した理由を尋ねるクエリで、その修正に使われた言葉を別途推測する必要はありません。プロジェクトをまたいでも機能します。query --all-projects は、リンクが見つかったプロジェクトのデータベースを使って失敗と修正を連鎖させます。リンクは常に、見つかったプロジェクト内でのみ記録されるためです。

$ nexusmem query "why did npm whoami fail"

- 2026-08-12 [observed] shell: npm whoami  (exit 1)
- 2026-08-12 [observed] shell: npm login   (exit 0)  -- linked as the fix

検索の仕組み

すべてのソースは同じ MemoryNode 形式に正規化されるため、コミット、シェルコマンド、ドキュメントのセクションが同じ条件で競合します。検索では、FTS5 に対して BM25 を実行し、埋め込みモデルにアクセスできる場合は sqlite-vec に対してベクトル検索を実行します。これらを Reciprocal Rank Fusion で統合します。対象にするのは順位の 位置 のみで、生のスコアは使いません。BM25 のコストとベクトル距離は尺度に互換性がなく、上限も異なるため、両者で一致するのは位置だけです。

次に、ランキングでは3つの要素を乗算します。

score = relevance × signal^0.215 × recency^0.288

relevance はクエリに基づきます。signal(fix: コミットは chore: より上位、失敗したコマンドは成功したコマンドより上位)と recency は、クエリが存在する前から適用される事前要素です。各要素は [0, 1] ではなく [floor, 1] に収まるため、1つの要素が弱くても、強い一致がゼロになることはありません。

指数によって、signal と recency が 合わせて relevance を覆せる範囲が制限されます。全範囲を通じて最大2倍で、事前要素ごとではなく、共同で適用されます。これは意図的な設計です。スコアでは2つの事前要素を 乗算 するため、それぞれを個別に2倍までに制限しても、組み合わせると4倍になり、特に活発な作業日の新しくシグナルの強いコミットに大きく影響していました。このバグが明らかになったのは、クエリに実際に答えるドキュメントのセクションより、同じ日に作成された関連性のない fix: コミットが2件上位になったときです。詳しい導出については retrieval/rank.ts を参照してください。

Ollama がない場合はベクトル検索がスキップされ、BM25 のみが使われます。これは完全にサポートされており、機能が低下した状態ではありません。sync と query はどちらも正常に完了し、単に処理が少なくなるだけです。

セッションの要約(任意、ローカルモデル)

sources.session.enabled を設定すると、終了した各セッションから、未加工のやり取りに加えて要点をまとめたノードが1つ作成されます。個々のやり取りを40件並べるのではなく、何が決まり、その理由は何かをまとめます。ローカルの Ollama チャットモデル(デフォルトは qwen2.5:3b)を使います。自動でダウンロードされることはなく、データがマシンの外部に送信されることもありません。

nexusmem scan-session --dry-run

これを実行すると、セッションが生成する正確なプロンプトを、モデルを呼び出さずに、秘匿情報の除去と予算に合わせたトリミングを行った後の状態で出力します。

コストを抑える仕組みは3つあります。セッションの要約は、settleMinutes(デフォルトは30分)にわたって更新がない場合にのみ行われるため、進行中のセッションが同期のたびに再要約されることはありません。プロンプトはハッシュ化され、ハッシュが変わっていなければモデルの呼び出し全体がスキップされます。このリポジトリでは、要約済みセッションが14件ある状態で安定後に同期すると、モデルを呼び出すことなく0.25秒で完了します。また、maxSessions(デフォルトは10)によって、実行ごとにモデルへ送るセッション数が制限されます。残りはキューに入ったものとして報告され、次回の同期で処理されます。

qwen2.5:3b を使って実際のセッション14件で測定した結果。 要約の内容は良好で、プロンプトの指定どおりの形式で、決定事項とその理由がまとめられています。一方、タイトルの信頼性は低く、モデルが使えるタイトルを返したのは約3分の1でした。それ以外では、会話形式の前置き、余分な箇条書き、または「Summary of the Session」だけのタイトルが生成されました。これらは採用されず、タイトルにはセッション開始時の質問の最初の行が使われます。洗練された表現ではない場合でも、必ず具体的な内容になります。長いセッションや、英語以外の文字起こしでは指示への準拠が特に悪くなりました。タイトルの品質を重視する場合は、より大きなモデル(qwen2.5:7b)を使用してください。sources.session.model を設定します。

GitHub の issue と PR(任意)

sources.github.enabled を有効にすると、このリポジトリの github.com リモートにある各 issue と PR が、それぞれ 1 つのノードになります(タイトル、最初の投稿、すべてのコメントをまとめたもの)。これは、ディスカッションがシェル/会話履歴に残す生のやり取りと並んで追加されます。デフォルトでは無効です。機密性の問題ではなく、実際の外部依存関係を持つ最初のソースだからです。gh CLI 経由で読み取るため、gh のインストールと認証(gh auth login)が必要で、ディスク上にすでにある情報を読むだけでなく、ネットワークにリアルタイムでアクセスします。github.com のリモートがないリポジトリでも、gh が未認証でも、どちらの場合も何もせずに終了します。

nexusmem scan-github

他の scan-* コマンドと同様に、同期によって作成されるノードをプレビューします。maxThreads(デフォルトは 100)と maxCommentsPerThread(デフォルトは 100)で、1 回の同期にかかる処理量を制限します。since は独立したカーソルとして追跡されるため、繰り返し同期すると、変更されたスレッドだけを再読み込みします。このリポジトリ自体の実際の issue/PR 14 件で試したところ、取り込みは 1 秒未満で完了し、「labelled retrieval regression corpus」で検索すると、依頼した issue #8 が正しく最上位に表示されました。

エージェントから使用する

{
  "mcpServers": {
    "nexusmem": {
      "command": "npx",
      "args": ["-y", "nexusmem", "mcp"]
    }
  }
}

stdio 経由で 3 つのツールを提供します。search_memory はパックされたコンテキストブロックを返し、sync_project は取り込みを実行し、get_status は現在記憶されている内容を報告します。MCP ツール呼び出しにはシェルの作業ディレクトリが含まれないため、各ツールには明示的な projectRoot を指定します。リポジトリがまだセットアップされていない場合、sync_project が代わりに init を実行します。

コスト

このようなツールでは2つの数値が混同されがちなため、ここでは分けて扱います。

パッカーの効率は、ランカーが自身の候補セットをどれだけ絞り込むかを示します。このリポジトリのコーパスでは81~84%です。ランカーの調整には役立ちますが、請求額についての主張には使えません。基準が仮定上のものだからです。NexusMemがなければ、その候補がそもそもコンテキストウィンドウに入ることはありませんでした。

エンドツーエンドの削減率は、NexusMemが実際に送信するパック済みコンテキストと、クエリに関連すると自身のランキングで特定した同じファイルをすべて読む場合とを比較したものです。 scripts/benchmark.ts(npm run bench)を使って測定しています。このリポジトリをクローンし、同期済みのコーパスを指定すれば、誰でも最初から再実行できます。

コーパスコミット数クエリセットファイル内容全体との比較対象ファイルの git log -p との比較
このリポジトリ62このプロジェクト自身の履歴からそのまま引用した実際のプロンプト16件95%(中央値94%)98%(中央値97%)
vitejs/vite9,567機械的に抽出した16件 — 詳細は後述99%(中央値98%)約100%(中央値約100%)— ただし注意点あり

どちらも当初の目標である「完全なコンテキストを送信する場合と比べてAPIトークンの消費を70%超削減」を達成しています。また、viteでの測定は、この目標が当初から適用対象として説明されていた規模での初めての測定です。

どちらの数値を引用する場合も、まず測定方法をお読みください。見た目より限定的な主張です。

  • NexusMem自身のランキングを基準にしています。 外部の正解データとは照合していません。対象ファイルは、各クエリのパック済みノードが触れるファイルです。これは、検索ですでに候補セットが選ばれた後にパック処理でどれだけ削減できるかを測るものであり、そのセットが適切だったかどうかを独立して検証するものではありません。
  • クエリセットは機械的に作成しており、都合よく選んだものではありません(scripts/benchmark.tsを参照)。viteのセットは、説明が十分なfix/feat/perf/refactorコミットと、根拠の説明を含むドキュメント見出しから均等にサンプリングしています。このリポジトリのセットは、過去に使われた実際のプロンプトをそのまま再利用しており、そのいくつかは限定的な質問ではなく幅広いタスクの指示です。これが、このリポジトリの数値がviteより低い理由の一つです。
  • git log -pを基準にすると、膨大な量になることがあります。 viteのあるクエリでは、解決されたファイルセットに含まれるファイルの履歴だけで750万トークンに達しました。この規模では、「代わりにファイルの履歴だけを読めばいい」という選択肢は、もはや現実的ではありません。
  • 以前の約40%という数値に代わるものです。 この数値は、このリポジトリだけを対象に、手作業で選んだ2つのクエリを、明記されていない基準と比較して手作業で集計したものでした。誤りではありませんが、条件が十分に定義されていませんでした。測定方法を明示し、再現できるスクリプトを用意した今回の数値に置き換えます。

割合では表せないものもあります。シェルコマンドや会話のターンには、安価に使えるgrep相当のものがありません。記録する仕組みがなければ、それらは見つけにくくなるのではなく、失われます。

これらの数値を類似ツール自身の主張と比較する方法については、docs/competitor-comparison.md(projectmemとの比較)およびdocs/competitor-comparison-yesmem.md(YesMemとの比較。ネイティブのWindows対応と、YesMemが文書で要件としているWSL2との比較を含む)を参照してください。

約530ノードのコーパスをウォーム状態で実行した場合の、10回の実行におけるp50レイテンシー:

操作
BM25検索(FTS5)約1.1 ms
ベクトルKNN(sqlite-vec)約3.2 ms
統合、ランキング、パック約0.6 ms
クエリの埋め込み(ローカルOllama)約55~77 ms
ハイブリッド処理のエンドツーエンド約56 ms

SQLiteの処理時間は合計で約5 msです。この経路で最適化に値するのは埋め込みの呼び出しだけであり、それは別のプロセスが担っています。

鮮度と出所

メモリレイヤーに必要なものが2つありますが、このメモリレイヤーではまだ一部しか実現できていません。観測された事実と推測を見分ける方法、そして何かに矛盾した結論を取り下げる方法です。このセクションでは、実現できていることと、できていないことを説明します。

すべてのノードにはprovenanceがあり、取り込み時にコレクターごとに設定される4段階の信頼階層を示します。observed(実際に取り込まれたコミット、シェルコマンドの実際の終了コード)> authored(ドキュメントのセクション — 人間自身が書いた主張)> recorded(会話のターン — 一語一句そのままですが、イベントそのものではなくイベントについて話したもの)> derived(セッションの要約 — モデルによる要約)です。この段階は各クエリ結果にタグとして表示され、検索の重みを減衰させます。信頼度が低いほど、時間がたつにつれてランキングから早く消えていきます。この順序は設計上の主張であり、正確な減衰率は測定された最適値ではなく、判断によるものです。

nexusmem stale

observed以外のノードのうち、古くなっても(デフォルトでは45日以上)現在も有効だと確認されていないものを一覧表示します。これは内容ではなく、経過時間と出所に基づくヒューリスティックです。何も書き込みません。どの候補が実際に誤っているかは、ユーザーが判断します。SLMがすでにフラグを付けた候補(下記参照)には、その時点でのlikely superseded by候補が表示されます。これを読むコストはかからないため、この通常のコマンドはオフラインでも即座に実行できます。

nexusmem mark-stale <oldNodeId> --supersedes <newNodeId>

newNodeIdをoldNodeIdの置き換え先としてリンクします。以降、ランカーは古いノードの重みを下げます(クエリは可能なままですが、通常は置き換え先より順位が下がります)。forgetとは異なり、何も削除されません。

nexusmem stale --check-contradictions

各候補について、最も類似度の高い新しいノードを(ローカルの埋め込み検索で)見つけ、ローカルのSLM(デフォルトではOllamaのqwen2.5:3b)に、実際に古いノードと矛盾しているかどうかを尋ねます。単なる経過時間ではなく、内容を実際に比較します。一致した場合は、候補の下にlikely superseded by <id> <title> -- <reason>と表示されます。どちらの判定も記録されるため、一度判定したペアが再びモデルに送られることはありません。ほかには何も書き込まれません。supersedesの設定は引き続きユーザーがmark-staleで行います。

これはsync中にも自動で実行されます。1回の実行につき新たな判定は最大3件です(.nexusmem/config.jsonのcontradictionsブロックで設定可能。無効にするにはautoCheck: falseにします)。埋め込みプロバイダーに接続できた場合に限り実行され、判定結果が記録されるため、2回目以降の同期ではコストがかかりません。新しい候補と未解決の候補は、同期の概要、nexusmem status(flaggedの行)、および通常のnexusmem staleに表示されます。

この機能でできないこと: これは小規模なモデルが、1つの古いノードと新しいノードのペアについて二者択一で判定するもので、検証済みの事実ではありません。一致した場合は結論ではなく、確認すべき手がかりとして扱ってください。また、候補を比較する対象は埋め込みの類似度で見つかったノードに限られます。関連性がなさそうなノードによる矛盾が見つかることはありません。包括的な矛盾の検出(ベクトル検索でたまたま見つかったペアだけに限らないこと)は、依然として未解決の問題であり、この機能が独自にノードを置き換えることはありません。

provenanceはtrust_stateとは別の問いに答えるものです。provenanceが示すのは主張の出所であり、誰かがそれを確認したかどうかではありません。

nexusmem review <nodeId> --verify
nexusmem review <nodeId> --reject

上記のSLM矛盾チェッカーとは独立して、1つのノードに対するユーザー自身の判定を記録します(SLM矛盾チェッカーが書き込むのは候補だけであり、判定ではありません)。--rejectはランキング内でノードの重みを下げます。これはmark-staleと同じく、降格はしても削除はしないルールです。ノードはクエリ可能なままですが、通常はより適切な一致候補より順位が下がります。その後、そのノードを返すすべてのクエリ結果に、どちらの判定も[verified]/[rejected]タグとして表示されます。--verifyはラベルを付けるだけで、ランキングは上がりません。すべてのノードは確認されるまでcandidate(タグなし)で始まり、再同期によって一度設定された判定が上書きされることはありません。

うまくいかない場合

  • フックを使わないシェル履歴には、スコープがありません。 収集された履歴にはディレクトリのコンテキストがないため、sync を実行したリポジトリに紐付けられます。対象は直近の一定範囲に限られ、保証ではなく近似値です。
  • 日本語と中国語はベクトル処理に依存します。 FTS5 の unicode61 トークナイザーは空白で分割するため、単語間にスペースを使わない言語では、BM25 による有用な再現率が得られません。
  • リベースするとノードが取り残されます。 書き換えられた履歴により、到達不能なコミットに対応するノードが残ります。実際のイベントを記述しているため誤りではありませんが、対象を絞ったプルーニングはまだできません。sync --rebuild を実行すると、クリーンな再スキャンが強制されます。
  • 複数行の PowerShell 入力は、別々のコマンドとして読み込まれます。 プロンプトで複数行にわたって入力した関数は再構築されません。
  • スクレイピングによるフォールバック ID は、同期の間に履歴ファイルの先頭が削られるとずれます。 フックをインストールすると解決します。
  • セッション要約のタイトルは、モデルが指示に従うかどうかに左右されます。3B モデルでは、指示に従わないことがよくあります。フォールバックでは汎用的なタイトルではなく、具体的なタイトルを維持しますが、予想される結果については上のセクションをご覧ください。
  • 埋め込みモデルを変更すると、すべてが再埋め込みされます。 異なるモデルのベクトルは比較できず、nodes_vec には行ごとの出所情報も記録されていません。そのため sync は混在したベクトルを対象にランキングするのではなく、すべてを破棄して再構築します。この処理が発生すると、その旨が表示されます。ノードには手を加えず、その間も BM25 は機能し続けます。
  • 差分のインデックス作成には上限があり、意図的に情報を失います。 初回の同期では直近 200 件のコミットのパッチをインデックスに登録し(以降の同期では cursor..HEAD のみをたどります)、マージコミットは対象になりません。マージコミットのパッチは、このパーサーが読み取らない結合形式でしか存在しないためです。また、依存関係の更新でコーパスが埋もれないよう、バイナリ、ロックファイル、ビルド出力はスキップされます。それでも、すべて git_commit ノードとして記録されます。パッチが limits.maxBodyChars より長い場合は切り詰められるため、非常に大きな変更の末尾はインデックスに登録されません。上限は config.json の sources.diff にあります。
  • プロジェクト横断の検索では、幅広さが優先されます。 各リポジトリの検索結果は順位で統合されるため、最良の一致が平凡なプロジェクトでも、順位 1 の項目が加わります。また、どのリストでも順位 1 の価値は同じです。質問に関係する情報が少ないリポジトリを追加しても、その結果の一部が予算に割り当てられます。シグナル、新しさ、予算によって抑制していますが、プロジェクトごとの品質ウェイトはありません。
  • プロジェクトレジストリはインデックスであり、真実の情報源ではありません。 移動または削除されたデータベースを参照している場合があります。そのようなデータベースは報告され、スキップされますが、暗黙にプルーニングされることはありません。マウント解除されたドライブは、削除されたプロジェクトとは限らないためです。
  • 会話のチャンク分割は評価されていません。 長い返信を見出しの境界で分割すると測定上の改善がありましたが、体系的にテストされたことはありません。
  • 1 つの結果に含まれるチャンク化ノードの兄弟数は、調整済みではなく上限が設けられています。 conversation_turn と doc_section は、1 つの返信またはファイルを複数のノードに分割します。パッキングされた結果には、そのうち最大 2 つまでしか同時に含まれません。実環境で確認された例では、「token」を検索すると、上位 12 件のうち 9 件が、細かくセクション分割された 1 つの返信の異なる部分で占められ、実際に答えを含むノードが押し出されました。上限の 2 は判断によるもので、上記のランキング事前分布の予算と同様、測定によって最適化された値ではありません。
  • 事前分布の予算の大きさは判断によるもので、測定によって最適化された値ではありません。 事前分布は個別ではなく共同で上限を設けるようになり、実際に存在した 4 倍の抜け穴が解消されました(ランキングのセクションを参照)。ただし、2 倍の予算そのものは、ラベル付き関連性データセットに対して調整されたことがありません。そのようなデータセットは存在しません。これは妥当な定数であり、測定結果ではありません。測定されているのは改善の方向です。このリポジトリ自身のメモリを対象に実際の 4 つのクエリを実行したところ、共同上限に切り替えることで、3 つのクエリでは質問に答えるセクションの順位が上がりました(「ベクトル検索の前に BM25 を使う理由」の根拠を説明するセクションは、順位 4 から順位 1 になりました)。また、どのクエリでも正解の最上位ヒットが押し出されることはありませんでした。
  • forget はリポジトリ単位であり、グローバルではありません。 拒否リストは、コマンドを実行した 1 つの .nexusmem/memory.db に保存されます(さらに、同じスコープで --prune-source が処理する、そのリポジトリの古い事前分布 ID も対象です)。複数のリポジトリのシェル履歴に漏れた値には、リポジトリごとに forget を実行する必要があります。同期したすべてのプロジェクトに適用される、マシン全体の共有拒否リストはありません。
  • 拒否リストは、クローンまたは復元だけでは引き継がれません。 .nexusmem/ は設計上 gitignore されているため、deny_list は git clone/git push では引き継がれません。一方、履歴そのものは新しい sync で再構築される情報であり、すべてのクローンにそのままコピーされます。チームメイトが新たにチェックアウトした場合、新しいマシンを使う場合、またはバックアップを復元した場合、保護はゼロから始まります。忘れた値は最初の同期で再び取り込まれます。これはコードの理論的な読み取りだけではなく、2026-08-17 に実環境で確認されています。forget --export <path>/forget --import <path> でこの問題を解決できます。エクスポートすると有効なエントリがプレーンテキストの JSON ファイルに書き込まれるため、管理下にある経路で移動してください(このファイルは保持する値と同じ機密性を持つため、git は絶対に使わないでください)。インポートすると、新しいチェックアウトでエントリが再適用され、すでに再同期されていたコピーも削除されます。これは意図的に手動で行う仕様であり、sync のたびに自動実行されるものではありません。

コマンド

init、sync、query <text>(時系列の両側面を考慮した読み取りには --as-of <date> を追加します。以下を参照してください)、status(どこかに貼り付けられるプレーンテキストの要約には --share を追加します)、projects、mcp、forget <value>、scrub-secrets(バージョン 0.10.5 より前にすでに保存されたシークレットを再度マスキングします。対象はデータベースの行、FTS インデックス、埋め込み、シェルフックのログです。デフォルトではドライランで、--yes を指定すると最初に各データベースをバックアップします。--all-projects を指定すると、登録済みのすべてのリポジトリが対象になります)、 stale(ローカル SLM によるコンテンツチェックには --check-contradictions を追加します。上記を参照してください)、mark-stale <nodeId> --supersedes <newNodeId>、review <nodeId> --verify|--reject(1 つのノードに対する人間の判定を記録します。上記を参照してください)、precheck(助言目的です。未解決の過去の失敗や最近の変更頻度の高さがあるステージ済みファイルについて警告します。--strict を指定しない限り、終了コードは 0 です)、hook install|remove|status(PowerShell の終了コードフック)、hook git install|remove|status(各コミットの前に precheck を実行する git の pre-commit フック)、および hook git-post install|remove|status(各コミット後に、埋め込みを含む完全な sync をバックグラウンドで実行する git の post-commit フックです。切り離して実行するため、git commit 自体を待たせることはありません。コミットが連続して発生した場合は、積み重なるのではなく、sync --auto のロックによって 1 回の同期にまとめられます)。agent install|remove|status は上記の Claude Code フックを管理します。agent recall と agent session-start は手動ではなく、これらのフックによって実行されます。

また、ドライランプレビューが 8 つあります(scan-git、scan-diff、scan-shell、scan-docs、scan-conversation、scan-session、scan-github、scan-structure)。これらは何も書き込まず、取り込みによって生成される内容を出力します。最初の 7 つはノードとそのシグナルスコア、scan-structure はインポートグラフのエッジを出力します。実際のリポジトリでスコアリングを調整し、変更を確定する前に確認するための想定された方法です。どこかに渡すには --json を追加します。

すべてのコマンドで -C <path> を指定して、別のリポジトリを対象にできます。sync では、--conversation と --github を指定すると、設定を保存せずに、その実行に限って各オプトインソースを有効にできます。--no-embed はベクトル処理をスキップし、--link-failures は上記で説明した失敗 → 修正の連鎖を構築し、--rebuild はプロジェクトのノードを削除して最初から再取り込みします。

sync --prune-source <name> はコレクターのソース全体(例:shell:pwsh)を削除します。より細かな操作として、forget <value> は完全一致する文字列(または --regex パターン)に一致するすべてのノードを削除し、値が後の sync --rebuild によって追記専用のシェルフックログや完全なトランスクリプトスキャンから再読み込みされる場合でも、二度と再取り込みされないよう恒久的な拒否リストのエントリーを書き込みます。どちらの削除でも、忘れた内容そのものではなく、ハッシュのみのトゥームストーンが残ります。どちらもデフォルトではドライランで、--yes で確定します。forget --list は有効なエントリーを表示し、forget --export <path> / forget --import <path> は同じリポジトリの別のチェックアウトにエントリーを引き継ぎます(上記の制限を参照してください)。この仕組みが存在する理由については、docs/forget-mechanism.md を参照してください。

プロジェクトをまたいだ想起

query --all-projects は、現在のリポジトリだけでなく、NexusMem を実行したすべてのリポジトリを検索し、各結果にそのリポジトリを示すタグを付けます。

$ nexusmem query --all-projects "why was the retry budget raised"
scope   2 project(s): NexusMem, uploader

- 2026-08-12 [observed] [uploader] fix: raise the retry budget after the S3 upload timeouts
- 2026-08-12 [observed] [uploader] retry.ts @ 8d0f98b — fix: raise the retry budget after the S3 upload timeouts
  @@ -1 +1 @@
  -export const RETRY_BUDGET = 3;
  +export const RETRY_BUDGET = 5;
- 2026-08-09 [observed] [NexusMem] fix(git): retry a transient failure to spawn git

双時点読み取り

すべてのノードには2つの時計があります。イベント自体の時刻である ts(「そのとき何が起きたか」)と、ストアが実際に記録した時刻である created_at(「そのときストアには何が記録されていたか」)です。通常は同じ問いですが、同期が遅れて実行された場合や、イベントから数週間後にバックフィルが行われた場合、あるいはチームメイトのクローンが一度に追いついた場合は異なります。query/search_memory はデフォルトで前者に答えますが、--as-of <date> を指定すると後者に切り替わります。

nexusmem query "why does the ranker cap joint priors" --as-of 2026-08-10

その時点以前に記録されたノードだけが対象になります。ノードが記述するイベントがそれより古くても同様です。これに相当する書き込み機能はありません。これは created_at に対する読み取り時のフィルターであり、スナップショットでも、ノードは一度書き込まれると変更されないため、後から変わった値を問い合わせる方法でもありません。ノードの記録ではなく、ノードの重みを変えるものについては、陳腐化と来歴を参照してください。

データベースはリポジトリごとに保持されます。共有のグローバルストアはなく、あるリポジトリの .nexusmem/ を削除しても、そのリポジトリのメモリだけが削除されます。他のリポジトリを検索可能にするのは、init が書き込み、すべての sync が更新する ~/.nexusmem/projects.json のプレーンなインデックスです。nexusmem projects で内容を確認でき、--prune でデータベースが存在しないエントリを削除できます。

リポジトリをまたいだランキングでは、生の BM25 ではなく、プロジェクトごとの相互順位融合を使います。BM25 のコストは各データベース固有のコーパスに基づいて算出されるため、50ノードのデータベースと50,000ノードのデータベースでは意味が異なるからです。この選択のトレードオフについては、機能しなくなるケースで説明します。

MCP の search_memory ツールでも、allProjects: true と同じ切り替えを使用できます。

ディスク上の構成

<repo>/.nexusmem/
  .gitignore     '*' — the workspace ignores itself, so init never edits a file it doesn't own
  config.json    validated on read; a corrupt config fails loudly rather than silently
  memory.db      SQLite in WAL mode

~/.nexusmem/
  projects.json      which repositories exist, for cross-project recall; a corrupt one reads as empty
  shell-history.jsonl  the hook's log, if you installed it

NEXUSMEM_HOME を設定すると、ユーザー単位のディレクトリを上書きできます。

ノード ID は sha256(projectId + kind + naturalKey) からコンテンツアドレス方式で生成されるため、sync を2回実行しても重複は発生せず、カーソルを失った場合でも取り込みは正しく行われます。プロジェクト ID は、正規化されたオリジン URL がある場合はそこから取得し、ない場合は絶対パスを使用します。そのため、同じリポジトリのクローンは同じメモリ名前空間を共有します。

.nexusmem/ を削除しても、sync で再構築できるものが失われることはありません。

状況

取り込み、ハイブリッド検索、予算に応じたパッキング、MCP サーバーはすべて動作しており、Linux と Windows で、Node 22 および 24 を対象に実行する701件のテストでカバーされています。

開発

npm install
npm run typecheck
npm test
npm run build

テストはスナップショットベースではなく、動作を検証するもので、いくつかは実際に確認された特定の障害に対するリグレッションテストです。tests/git-errors.test.ts では偽の spawn を挿入して、任意のタイミングで発生させられない Windows のプロセス起動障害を検証します。

バグ修正、テストカバレッジの改善、小規模で焦点を絞った機能追加を歓迎します。ワークフローについてはCONTRIBUTING.mdを参照してください。取り組みやすく、範囲の明確な課題を探すには、good first issueラベルの付いたイシューをご覧ください。

開発の経緯

このプロジェクトは、コーディングエージェント向けのローカルなコンテキストメモリエンジンが実用になるかを、Claude Code を使って試作した実験として始まりました。コードの作成には AI 支援ワークフローを利用しましたが、アーキテクチャ、設計上の判断、仕様は人間が主導しました。

この点を明確にしておく価値があります。コードをどう読むべきかに関わることであり、信頼するかどうかを左右するものではないからです。監査、修正、PR を心から歓迎します。また、以前の仮定が誤りだと判明した場合も含め、各要素がなぜそのようになっているのかを、コミット履歴で意図的に詳しく説明しています。

ライセンス

MIT

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。