<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>OSS on Claude Code 始めました</title><link>https://kitepon-rgb.github.io/WebAICoding/tags/oss/</link><description>Recent content in OSS on Claude Code 始めました</description><generator>Hugo</generator><language>ja</language><lastBuildDate>Sat, 18 Apr 2026 12:00:00 +0900</lastBuildDate><atom:link href="https://kitepon-rgb.github.io/WebAICoding/tags/oss/index.xml" rel="self" type="application/rss+xml"/><item><title>Throughline を npm に公開した — Claude CodeのツールI/OをSQLiteに退避するhook</title><link>https://kitepon-rgb.github.io/WebAICoding/post/throughline-release/</link><pubDate>Sat, 18 Apr 2026 12:00:00 +0900</pubDate><guid>https://kitepon-rgb.github.io/WebAICoding/post/throughline-release/</guid><description>&lt;p&gt;&lt;a href="https://github.com/kitepon-rgb/Throughline"&gt;Throughline&lt;/a&gt; っていうClaude Code用のhookプラグインをnpmに公開した。&lt;/p&gt;
&lt;h2 id="何をするか"&gt;何をするか&lt;/h2&gt;
&lt;p&gt;Claude Codeのセッションで、コンテキストの大半は「ツールI/O」の残骸で埋まってる。ファイルを読んだ中身、grepの結果、Bashの出力。AIがその場で使って、判断して、次に進んだ時点で役目を終えてるデータ。でも最後までコンテキストに居座ってトークンを食い続ける。&lt;/p&gt;
&lt;p&gt;Throughlineは会話を3層に分けて管理する。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;層&lt;/th&gt;
 &lt;th&gt;中身&lt;/th&gt;
 &lt;th&gt;コンテキストへの注入&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;L2&lt;/td&gt;
 &lt;td&gt;会話本文（ユーザー発言 + AI応答）&lt;/td&gt;
 &lt;td&gt;直近20ターンはそのまま注入&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;L1&lt;/td&gt;
 &lt;td&gt;L2を要点を欠落させない程度（1/5）に要約したもの&lt;/td&gt;
 &lt;td&gt;20ターンより古いターンはL1を注入&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;L3&lt;/td&gt;
 &lt;td&gt;ツールI/O・システムメッセージ・thinking&lt;/td&gt;
 &lt;td&gt;注入せずSQLiteに退避、必要になったらClaude自身が取り出す&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;ツールI/Oはコンテキストから完全に抜くので、読み終わったgrep結果やBash出力がセッション最後まで居座らない。古い会話は1/5に圧縮されるが要点は残るので、数十ターン前の判断の文脈もちゃんと追える。&lt;/p&gt;
