Posts

Claude Code のセッション履歴をベクトル検索できる QMD を入れた

TL;DR — QMD は BM25 + ベクトル検索でローカル Markdown を横断検索できる MCP ツール。Stop hook で Claude Code セッション終了時に自動インデックス化することで、複数リポジトリのセッション履歴を横断検索できる。

nanoclaw(Claude Code の fork)をアップデートしているとき、内部で QMD が使われているのを見かけた。調べてみたら「お、これ良さそう」となったので入れた。

QMD とは

QMD は、ローカルの Markdown ファイルを対象に検索エンジンを立てる CLI ツール兼 MCP サーバー。単純な grep と違うのは、BM25(キーワード検索)とベクトル検索を組み合わせて意味的に近いドキュメントを引けること。検索用の埋め込みモデルもローカルで動く(GPU がなくても動作する、が後ほど書くけど CPU だとクソ重い処理が一回だけある)。

インストール

本体は以下のように、npm でインストール。

npm install -g @tobilu/qmd

Claude Code はプラグインとして追加する。

claude plugin marketplace add tobi/qmd
claude plugin install qmd@qmd

今回やりたかったことと設定

自分は複数のリポジトリで Claude Code を使っていて、技術的な会話もリポジトリをまたいでいる。そこで、QMD のコレクションに全リポジトリのセッション Markdown を入れておけば、「あのリポジトリでやった話、なんだったっけ」を後から引ける。

同じようなことを実現できるものとして、claude-mem がある。

そもそもこれ入れればいいじゃんって話もあるけど、なんか分からんけど直感で QMD いいかもって思ってしまったので仕方ない。直感で突き進みます。

まずは、Claude のセッションをマークダウンに変換するスクリプト convert-sessions.js を用意して、~/.claude/hooks 配下に配置。

Claude Code の git push がまた壊れた:sandbox と credential helper のサブプロセス問題

TL;DR — Claude Code の sandbox が GNOME keyring への D-Bus アクセスをブロックするため git push が失敗する。gh auth login --insecure-storage でトークンをファイルに保存すれば回避したがこれでいいのか若干不安。

以前も同じ目に遭った。

そしてまた再現。今回はrtk 導入の記事をプッシュしようとしたら同じ目にあったよ。

またかよ、おい…

とりあえず Claude さんと一緒に解決したけど、これでいいのか正直わからん。

というか、公式で sandbox 関連の特集記事とか出してくれないかな。

以下、Claude さんの解説と俺の感想です。

TL;DR

内容
根本原因gh auth git-credentialgit push のサブプロセス)が GNOME keyring に D-Bus 経由でアクセスできない
なぜsandbox が Unix ソケットをブロックしている
excludedCommands: ["gh *"] が効かない理由Claude Code が直接呼ぶコマンドにしか効かず、サブプロセスには届かない
回避策gh auth login --insecure-storage でトークンをファイルに保存

何が起きたか

git push origin main を実行すると以下で失敗する。

fatal: could not read Username for 'https://github.com': No such device or address

まず excludedCommands"gh *" を追加してみたが効果なし。

Claude Code のトークンを節約する RTK を入れた

TL;DR — RTK(Rust Token Killer)は Claude Code の bash 出力をフィルタして LLM へのトークン消費を削減するプロキシ。rtk init -g だけで自動設定される。

ちょっと仕事が忙しくて疲弊してたんだけど、気分転換に前から気になってたツールを入れてみた。

効果はこれからに期待。

RTK とは

RTK (Rust Token Killer) は、Claude Code などの AI アシスタントが bash コマンドを実行したときの出力をフィルタして、LLM に渡るトークン数を削減する CLI プロキシ。git logls の出力って、そのまま渡すと無駄に長いことが多い。RTK はそれをコンパクトにまとめてから Claude のコンテキストに入れてくれる。公称で 60〜90% の節約効果があるらしい。

インストール

brew install rtk
rtk init -g

rtk init -g を叩くだけで、以下が自動設定される:

  • ~/.claude/RTK.md の生成(Claude Code に RTK の使い方を教えるファイル)
  • ~/.claude/settings.json への hook 追記(PreToolUse/Bashrtk hook claude が入る)

これ以降、Claude Code が bash を呼ぶたびに透過的に RTK を通るようになる。ユーザー側で何か変えなくていい。

LCP が 100% Good になった

TL;DR — Google Fonts をシステムフォントに切り替えた結果、Cloudflare の LCP が「100% Good」になり、ページ読み込み時間が 56% 削減(514ms)された。

やったね!

Cloudflare Web Analytics。LCP が 100% Good、ページ読み込み時間 514ms(-56.88%)

先日 Google Fonts をやめてシステムフォントに切り替えた記事を出してから今日早速確認してみたところ、大幅に改善されてた。いやほんと申し訳ない。海よりも深く反省しています。

LCP:100% Good。 ページ読み込み時間は 514ms で、改善前から 56% 削減。

このブログ、frontend-design スキルにすべて任せて既存テンプレートのデザインを改修してもらったんだけど、こういう重くなる要素に考えが至らなかった。