&lt;p&gt;手元の50ターンセッションで実測すると、125,000トークン使ってた会話が、13,000トークンに収まる。&lt;/p&gt;
&lt;h2 id="インストール"&gt;インストール&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install -g throughline
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;throughline install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;install&lt;/code&gt; は &lt;code&gt;~/.claude/settings.json&lt;/code&gt; にhookを登録する。PC内の全Claude Codeプロジェクトで自動で動く。プロジェクトごとの設定は不要。&lt;/p&gt;
&lt;h2 id="セッション間の引き継ぎ"&gt;セッション間の引き継ぎ&lt;/h2&gt;
&lt;p&gt;Throughlineは会話をSQLiteに退避してるので、&lt;code&gt;/clear&lt;/code&gt; してもデータ自体は残ってる。次のセッションに記憶を持ち越したい時は、前のセッションで &lt;code&gt;/tl&lt;/code&gt; って打つ。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/tl&lt;/code&gt; を打った時だけ、次のセッションに引き継がれる。打たなければ新規セッションとして始まる。並行ウィンドウを開いても、VSCodeを再起動しても、「&lt;code&gt;/tl&lt;/code&gt;を打たない限り誤爆しない」ようにできてる。&lt;/p&gt;
&lt;p&gt;引き継ぎ時には、前のClaudeが書いた「次の一手メモ」と、最終ターンの内部推論（thinking）も一緒に渡る。次のClaudeは「過去ログを読む」じゃなく「中断地点から続ける」モードで動く。&lt;/p&gt;
&lt;h2 id="トークンモニター"&gt;トークンモニター&lt;/h2&gt;
&lt;p&gt;副産物として、マルチセッション対応のトークンモニターもついてくる。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;throughline monitor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;[Throughline] 1 セッション
▶ Throughline 2ed5039c ████░░░░░░░░░░░░░░░░ 205.1k / 21% 残 794.9k claude-opus-4-6
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;transcriptのJSONLからAPIの実測値（&lt;code&gt;message.usage&lt;/code&gt;）を読むので、&lt;code&gt;文字数÷4&lt;/code&gt;の推定じゃなくて正確な値が出る。1Mコンテキストの自動検出にも対応。&lt;/p&gt;
&lt;h2 id="要件"&gt;要件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Node.js 22.5+（&lt;code&gt;node:sqlite&lt;/code&gt; 組み込みモジュールを使うため）&lt;/li&gt;
&lt;li&gt;Claude Code（hooks対応）&lt;/li&gt;
&lt;li&gt;Claude Max契約（L1要約のHaiku呼び出しに使う、APIキーは不要）&lt;/li&gt;
&lt;li&gt;Windows / macOS / Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="依存関係"&gt;依存関係&lt;/h2&gt;
&lt;p&gt;ゼロ。npmに公開してるtarballは &lt;code&gt;.mjs&lt;/code&gt; ファイルだけ。ビルドもネイティブバインディングも不要。&lt;/p&gt;</description></item><item><title>Claude Codeの"続きから"を実装するのに、自動検知を諦めた話</title><link>https://kitepon-rgb.github.io/WebAICoding/post/throughline-declare-over-detect/</link><pubDate>Sat, 18 Apr 2026 09:00:00 +0900</pubDate><guid>https://kitepon-rgb.github.io/WebAICoding/post/throughline-declare-over-detect/</guid><description>&lt;p&gt;&lt;a href="https://kitepon-rgb.github.io/WebAICoding/post/throughline-context-diet/"&gt;前の記事&lt;/a&gt;でThroughlineを公開した。コンテキストの大半を占めてるツールI/Oを退避するやつ。&lt;/p&gt;
&lt;p&gt;あの時点では&amp;quot;動いてた&amp;quot;。自分の環境では。&lt;/p&gt;
&lt;p&gt;でも記事を出した直後から、おかしな挙動に気づき始めてた。&lt;/p&gt;
&lt;p&gt;並行で別のウィンドウを開くと、新しいセッションが前のセッションの記憶を勝手に拾う。VSCodeを再起動すると、毎回「前回のセッションから続き」扱いになる。一度も &lt;code&gt;/clear&lt;/code&gt; してないのに。&lt;/p&gt;
&lt;h2 id="原因-clear-を検知できない"&gt;原因: /clear を検知できない&lt;/h2&gt;
&lt;p&gt;Claude Codeのhookには &lt;code&gt;SessionStart&lt;/code&gt; ってイベントがあって、&lt;code&gt;source&lt;/code&gt; っていうフィールドで startup（新規起動）と clear（/clear後）を区別できる、はずだった。&lt;/p&gt;
&lt;p&gt;ところがVSCode拡張だと、&lt;code&gt;/clear&lt;/code&gt; しても &lt;code&gt;source&lt;/code&gt; が &lt;code&gt;startup&lt;/code&gt; に潰される。&lt;a href="https://github.com/anthropics/claude-code/issues/49937"&gt;GitHub issue #49937&lt;/a&gt; に上がってる既知の問題。CLI単体なら動くけど、拡張だと識別できない。&lt;/p&gt;
&lt;p&gt;自分はVSCode拡張で使ってる。つまり「startupとclearを区別する」前提の設計が、根本から崩れてた。&lt;/p&gt;
&lt;h2 id="ヒューリスティックで補おうとした"&gt;ヒューリスティックで補おうとした&lt;/h2&gt;
&lt;p&gt;じゃあ時間差で判定するか。前のセッションの最終活動から10秒以内ならclear、それ以上ならstartup、みたいな。&lt;/p&gt;
&lt;p&gt;これも壊れた。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;並行でウィンドウを2つ開いてると、両方が&amp;quot;最近活動してた&amp;quot;ので両方が継承先候補になる&lt;/li&gt;
&lt;li&gt;VSCode再起動でもtranscriptは残ってるので&amp;quot;最近&amp;quot;に見える&lt;/li&gt;
&lt;li&gt;プロセスツリーを追いかけようとしたけど、CLIとextensionでプロセス構造が違う&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ldquo;そもそも検知できる条件が無い&amp;quot;と気づいた。&lt;/p&gt;
&lt;h2 id="発想を変えた"&gt;発想を変えた&lt;/h2&gt;
&lt;p&gt;検知しようとするから失敗する。&lt;strong&gt;ユーザーが宣言すれば、検知はいらない。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作ったのが &lt;code&gt;/tl&lt;/code&gt; ってスラッシュコマンド。ユーザーが次のセッションに記憶を引き継ぎたい時だけ打つ。打つと &lt;code&gt;handoff_batons&lt;/code&gt; ってテーブルにそのセッションIDが書き込まれる。バトンを置くイメージ。&lt;/p&gt;
&lt;p&gt;次のセッション開始時、バトンが1時間以内に置かれてたら、そのセッションの記憶を引き継ぐ。なければ、何もしない。新規セッションとして始まる。&lt;/p&gt;
&lt;p&gt;並行ウィンドウもVSCode再起動も、「バトンが置かれてない限り誤爆しない」が原理的に保証される。&lt;/p&gt;
&lt;p&gt;明示的なのは一見面倒だけど、「勝手に引き継いで迷惑」の方が遥かに困る。誤爆ゼロの方が価値があった。&lt;/p&gt;
&lt;h2 id="でもこれだけじゃ物足りなかった"&gt;でも、これだけじゃ物足りなかった&lt;/h2&gt;
&lt;p&gt;バトンができて、次のセッションが前のセッションの会話ログを読めるようになった。でも実際に使ってみて思ったのが、「ただログを読んでるだけ」感。&lt;/p&gt;
&lt;p&gt;過去ログを読むAIと、中断地点から続けるAIは、体感が違う。&lt;/p&gt;
&lt;p&gt;前者は「よし、状況を把握した。じゃあ今から何をしましょうか？」って聞いてくる。後者は「さっきの続きだと、あと◯◯を確認すればいいよね」って進める。&lt;/p&gt;
&lt;p&gt;ここで2つ足した。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;in-flight memo.&lt;/strong&gt; &lt;code&gt;/tl&lt;/code&gt; を打った瞬間、今動いてるClaude自身に「次の一手、今の仮説、未解決の問題、進行中のTODO」をMarkdownで書いてもらう。それをバトンに添付する。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;thinkingの保存.&lt;/strong&gt; Claudeのextended thinking（内部推論）ブロックもL3として保存しておく。次のセッションの注入時、最終ターンのthinkingを頭に出す。前のClaudeが何を考えてたかが、次のClaudeに渡る。&lt;/p&gt;
&lt;p&gt;結果、次のセッションの注入テキストはこういう形になる。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;あなたは中断されたタスクを再開します。

[前のClaudeが書いた in-flight memo]
次やること: X のテストを書く。仮説: Y が原因だと思う。未解決: Z。

[前のClaudeが最後に考えてたこと]
Z の挙動が気になる。もしかしたら...

[直近20ターンの会話]
...
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="読むじゃなく続ける"&gt;&amp;ldquo;読む&amp;quot;じゃなく&amp;quot;続ける&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;これで手応えが変わった。&lt;/p&gt;</description></item><item><title>コンテキストの87%が使い捨てだったので自分で対策した話</title><link>https://kitepon-rgb.github.io/WebAICoding/post/throughline-context-diet/</link><pubDate>Thu, 16 Apr 2026 00:00:00 +0000</pubDate><guid>https://kitepon-rgb.github.io/WebAICoding/post/throughline-context-diet/</guid><description>&lt;p&gt;MAXプランの週間クォータが3日で溶けた。&lt;/p&gt;
&lt;p&gt;×20のクォータがあるはずなのに、水曜には残量が怪しくなってる。それ自体は「まあそんなもんか」で済ませてたんだけど、ふと気になった。コンテキストウィンドウの中身って、実際どうなってるんだろう。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://kitepon-rgb.github.io/WebAICoding/post/ai-secretary-token-diet/"&gt;前の記事&lt;/a&gt;ではAI秘書のトークン節約について書いた。CLAUDE.mdを削ったり、MCPツール定義を削ったり。でも今回はAI秘書じゃなくて、Claude Code自体の話。道具のほうが大食いだったとは。&lt;/p&gt;
&lt;h2 id="きっかけ"&gt;きっかけ&lt;/h2&gt;
&lt;p&gt;4月14日、スペイン語圏のあるツイートが目に留まった。&lt;/p&gt;
&lt;p&gt;「Claude Codeのトークン浪費の大半はユーザー側に原因がある」&lt;/p&gt;
&lt;p&gt;言いたいことはわかる。CLAUDE.mdが肥大化してるとか、プロンプトが冗長だとか。でも「大半がユーザー側」って、ちゃんと測って言ってるのか？&lt;/p&gt;
&lt;p&gt;じゃあ俺が測ってやる、と思った。&lt;/p&gt;
&lt;h2 id="実測してみた"&gt;実測してみた&lt;/h2&gt;
&lt;p&gt;Claude Codeの内部transcript（セッションを記録してるJSONL）を解析した。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1ターンあたり188,000トークン。うち164,000トークン（87%）が会話履歴。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CLAUDE.mdは12,700トークン。MCPツール定義は3,900トークン。合わせても全体の9%。これを半分にしても5%弱しか浮かない。&lt;/p&gt;
&lt;p&gt;本丸は会話履歴の肥大化だった。CLAUDE.mdを必死に削ってた自分がちょっと恥ずかしい。&lt;/p&gt;
&lt;h2 id="犯人はツールio"&gt;犯人はツールI/O&lt;/h2&gt;
&lt;p&gt;じゃあ履歴の中身は何か。&lt;/p&gt;
&lt;p&gt;開いて驚いた。&lt;strong&gt;履歴の約80%がツールの入出力だった。&lt;/strong&gt; ファイルの読み取り結果、Bashコマンドの出力、grepの結果。AIがその場で使って、判断して、次に進んだ時点で役目を終えてるデータ。&lt;/p&gt;
&lt;p&gt;なのに、それがコンテキストウィンドウにずっと居座って、毎ターントークンを食い続けている。&lt;/p&gt;
&lt;p&gt;50ターンのセッションだと、最初のほうにgrepした結果がまだコンテキストにいる。もう二度と見ることはないのに。&lt;/p&gt;
&lt;h2 id="compactの矛盾"&gt;/compactの矛盾&lt;/h2&gt;
&lt;p&gt;「/compactすればいいじゃん」って思うかもしれない。自分もそう思ってた。&lt;/p&gt;
&lt;p&gt;でも/compactの仕組み、&lt;strong&gt;AIに全履歴を読ませて要約させる&lt;/strong&gt;んだよね。&lt;/p&gt;
&lt;p&gt;トークンを節約するために、トークンを大量に消費する。しかも要約の過程でニュアンスが消える。「あの時なぜこの設計にしたか」みたいな文脈が、丸められて消えることがある。&lt;/p&gt;
&lt;p&gt;要約後にまた作業を続ければ、また肥大化して、またcompactして&amp;hellip;の繰り返し。根本解決じゃない。&lt;/p&gt;
&lt;h2 id="時間じゃなく種類で分ける"&gt;時間じゃなく、種類で分ける&lt;/h2&gt;
&lt;p&gt;ここで発想を変えた。&lt;/p&gt;
&lt;p&gt;MemGPTやLangChainのSummaryBufferMemoryは、&lt;strong&gt;古いものから要約する&lt;/strong&gt;。時間ベースの圧縮。でも問題は「古さ」じゃない。&lt;/p&gt;
&lt;p&gt;10ターン前の「この設計にした理由」は今でも価値がある。さっきのgrepの結果は、1ターン前でも用済み。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;時間じゃなく、種類で分ければいい。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;会話本文（人間が書いたこと、AIが答えたこと）→ 残す&lt;/li&gt;
&lt;li&gt;ツール入出力（ファイル内容、コマンド結果）→ 退避する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この発想で作ったのが&lt;a href="https://github.com/kitepon-rgb/Throughline"&gt;Throughline&lt;/a&gt;だ。&lt;/p&gt;
&lt;h2 id="3層モデル"&gt;3層モデル&lt;/h2&gt;
&lt;p&gt;Throughlineは会話を3つのレイヤーに分解してSQLiteに保存する。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L1（Skeleton）&lt;/strong&gt; — 古いターンの一行要約。軽量モデルが生成する。1ターン約10トークン。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L2（Body）&lt;/strong&gt; — 直近20ターンの会話本文。ユーザーの発言とAIの応答がそのまま残る。圧縮なし、ロスレス。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L3（Detail）&lt;/strong&gt; — ツール入出力、システムメッセージ。SQLiteに退避してコンテキストには一切残さない。必要になったらAIが自分でSQLiteから引っ張ってくる。&lt;/p&gt;
&lt;p&gt;/clearを打っても大丈夫。SQLiteは消えないから、次のセッション開始時にトランザクション一発で前セッションの記憶を引き継ぐ。PIDを追いかけたり、時間窓で判定する必要はない。決定的に動く。&lt;/p&gt;
&lt;p&gt;数字で言うとこう。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Throughlineなし（50ターン、/clearなし）：
 コンテキスト ≈ 125,000トークン（80%が用済みのツールI/O）

Throughlineあり（50ターン → /clear → 復帰）：
 コンテキスト ≈ 13,000トークン
 （直近20ターンのL2 + 古い30ターンのL1要約）
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;約90%の削減。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="失敗した設計の話もしておく"&gt;失敗した設計の話もしておく&lt;/h2&gt;
&lt;p&gt;最初からこの形だったわけじゃない。&lt;/p&gt;
&lt;p&gt;初期の設計では、L2を「重要な判断の構造化抽出」にしようとした。&lt;code&gt;[DECISION] WebSocketを採用&lt;/code&gt;、&lt;code&gt;[CONSTRAINT] ポート8080は使えない&lt;/code&gt; みたいなタグ付きで、会話から重要な情報だけ引き抜くイメージ。&lt;/p&gt;</description></item></channel></rss>