事象を確認してはじめて自分の失態に気づく、という結果だったので、改めて監視って大事だなと思いました(小並)。

Cloudflare Web Analytics の LCP がひどかったので Google Fonts をやめた

TL;DR — 日本語 Google Fonts(M PLUS Rounded 1c)は初回訪問者の LCP を大幅に悪化させる。システムフォントに切り替えれば外部フォントリクエストがゼロになる。

このブログの Cloudflare Web Analytics を眺めていたら、LCP(Largest Contentful Paint)の評価がなかなかひどいことになっていた。

Cloudflare Web Analytics の Core Web Vitals。LCP が Good 0%、Needs Improvement 50%、Poor 50%

体感では全然遅くないんだけどなぁ、と思いつつ、Claude さんに原因を調べてもらった。

結論:Google Fonts

Claude さん、さすがに早い。表題の通りの結論でした。

どうやら、デザインで使っていた、M+PLUS+Rounded+1c という日本語の丸ゴシックフォントが犯人。

グリフ数が数万文字規模になるので、ファイルサイズが英字フォントとは桁違いに重いとのこと。

詳しく説明してもらいました。

Hugo の設定を見たら、こういう記述があった。

googleFontsLink = "https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@300;400;700&family=JetBrains+Mono:wght@400;500&display=swap"

しかも外部 CDN(fonts.googleapis.com)へのリクエストが発生するので:

  1. DNS 解決
  2. TCP 接続
  3. TLS ハンドシェイク
  4. フォント CSS の取得
  5. 実際のフォントファイルの取得

という連鎖が初回訪問のクリティカルパスに入ってくる。LCP がテキスト要素(見出し)だったとすれば、フォントが読み込まれるまでレンダリングがブロックされる。

体感と計測がズレる理由

キャッシュだよ、キャッシュ。

Claude さんに言われて気づいたわ。

自分のブラウザに既にあるんだもん、そりゃ速いはずだよ。

Cloudflare Web Analytics は実際の訪問者のブラウザで計測した RUM(Real User Monitoring)データを集めている。

訪問者のほとんどは初回訪問でキャッシュなし。

Lighthouse スコア 64 の正体を調べたら、シミュレーションだった話

TL;DR — Lighthouse のスコアは「低速 4G シミュレーション」値なので実測とは乖離する。実測(WebPageTest)で 713ms なら対応不要と判断できる。アクセシビリティのコントラスト問題は修正済み。

Chrome の Lighthouse でこのブログのパフォーマンスを測ったら 64/100 という微妙なスコアが出た。
「さすがにまずいかな」と思って調査してみたら、想定外の結論にたどり着いたのでメモ。

状況

Lighthouse の JSON レポートを取得して Claude Code に分析させた。

スコアはこんな感じ:

カテゴリスコア
Performance64
Accessibility94
Best Practices100
SEO100

Performance と Accessibility に問題あり。

Accessibility の問題: コントラスト比

こっちはシンプルだった。

シンタックスハイライトの CSS(monokai テーマ)で、JSON のキー名や XML タグに使われるピンク赤(#f92672)が、背景色(#272822)に対してコントラスト比 3.92:1 しかなかった。WCAG AA 基準は 4.5:1 以上なので落第。

Lighthouse のレポートで指摘されていた要素:

selector: code.language-json > span.line > span.cl > span.nt
explanation: contrast ratio of 3.92 (foreground: #f92672, background: #272822)

該当するトークンを全部拾うと .nt(NameTag)、.kn(KeywordNamespace)、.o(Operator)、.ow(OperatorWord)、.gd(GenericDeleted)の 5 種類。

dotfiles に埋まってた GNOME 拡張を GitHub リポジトリに切り出した

TL;DR — dotfiles に埋まっていた GNOME Shell 拡張を独立リポジトリに切り出して GitHub で公開した。uuid を 拡張名@ドメイン 形式に変更し、インストール先ディレクトリ名も合わせる必要がある。

以前の記事で作った IME インジケーター拡張を、dotfiles から独立したリポジトリに切り出した。

背景:GNOME Extensions への公開は見送り

GNOME Extensions に登録しようかと思ったけど、ポリシーを Claude さんに確認してもらって、それを自分なりに解釈した結果、「AI で生成したコードはいいけど、自分で説明できないならリジェクトね」っていうことらしい(たぶん)。

「GNOME + fcitx5 + Mozc + 入力モードが視覚的にわからなくてつらい人」というターゲットの少なさも考えると、ひとまず GitHub に public リポジトリを作ることにした。
そんな見る人もいないからいいよね。俺得だし。

やったこと

リポジトリ自体は空で作ってあったので、ファイルをコピーして移植する作業を Claude Code に任せた。

uuid と metadata.json の整備

dotfiles 版は ime-indicator@local という uuid を使っていた。GNOME 拡張の uuid は慣習的に 拡張名@ドメイン の形式をとるので、[email protected] に変更し、あわせて GitHub リポジトリの URL も追記。

{
  // Before
  "uuid": "ime-indicator@local"
}
{
  // After
  "uuid": "[email protected]",
  "url": "https://github.com/devmasa/gnome-ime-indicator"
}

uuid を変えるとインストール先のディレクトリ名も変わるので、既存環境では入れ直しが必要。まあ自分しか使ってないので問題なし。

Claude Code の git push が壊れた日:バイナリ解析よりリリースノートを先に読め

TL;DR — Claude Code で git push が v2.1.91 のアップデートで壊れた原因は excludedCommands のマッチング修正。"git""git *" に変えれば直る。

Claude Code で git push できなくなった。原因調査の過程を Claude とのやりとりで再現する。結論を先に言うと、リリースノートを最初に見ていれば 5 分で終わった話だった。

TL;DR

根本原因: v2.1.91 で excludedCommands のマッチング挙動が修正された

バージョンexcludedCommands: ["git"] の動作
v2.1.90 以前バグで git push origin main にもマッチ → sandbox 完全 bypass
v2.1.91 以降仕様通りに修正。"git" は bare git のみにマッチ → sandbox 適用 → 認証情報へのアクセスがブロックされ失敗

修正した設定:

{
  // Before
  "excludedCommands": ["git"]
}
{
  // After
  "excludedCommands": ["git *"]
}

今回は俺と Claude さんの会話を再現してます。一部省略。

Anthropic の OAuth 禁止で NanoClaw 大丈夫?→大丈夫(?)だと思うけど別の問題があった

TL;DR — systemd user service が After=network.target のみだと Docker より先に起動してクラッシュループになる。After=docker.service + ExecStartPre のソケット待機で解決できる。

Anthropic がサードパーティツールでの OAuth トークン利用を禁止したというニュースを見て、自分の NanoClaw は大丈夫かと確認しに行ったら、OAuth とは別のところに問題があった。

Anthropic の OAuth 禁止とは

Anthropic が Claude Pro/Max のサブスクリプション認証トークンをサードパーティツール経由で使う行為を禁止した。

要するに「月 20 ドルの Claude Pro に加入して、API キーの代わりにそのセッショントークンを使えば月 200〜500 ドル相当の API をほぼタダで使える」という抜け道を塞いだ話。

昨今の Claude への流入増大も影響あったんだろうね。

NanoClaw への影響は?

(おそらく)なし。

公式がそう言ってる、けどまあどうなるか分かんないけどね。
NanoClaw は、Anthropic の正規 API キーを使う構成で、OAuth には一切依存していない。認証まわりは OneCLI という credential proxy が担っていて、コンテナ起動時に ANTHROPIC_BASE_URL 経由でキーを注入する仕組みになっている。

Credentials are injected by the host’s credential proxy via ANTHROPIC_BASE_URL.

直接 .env に API キーを書くわけでもなく、OAuth トークンを流用するわけでもないので、今回の禁止措置とは完全に無関係って言ってる。まあ若干不安だけど。
ということで確かめてみることにしました。

Ubuntu + fcitx5 で IME モードをトップバーの色で示す GNOME 拡張を作った(作ってもらった)

TL;DR — Ubuntu + fcitx5 で IME の入力モードをトップバーの色で示す GNOME Shell 拡張を作った。fcitx5-remote を 400ms ポーリングして Main.panel.set_style() で色を切り替えている。→ Github で公開してます

Ubuntu デスクトップで日本語入力してると、いま英語モードなのか日本語モードなのかわからなくなることがよくある。トップバーの右端に「あ」とか「A」は出てるんだけど、作業中は目線が画面中央にあるので全然気づかない。

何回も Ghostty の tmux でプレフィックス打って効かないことが続くの嫌になった。心底嫌になった。

macOS だと入力モード切り替え時に画面上に色付きのバーで今のモードを表示してくれるやつがあったのに…

既製品を探した

Claude さんに相談してみて、GNOME Extensions も漁ったけど、ぴったりのものがない。

  • Kimpanel(extension/261): fcitx5 と連携してトップバーにモード表示できる。でも文字表示なので見づらさは変わらない
  • Input-Method Status Indicator(extension/68): IBus 向けなので fcitx5 環境では微妙
  • カーソル近くに表示するやつ: Linux/GNOME には存在しなかった

そんなとき

「無ければ作ればいいじゃんw」

みたいなノリで Claude 様がおっしゃられたので

「まじっすか、それでお願いします!」

って言ったら10分もかからずにできた。すげーなおい。

俺指示したの「色の指定は外出しファイルにしてリアルタイムに反映できるようにしといて」の雑な1行だけ。

そんな雑な指示によって作られたものが以下です。

作ったもの(作ってもらったもの)

fcitx5 が提供している fcitx5-remote コマンドをポーリングして、返り値に応じてトップバーの色を切り替える GNOME Shell 拡張。

~/.local/share/gnome-shell/extensions/ime-indicator@local/
├── extension.js
├── colors.json
└── metadata.json

metadata.json

{
  "name": "IME Indicator",
  "description": "fcitx5 の入力モードに応じてトップバーの背景色を切り替える",
  "uuid": "ime-indicator@local",
  "shell-version": ["46"],
  "version": 1
}

colors.json

色の設定だけを切り出したファイル。ここを編集すると即時反映される。