<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: bayuh85</title>
    <description>The latest articles on DEV Community by bayuh85 (@bayuh85).</description>
    <link>https://dev.to/bayuh85</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4186907%2F10c72ed9-7026-425d-942e-8ae529393db8.png</url>
      <title>DEV Community: bayuh85</title>
      <link>https://dev.to/bayuh85</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bayuh85"/>
    <language>en</language>
    <item>
      <title>skimsearch: stop pasting grep dumps into your coding agent</title>
      <dc:creator>bayuh85</dc:creator>
      <pubDate>Fri, 16 Oct 2026 02:39:45 +0000</pubDate>
      <link>https://dev.to/bayuh85/skimsearch-stop-pasting-grep-dumps-into-your-coding-agent-hlj</link>
      <guid>https://dev.to/bayuh85/skimsearch-stop-pasting-grep-dumps-into-your-coding-agent-hlj</guid>
      <description>&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; 👉 &lt;a href="https://github.com/bayuh85/skimsearch" rel="noopener noreferrer"&gt;github.com/bayuh85/skimsearch&lt;/a&gt; (MIT, Python 3.10+)&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Ask a coding agent "where is the late fee calculated?" and watch what happens. It runs &lt;code&gt;grep -rn "late fee"&lt;/code&gt;, gets back dozens of hits from code, docs, vendored dependencies, and tests, and pastes the whole dump into its context. Then it opens three wrong files before finding the right function.&lt;/p&gt;

&lt;p&gt;Every one of those lines is tokens you pay for, re-read on every turn, crowding out the few lines that actually answer the question.&lt;/p&gt;

&lt;h2&gt;
  
  
  What skimsearch does
&lt;/h2&gt;

&lt;p&gt;skimsearch indexes the tree once, then answers with a short deck of ranked snippets that fits a token budget.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Index.&lt;/strong&gt; &lt;code&gt;skimsearch index&lt;/code&gt; folds code and docs into &lt;em&gt;skim cards&lt;/em&gt;: one function, class, heading, or text window each, with its path and line range. Cards live in a compact SQLite file at &lt;code&gt;.skimsearch/index.sqlite&lt;/code&gt;, ranked offline with SQLite FTS5 (BM25).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search.&lt;/strong&gt; &lt;code&gt;skimsearch search&lt;/code&gt; returns the best cards, trimmed so the whole deck stays inside a budget (700 tokens by default). Lexical mode never touches the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask.&lt;/strong&gt; &lt;code&gt;--mode semantic&lt;/code&gt; lets a cheap model turn a prose question into a few lexical probes, then searches locally. &lt;code&gt;--mode ask&lt;/code&gt; also returns a short answer that cites only the cards it used.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It respects &lt;code&gt;.gitignore&lt;/code&gt;, supports a &lt;code&gt;.skimignore&lt;/code&gt;, and skips dependency folders, lockfiles, and secret files like &lt;code&gt;.env&lt;/code&gt; and &lt;code&gt;.pem&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real use case: finding the late fee
&lt;/h2&gt;

&lt;p&gt;The repo ships a small sample project, &lt;code&gt;examples/acme-billing&lt;/code&gt;, with code, docs, a vendored folder, and a fake &lt;code&gt;.env&lt;/code&gt;. Install and index it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/bayuh85/skimsearch.git
&lt;span class="nb"&gt;cd &lt;/span&gt;skimsearch
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

skimsearch index examples/acme-billing
skimsearch search &lt;span class="s2"&gt;"where is the late fee calculated"&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; examples/acme-billing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;skimsearch  mode=lexical  cards=4  tokens=322/700

1. src/billing/invoices.py:17-28  function apply_late_fee  (python, score 1.71)
    def apply_late_fee(invoice: dict, as_of: date) -&amp;gt; dict:
        """The late fee is calculated from the subtotal after the grace window."""
        if invoice.get("settled"):
            return invoice
        overdue = (as_of - invoice["due"]).days
        if overdue &amp;lt;= GRACE_DAYS:
            return invoice
        fee = round(invoice["subtotal"] * LATE_FEE_RATE, 2)
        ...

2. docs/billing.md:3-5  heading Late fees  (markdown, score 1.50)
    ## Late fees

    A fee of 1.5 percent is added after a three-day grace period.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;322 tokens, with the right function first and the matching docs right below it. The vendored code and the fake &lt;code&gt;.env&lt;/code&gt; never show up. That's what the agent reads instead of a raw grep dump.&lt;/p&gt;

&lt;p&gt;If you'd rather get a direct answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ATMOROUTER_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ar-your-key-here
skimsearch search &lt;span class="s2"&gt;"where is the late fee calculated"&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; examples/acme-billing &lt;span class="nt"&gt;--mode&lt;/span&gt; ask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Plugging it into Cursor or Claude Code (MCP)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;skimsearch serve&lt;/code&gt; runs a stdio MCP server (and builds the index if it's missing). For Claude Code, add this to &lt;code&gt;.mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skimsearch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"skimsearch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/absolute/path/to/repo"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ATMOROUTER_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ar-your-key-here"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cursor uses the same block in &lt;code&gt;.cursor/mcp.json&lt;/code&gt;. The agent gets two tools: &lt;code&gt;skim_search&lt;/code&gt; (query, mode, budget, limit, format) and &lt;code&gt;skim_status&lt;/code&gt;. Use &lt;code&gt;lexical&lt;/code&gt; in tight loops and &lt;code&gt;ask&lt;/code&gt; for prose questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handy commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;skimsearch index &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt;          &lt;span class="c"&gt;# rebuild&lt;/span&gt;
skimsearch index &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--embed&lt;/span&gt;          &lt;span class="c"&gt;# also store /embeddings vectors&lt;/span&gt;
skimsearch search &lt;span class="s2"&gt;"session ttl"&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--budget&lt;/span&gt; 500
skimsearch search &lt;span class="s2"&gt;"session ttl"&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; ask &lt;span class="nt"&gt;--format&lt;/span&gt; json
skimsearch status &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test suite (44 tests) runs fully offline: &lt;code&gt;pip install -e ".[dev]" &amp;amp;&amp;amp; pytest -q&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About AtmoRouter (the default model endpoint)
&lt;/h2&gt;

&lt;p&gt;Semantic and ask modes call an OpenAI-compatible chat API, and the default base URL is &lt;code&gt;https://atmorouter.dev/v1&lt;/code&gt;. &lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;AtmoRouter&lt;/a&gt; is a cheap AI API gateway and an OpenRouter alternative: 109 models behind one key, compatible with both OpenAI and Anthropic clients, pay per token, with prices averaging 83.7% below official rates. It also accepts QRIS and USDC. That fits this tool's job: query rewriting should cost far less than the coding model it's saving tokens for. The default model is &lt;code&gt;atmo/deepseek-v4.1-flash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Prefer the Anthropic Messages API? Set &lt;code&gt;SKIMSEARCH_API_STYLE=anthropic&lt;/code&gt;. Want another provider? Point &lt;code&gt;SKIMSEARCH_BASE_URL&lt;/code&gt; at any OpenAI-compatible endpoint. And lexical search needs no key at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If your agent keeps drowning in grep output, give it a deck instead:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/bayuh85/skimsearch" rel="noopener noreferrer"&gt;github.com/bayuh85/skimsearch&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Issues with a query that ranks badly (plus a small sample repo) are the most useful feedback you can send.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>opensource</category>
      <category>devtools</category>
    </item>
    <item>
      <title>atmo-mem: persistent memory for Claude Code, Cursor, and Cline in one Go binary</title>
      <dc:creator>bayuh85</dc:creator>
      <pubDate>Thu, 15 Oct 2026 17:47:15 +0000</pubDate>
      <link>https://dev.to/bayuh85/atmo-mem-persistent-memory-for-claude-code-cursor-and-cline-in-one-go-binary-2fn0</link>
      <guid>https://dev.to/bayuh85/atmo-mem-persistent-memory-for-claude-code-cursor-and-cline-in-one-go-binary-2fn0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; 👉 &lt;a href="https://github.com/bayuh85/atmo-mem" rel="noopener noreferrer"&gt;github.com/bayuh85/atmo-mem&lt;/a&gt; (MIT, pure Go, no CGO)&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Every new coding-agent session starts with amnesia. You spent yesterday's session with Claude Code working out why the auth refresh was flaky, decided on an approach, and agreed on a few project conventions. Today you open a fresh session and the agent has forgotten all of it. Either you explain it again, or you keep a hand-maintained notes file and hope the agent reads it.&lt;/p&gt;

&lt;p&gt;What I wanted was boring: a local place where the agent can &lt;strong&gt;write down decisions and fixes as it works&lt;/strong&gt;, and &lt;strong&gt;look them up at the start of the next session&lt;/strong&gt;. No hosted service, no Docker, no Python environment. Just one binary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What atmo-mem does
&lt;/h2&gt;

&lt;p&gt;atmo-mem is persistent cross-session memory for coding agents, shipped as one static binary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It speaks the &lt;strong&gt;&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; over stdio&lt;/strong&gt;, so Claude Code, Cursor, Cline, and other MCP clients can use it.&lt;/li&gt;
&lt;li&gt;The agent &lt;strong&gt;saves&lt;/strong&gt; decisions, bugfixes, and facts while it works.&lt;/li&gt;
&lt;li&gt;A later session &lt;strong&gt;searches&lt;/strong&gt; and &lt;strong&gt;recalls&lt;/strong&gt; them from a &lt;strong&gt;local SQLite database&lt;/strong&gt; with an FTS5 full-text index.&lt;/li&gt;
&lt;li&gt;At the end of a session, atmo-mem can &lt;strong&gt;compress the notes into a summary&lt;/strong&gt; with any OpenAI-compatible model (default: &lt;code&gt;zai/glm-5.3&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Privacy-wise, the important part is: &lt;strong&gt;save, search, and recall stay on disk. Only &lt;code&gt;session_summarize&lt;/code&gt; calls the network.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SQLite is compiled in through &lt;code&gt;modernc.org/sqlite&lt;/code&gt; (pure Go, FTS5 included), so you don't need CGO or a system SQLite library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;p&gt;You need a Go toolchain. Clone and build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/bayuh85/atmo-mem.git
&lt;span class="nb"&gt;cd &lt;/span&gt;atmo-mem
&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build &lt;span class="nt"&gt;-o&lt;/span&gt; atmo-mem ./cmd/atmo-mem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move the &lt;code&gt;atmo-mem&lt;/code&gt; binary somewhere on your &lt;code&gt;PATH&lt;/code&gt;, then check it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;atmo-mem version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The README also describes a &lt;code&gt;go install&lt;/code&gt; command and GoReleaser archives for Linux, macOS, and Windows (&lt;code&gt;amd64&lt;/code&gt; and &lt;code&gt;arm64&lt;/code&gt;) for tagged releases. When I wrote this the repo had no published releases yet, so building from a checkout is the reliable route.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want end-of-session summaries, configure an API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# edit ATMO_MEM_API_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can try it without an agent first. The CLI uses the same database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;atmo-mem save &lt;span class="nt"&gt;--project&lt;/span&gt; my-app &lt;span class="nt"&gt;--kind&lt;/span&gt; decision &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--title&lt;/span&gt; &lt;span class="s2"&gt;"Use FTS5"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--content&lt;/span&gt; &lt;span class="s2"&gt;"Memory search is SQLite FTS5."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; sqlite,search

atmo-mem search &lt;span class="nt"&gt;--project&lt;/span&gt; my-app fts
atmo-mem recall &lt;span class="nt"&gt;--project&lt;/span&gt; my-app memory search
atmo-mem summarize &lt;span class="nt"&gt;--project&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--notes&lt;/span&gt; &lt;span class="s2"&gt;"We stored memories in SQLite and exposed them over MCP."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flags go before positional arguments. Add &lt;code&gt;--json&lt;/code&gt; to get JSON output from &lt;code&gt;save&lt;/code&gt;, &lt;code&gt;search&lt;/code&gt;, &lt;code&gt;recall&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, and &lt;code&gt;summarize&lt;/code&gt;. &lt;code&gt;atmo-mem help&lt;/code&gt; lists every command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting it up with your agent
&lt;/h2&gt;

&lt;p&gt;Running &lt;code&gt;atmo-mem&lt;/code&gt; with no arguments (or &lt;code&gt;atmo-mem mcp&lt;/code&gt;) reads JSON-RPC from stdin and writes JSON-RPC to stdout, with logs on stderr. In an interactive terminal it prints help instead; when stdin is a pipe, which is how editors launch it, it starts the MCP server. Don't wrap it in a script that prints a banner, because that breaks the protocol.&lt;/p&gt;

&lt;p&gt;Two tips from the README:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;strong&gt;&lt;code&gt;ATMO_MEM_PROJECT&lt;/code&gt;&lt;/strong&gt; to the repo name so memories from different projects don't mix.&lt;/li&gt;
&lt;li&gt;Pass &lt;strong&gt;&lt;code&gt;ATMO_MEM_API_KEY&lt;/code&gt;&lt;/strong&gt; only if you want end-of-session summaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;Project &lt;code&gt;.mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"atmo-mem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"atmo-mem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ATMO_MEM_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ATMO_MEM_PROJECT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with the CLI (flags vary slightly across Claude Code versions; the JSON above is the stable form):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; stdio &lt;span class="nt"&gt;--scope&lt;/span&gt; project &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;ATMO_MEM_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;ATMO_MEM_PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-app &lt;span class="se"&gt;\&lt;/span&gt;
  atmo-mem &lt;span class="nt"&gt;--&lt;/span&gt; atmo-mem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;Put the same block in &lt;code&gt;.cursor/mcp.json&lt;/code&gt; in your project (or &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt; for a global config), then reload MCP servers from Cursor settings. &lt;code&gt;atmo-mem&lt;/code&gt; has to be on the &lt;code&gt;PATH&lt;/code&gt; Cursor sees, or you can set &lt;code&gt;command&lt;/code&gt; to the absolute path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cline
&lt;/h3&gt;

&lt;p&gt;Open &lt;strong&gt;MCP Servers → Configure&lt;/strong&gt; (&lt;code&gt;cline_mcp_settings.json&lt;/code&gt;) and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"atmo-mem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"atmo-mem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ATMO_MEM_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ATMO_MEM_PROJECT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoApprove"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"memory_recall"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"memory_search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"memory_list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"memory_get"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The auto-approve list here is a good default: read-only tools run without prompting, while &lt;code&gt;memory_delete&lt;/code&gt; (destructive) and &lt;code&gt;session_summarize&lt;/code&gt; (a billed network call) still ask for approval.&lt;/p&gt;

&lt;h3&gt;
  
  
  Teach the agent the workflow
&lt;/h3&gt;

&lt;p&gt;The tools only help if the agent calls them. The README suggests this loop, which works well in &lt;code&gt;CLAUDE.md&lt;/code&gt;, Cursor rules, or Cline custom instructions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At the start of a task, call &lt;code&gt;memory_recall&lt;/code&gt; with the task and the project.&lt;/li&gt;
&lt;li&gt;When a decision, fix, or preference lands, call &lt;code&gt;memory_save&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;At the end of the session, call &lt;code&gt;session_summarize&lt;/code&gt; with notes, or with a &lt;code&gt;session_id&lt;/code&gt; you used along the way.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory_save&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Store &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;content&lt;/code&gt;, optional &lt;code&gt;kind&lt;/code&gt;, &lt;code&gt;tags&lt;/code&gt;, &lt;code&gt;project&lt;/code&gt;, &lt;code&gt;session_id&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory_search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AND keyword search. Words of 3+ characters are prefix-matched (&lt;code&gt;auth&lt;/code&gt; finds &lt;code&gt;authentication&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory_recall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rank memories for a task. An empty query returns recent project memory. The newest summary is pinned first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory_list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recent memories, newest first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory_get&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One memory by id.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory_delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently delete one memory by id.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;session_summarize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compress a transcript and that session's memories with the LLM, then store a &lt;code&gt;summary&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Memory kinds: &lt;code&gt;observation&lt;/code&gt; (default), &lt;code&gt;decision&lt;/code&gt;, &lt;code&gt;bugfix&lt;/code&gt;, &lt;code&gt;preference&lt;/code&gt;, &lt;code&gt;fact&lt;/code&gt;, &lt;code&gt;summary&lt;/code&gt;, &lt;code&gt;task&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Search vs. recall
&lt;/h3&gt;

&lt;p&gt;The two lookups deliberately behave differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Search is strict.&lt;/strong&gt; A memory is returned only when &lt;em&gt;every&lt;/em&gt; query word matches. Results are ordered by FTS5 bm25, with the title weighted above the body and tags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recall is wide.&lt;/strong&gt; &lt;em&gt;Any&lt;/em&gt; word can match, and a small recency bonus breaks near ties. The latest &lt;code&gt;summary&lt;/code&gt; for the project goes to the front even if it shares no words with the query. A much stronger text match still outranks a memory that's only newer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common English filler words (&lt;code&gt;the&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, …) are dropped when the query has other words. Matching is case-insensitive, words shorter than two characters are ignored, and two-character words such as &lt;code&gt;go&lt;/code&gt; match exactly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage
&lt;/h3&gt;

&lt;p&gt;Everything lives in one SQLite file, by default &lt;code&gt;~/.atmo-mem/atmo-mem.db&lt;/code&gt;. The directory is created with mode &lt;code&gt;0700&lt;/code&gt;, and the file is set to &lt;code&gt;0600&lt;/code&gt; where the OS allows it. Triggers keep an FTS5 index (&lt;code&gt;unicode61&lt;/code&gt; tokenizer) in sync on insert and delete.&lt;/p&gt;

&lt;p&gt;There's no update call: to change a memory, delete the old id and save a new one. Sessions are just bookkeeping rows. What the next session actually reads is a normal memory with kind &lt;code&gt;summary&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summarization
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;session_summarize&lt;/code&gt; sends the transcript or notes, plus memories saved under that &lt;code&gt;session_id&lt;/code&gt;, to &lt;code&gt;POST {ATMO_MEM_BASE_URL}/chat/completions&lt;/code&gt; with a standard body (&lt;code&gt;model&lt;/code&gt;, &lt;code&gt;messages&lt;/code&gt;, &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;max_tokens&lt;/code&gt;) and stores the result as a &lt;code&gt;summary&lt;/code&gt; memory. Because recall pins the newest summary, the next session starts with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;Settings are read from flags first, then environment variables, then a &lt;code&gt;.env&lt;/code&gt; file in the working directory; the first non-empty value wins. &lt;code&gt;ATMO_MEM_*&lt;/code&gt; takes precedence over the &lt;code&gt;OPENAI_*&lt;/code&gt; fallbacks.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATMO_MEM_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;empty&lt;/td&gt;
&lt;td&gt;Bearer token for summarization (falls back to &lt;code&gt;OPENAI_API_KEY&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATMO_MEM_BASE_URL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://atmorouter.dev/v1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI-compatible base URL, including &lt;code&gt;/v1&lt;/code&gt; (falls back to &lt;code&gt;OPENAI_BASE_URL&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATMO_MEM_MODEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zai/glm-5.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Model id (falls back to &lt;code&gt;OPENAI_MODEL&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATMO_MEM_DB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.atmo-mem/atmo-mem.db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SQLite file.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATMO_MEM_PROJECT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;empty&lt;/td&gt;
&lt;td&gt;Project used when a call omits &lt;code&gt;project&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATMO_MEM_TIMEOUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;60s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Summarization timeout.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Credit
&lt;/h2&gt;

&lt;p&gt;atmo-mem is &lt;strong&gt;inspired by &lt;a href="https://github.com/thedotmack/claude-mem" rel="noopener noreferrer"&gt;claude-mem&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://github.com/Gentleman-Programming/engram" rel="noopener noreferrer"&gt;engram&lt;/a&gt;&lt;/strong&gt;, two projects that have explored persistent agent memory further than this one. atmo-mem is &lt;strong&gt;not affiliated&lt;/strong&gt; with either. Its angle is narrow: one dependency-free binary, local SQLite + FTS5, and an optional summary step that works with any OpenAI-compatible endpoint. If you want something richer, look at those projects too.&lt;/p&gt;

&lt;h2&gt;
  
  
  About AtmoRouter: a cheap AI API gateway and OpenRouter alternative
&lt;/h2&gt;

&lt;p&gt;Disclosure: by default, &lt;code&gt;session_summarize&lt;/code&gt; sends requests to AtmoRouter using &lt;code&gt;zai/glm-5.3&lt;/code&gt;; summaries don't need the model you code with. Any OpenAI-compatible provider works. &lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;AtmoRouter&lt;/a&gt; is a &lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;cheap AI API gateway&lt;/a&gt; that speaks both the OpenAI Chat Completions API and the Anthropic Messages API, so one key reaches 100+ models from Cursor, Cline, Claude Code, or any compatible client. It is pay-per-token with no subscription, accepts QRIS and USDC, and prices sit well below official rates: AtmoRouter advertises an average of 83.7% below official prices, which makes it an affordable LLM API and a practical OpenRouter alternative for agent workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you're tired of re-explaining your project to a fresh agent session every morning:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/bayuh85/atmo-mem" rel="noopener noreferrer"&gt;github.com/bayuh85/atmo-mem&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Issues and PRs are welcome. Real-world reports on how recall ranking holds up across a long-running project would be especially useful.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>go</category>
      <category>productivity</category>
    </item>
    <item>
      <title>ctxtrim: stop feeding your coding agent 300-line logs</title>
      <dc:creator>bayuh85</dc:creator>
      <pubDate>Thu, 15 Oct 2026 17:26:38 +0000</pubDate>
      <link>https://dev.to/bayuh85/ctxtrim-stop-feeding-your-coding-agent-300-line-logs-2c7e</link>
      <guid>https://dev.to/bayuh85/ctxtrim-stop-feeding-your-coding-agent-300-line-logs-2c7e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; 👉 &lt;a href="https://github.com/bayuh85/ctxtrim" rel="noopener noreferrer"&gt;github.com/bayuh85/ctxtrim&lt;/a&gt; (MIT, Node.js 20+)&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Coding agents like Claude Code, Cursor, and Cline are great until the context window fills up with junk. A test run dumps hundreds of lines. A server log repeats the same startup message over and over. The agent &lt;code&gt;cat&lt;/code&gt;s a large JSON file to check one field. All of that lands in context, gets re-read on every turn, and pushes out the stuff that actually matters — like what you decided twenty minutes ago.&lt;/p&gt;

&lt;p&gt;The usual fixes are manual: &lt;code&gt;| tail -50&lt;/code&gt;, &lt;code&gt;grep ERROR&lt;/code&gt;, or telling the agent "please don't read the whole file." That works until it doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ctxtrim does
&lt;/h2&gt;

&lt;p&gt;ctxtrim shrinks what a coding agent reads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Press:&lt;/strong&gt; large logs, test runs, and file dumps are trimmed mechanically — no network, no key needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gloss (optional):&lt;/strong&gt; if the trimmed result is still big, a cheap OpenAI-compatible model summarizes it (default: &lt;code&gt;zai/glm-5.3&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session notes:&lt;/strong&gt; decisions and findings go into a compact session ledger instead of a growing transcript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It ships as both a &lt;strong&gt;command-line tool&lt;/strong&gt; and an &lt;strong&gt;&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP&lt;/a&gt; server&lt;/strong&gt;, and both expose the same operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;p&gt;Requires Node.js 20 or newer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/bayuh85/ctxtrim.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ctxtrim
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mechanical press works with no key at all. If you want model summaries, set a key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# CTXTRIM_API_KEY=your-key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now pipe something noisy through it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CTXTRIM_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-key
&lt;span class="c"&gt;# optional; these are already the defaults&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CTXTRIM_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://atmorouter.dev/v1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CTXTRIM_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;zai/glm-5.3

npm &lt;span class="nb"&gt;test &lt;/span&gt;2&amp;gt;&amp;amp;1 | node dist/cli.js shrink &lt;span class="nt"&gt;--kind&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--session&lt;/span&gt; demo
node dist/cli.js note add &lt;span class="nt"&gt;--session&lt;/span&gt; demo &lt;span class="nt"&gt;--tag&lt;/span&gt; decision &lt;span class="s2"&gt;"Retry refresh with jitter."&lt;/span&gt;
node dist/cli.js recall &lt;span class="nt"&gt;--session&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The brief goes to &lt;strong&gt;stdout&lt;/strong&gt; (so you can keep piping), and a one-line receipt goes to &lt;strong&gt;stderr&lt;/strong&gt;. The README's example: pressing a 300-line log containing three connection errors and one warning reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ctxtrim · log · press · 12578 → 1299 bytes · kept 10%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The brief keeps the repeated startup line collapsed (&lt;code&gt;ready ×3&lt;/code&gt;), the errors, the warning, and the head and tail. The quiet middle becomes a single omission line.&lt;/p&gt;

&lt;p&gt;The receipt tells you which path was taken:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;passthrough&lt;/code&gt; — input was already small&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;press&lt;/code&gt; — mechanical trim&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gloss&lt;/code&gt; — model summary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cache&lt;/code&gt; — a gloss reused for the same text and model&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting it up with your agent (MCP)
&lt;/h2&gt;

&lt;p&gt;Build first (&lt;code&gt;npm run build&lt;/code&gt;), then point your client at &lt;code&gt;node /absolute/path/to/ctxtrim/dist/cli.js serve&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;Project &lt;code&gt;.mcp.json&lt;/code&gt; (or your user config):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ctxtrim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/absolute/path/to/ctxtrim/dist/cli.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"CTXTRIM_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"CTXTRIM_BASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://atmorouter.dev/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"CTXTRIM_MODEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zai/glm-5.3"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or from a shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add ctxtrim &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;CTXTRIM_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-key &lt;span class="nt"&gt;--&lt;/span&gt; node /absolute/path/to/ctxtrim/dist/cli.js serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;Put the same &lt;code&gt;mcpServers.ctxtrim&lt;/code&gt; block in &lt;code&gt;.cursor/mcp.json&lt;/code&gt; in your project, or add it via &lt;strong&gt;Cursor Settings → MCP&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cline
&lt;/h3&gt;

&lt;p&gt;Add the same entry to Cline's MCP settings file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux: &lt;code&gt;~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(VS Code Insiders and other forks use their own &lt;code&gt;User/globalStorage&lt;/code&gt; directory.) Cline picks up the file on save.&lt;/p&gt;

&lt;h3&gt;
  
  
  The tools your agent gets
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Use it when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shrink&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A tool result is too large to keep. &lt;code&gt;mode&lt;/code&gt; is &lt;code&gt;auto&lt;/code&gt; (default), &lt;code&gt;gloss&lt;/code&gt;, or &lt;code&gt;offline&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;note_add&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You decided something, found a cause, or have a next step.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;note_list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You only want the current notes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;note_clear&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The session is finished.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;recall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You need the stats, the rolled-up digest, and the notes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After restarting the MCP client, the practical habit is: ask the agent to call &lt;code&gt;shrink&lt;/code&gt; on bulky tool output, and &lt;code&gt;recall&lt;/code&gt; at the start of a long task. You can bake that into your &lt;code&gt;CLAUDE.md&lt;/code&gt;, Cursor rules, or Cline custom instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a shrink works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Detect.&lt;/strong&gt; &lt;code&gt;auto&lt;/code&gt; classifies the input as a log, a test run, a file dump (source or JSON), or generic text. Override with &lt;code&gt;--kind&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Press.&lt;/strong&gt; Duplicate lines collapse. Logs keep errors, warnings, and the head and tail. Test output keeps failures, nearby frames, and the summary. Source code keeps an outline of imports and declarations. JSON becomes a short description of its shape. Anything still huge is cut to a character cap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gloss.&lt;/strong&gt; If the press is still large and &lt;code&gt;CTXTRIM_API_KEY&lt;/code&gt; is set, ctxtrim sends &lt;strong&gt;the press — not the original blob&lt;/strong&gt; — to &lt;code&gt;POST {CTXTRIM_BASE_URL}/chat/completions&lt;/code&gt; and asks for a plain-text brief under &lt;code&gt;CTXTRIM_TARGET_CHARS&lt;/code&gt;. A gloss that comes back empty or longer than the press is thrown away. Results are cached under &lt;code&gt;CTXTRIM_HOME&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A few behaviors worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--offline&lt;/code&gt; never calls a model.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--gloss&lt;/code&gt; requires a key and exits with status 2 if it's missing.&lt;/li&gt;
&lt;li&gt;Provider errors fall back to the press, unless you explicitly asked for &lt;code&gt;--gloss&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Binary input isn't expanded; you get the original size and a one-line stub.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea behind the split is simple: the mechanical press is free and deterministic, and the cheap model only ever sees already-trimmed text, so the summary step stays much cheaper than having your main coding model read the raw output.&lt;/p&gt;

&lt;h3&gt;
  
  
  The session ledger
&lt;/h3&gt;

&lt;p&gt;Notes are stored per session as JSON under &lt;code&gt;$CTXTRIM_HOME/sessions/&lt;/code&gt; (default &lt;code&gt;~/.ctxtrim&lt;/code&gt;), one compact line per note, with optional tags like &lt;code&gt;decision&lt;/code&gt;, &lt;code&gt;blocker&lt;/code&gt;, or a filename stem. When active notes exceed &lt;code&gt;CTXTRIM_NOTE_BUDGET&lt;/code&gt;, the oldest lines roll up into a digest so the session stays small enough to paste back into a prompt.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;recall&lt;/code&gt; prints shrink totals (bytes in, bytes out, how much was kept), the digest, and current notes. Note that the shrunk brief itself isn't copied into the ledger — if you want the agent to remember a conclusion, write a note.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tuning
&lt;/h3&gt;

&lt;p&gt;Everything is an environment variable. The main knobs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CTXTRIM_PASSTHROUGH_CHARS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return text as-is at or below this size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CTXTRIM_GLOSS_THRESHOLD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3500&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gloss when the press is still at least this long&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CTXTRIM_TARGET_CHARS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1800&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Budget given to the model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CTXTRIM_PRESS_CHARS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;12000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cap on the press (what the model sees)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CTXTRIM_NOTE_BUDGET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Active note budget before rollup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Use it as a library
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shrink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Ledger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ctxtrim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;shrink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Ledger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature-auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Refresh flake is a clock skew, not a bad signature.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Credit
&lt;/h2&gt;

&lt;p&gt;The concept is &lt;strong&gt;inspired by &lt;a href="https://github.com/mksglu/context-mode" rel="noopener noreferrer"&gt;context-mode&lt;/a&gt;&lt;/strong&gt; by mksglu. ctxtrim is a separate project and is &lt;strong&gt;not affiliated&lt;/strong&gt; with it. If you're exploring this space, check out context-mode too.&lt;/p&gt;

&lt;h2&gt;
  
  
  About AtmoRouter: a cheap AI API gateway and OpenRouter alternative
&lt;/h2&gt;

&lt;p&gt;Out of the box, ctxtrim sends glosses to AtmoRouter using &lt;code&gt;zai/glm-5.3&lt;/code&gt;, which fits the "keep the summary step cheaper than the coding model" idea. Any OpenAI-compatible endpoint works too. &lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;AtmoRouter&lt;/a&gt; is a &lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;cheap AI API gateway&lt;/a&gt; that speaks both the OpenAI Chat Completions API and the Anthropic Messages API, so one key reaches 100+ models from Cursor, Cline, Claude Code, or any compatible client. It is pay-per-token with no subscription, accepts QRIS and USDC, and prices sit well below official rates: AtmoRouter advertises an average of 83.7% below official prices, which makes it an affordable LLM API and a practical OpenRouter alternative for agent workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If your agent keeps drowning in test output and logs, give it a try:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/bayuh85/ctxtrim" rel="noopener noreferrer"&gt;github.com/bayuh85/ctxtrim&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Feedback on the press heuristics is especially welcome — if there's a log or test format it handles badly, an issue with a sample is the most useful thing you can send.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>typescript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>grunt-proxy: a local proxy that trims your coding agent's prompts (and shows what it saved)</title>
      <dc:creator>bayuh85</dc:creator>
      <pubDate>Thu, 15 Oct 2026 17:25:31 +0000</pubDate>
      <link>https://dev.to/bayuh85/grunt-proxy-a-local-proxy-that-trims-your-coding-agents-prompts-and-shows-what-it-saved-40e2</link>
      <guid>https://dev.to/bayuh85/grunt-proxy-a-local-proxy-that-trims-your-coding-agents-prompts-and-shows-what-it-saved-40e2</guid>
      <description>&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; 👉 &lt;a href="https://github.com/bayuh85/grunt-proxy" rel="noopener noreferrer"&gt;github.com/bayuh85/grunt-proxy&lt;/a&gt; (MIT, Node.js 20+, TypeScript)&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;If you use Claude Code, Cursor, or Cline every day, you are sending a lot of text you didn't write: system prompts, tool descriptions, tool results, and your own "could you please…" phrasing. Every token of that is billed. And the replies often come back with a greeting, a recap, and a closing paragraph you'll never read.&lt;/p&gt;

&lt;p&gt;None of that is a big deal per request. Across a day of agent loops, it adds up, and most of us have no idea by how much.&lt;/p&gt;

&lt;p&gt;I wanted something that sits between the agent and the API, trims the obvious padding, asks the model to get to the point, and &lt;strong&gt;tells me what it actually saved&lt;/strong&gt; — without breaking code or tool calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  What grunt-proxy does
&lt;/h2&gt;

&lt;p&gt;grunt-proxy is a small local HTTP proxy for coding agents. For each request it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shortens the prompt&lt;/strong&gt; you were about to send (filler phrases, articles, common short forms — configurable).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asks the model for a terse reply&lt;/strong&gt; via a short, stable instruction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prints how many tokens and how many dollars that saved.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What it deliberately does &lt;em&gt;not&lt;/em&gt; do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replies stay in normal English. The proxy doesn't role-play and doesn't rewrite the model's response.&lt;/li&gt;
&lt;li&gt;Code, JSON, and tool calls stay intact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It speaks both the &lt;strong&gt;OpenAI Chat Completions&lt;/strong&gt; API and the &lt;strong&gt;Anthropic Messages&lt;/strong&gt; API (including the Anthropic SSE stream), so the same proxy works for Cursor, Cline, and Claude Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;p&gt;Requires Node.js 20 or newer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/bayuh85/grunt-proxy.git
&lt;span class="nb"&gt;cd &lt;/span&gt;grunt-proxy
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put your upstream key in &lt;code&gt;GRUNT_API_KEY&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt;. The default upstream is AtmoRouter (keys look like &lt;code&gt;ar-...&lt;/code&gt;), but you can point it anywhere — more on that below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;grunt-proxy now listens on &lt;code&gt;http://127.0.0.1:8642&lt;/code&gt;. Try a request through it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:8642/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$GRUNT_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "atmo/deepseek-v4.1-flash",
    "messages": [
      {"role": "user", "content": "Could you please explain the configuration options in the documentation? Thank you!"}
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Model ids change — check your provider's current catalog before relying on an example id.)&lt;/p&gt;

&lt;p&gt;Each request prints one line on stderr:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grunt  atmo/deepseek-v4.1-flash  in 1,204→640 (−46.8%)  out 180  saved $0.001692
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the running total, hit &lt;code&gt;GET /savings.txt&lt;/code&gt; or press Ctrl+C when the server is in the foreground:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grunt-proxy savings
requests              12
failed                0
input tokens          48,200 → 21,040  (−56.3%)
input tokens saved    27,160
output tokens         6,410
est. input $ saved    $0.081480
projected output $    $0.000000
est. $ saved          $0.081480
upstream billed       –
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be clear, the README labels those numbers as &lt;strong&gt;an illustration of the layout, not a benchmark&lt;/strong&gt;. Your savings depend on your prompts, your level setting, and your prices.&lt;/p&gt;

&lt;p&gt;For a compiled build: &lt;code&gt;npm run build &amp;amp;&amp;amp; npm start&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting it up with your agent
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;Claude Code (and the Anthropic SDK) appends &lt;code&gt;/v1/messages&lt;/code&gt; itself, so use the proxy origin &lt;strong&gt;without&lt;/strong&gt; &lt;code&gt;/v1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8642"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ar-your-key"&lt;/span&gt;
claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;grunt-proxy receives &lt;code&gt;POST /v1/messages&lt;/code&gt;, compresses it, and forwards it upstream, streaming included. It sets &lt;code&gt;anthropic-version: 2023-06-01&lt;/code&gt; if the client didn't send one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Start grunt-proxy (&lt;code&gt;npm run dev&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Cursor Settings → Models&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set the OpenAI API key to your upstream key. If &lt;code&gt;GRUNT_API_KEY&lt;/code&gt; is already set in &lt;code&gt;.env&lt;/code&gt;, any non-empty placeholder works — the proxy replaces the client key.&lt;/li&gt;
&lt;li&gt;Override the OpenAI base URL with &lt;code&gt;http://127.0.0.1:8642/v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a custom model whose id matches a model on your upstream, e.g. &lt;code&gt;atmo/deepseek-v4.1-flash&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Cline
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Start grunt-proxy.&lt;/li&gt;
&lt;li&gt;Set the API provider to &lt;strong&gt;OpenAI Compatible&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Base URL: &lt;code&gt;http://127.0.0.1:8642/v1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;API key: your upstream key (or a placeholder when &lt;code&gt;GRUNT_API_KEY&lt;/code&gt; is set).&lt;/li&gt;
&lt;li&gt;Model ID: a catalog id from your provider.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Quick reference:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client&lt;/th&gt;
&lt;th&gt;Base URL the client uses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cursor, Cline, OpenAI SDKs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://127.0.0.1:8642/v1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code, Anthropic SDK&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://127.0.0.1:8642&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What gets compressed (and what doesn't)
&lt;/h3&gt;

&lt;p&gt;grunt-proxy only edits text the model will &lt;em&gt;read&lt;/em&gt;: system and user text, assistant text, tool descriptions, and tool-result text. It leaves these alone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fenced code, inline code, and indented code lines&lt;/li&gt;
&lt;li&gt;URLs, file paths, and filenames&lt;/li&gt;
&lt;li&gt;JSON values, including tool-call arguments and tool parameter schemas&lt;/li&gt;
&lt;li&gt;camelCase and PascalCase identifiers&lt;/li&gt;
&lt;li&gt;image blocks and Anthropic &lt;code&gt;tool_use&lt;/code&gt; / &lt;code&gt;thinking&lt;/code&gt; blocks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Levels
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;What changes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Nothing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Courtesy and padded phrases ("could you please", "in order to", "thank you").&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Level 1, plus &lt;code&gt;a&lt;/code&gt;/&lt;code&gt;an&lt;/code&gt;/&lt;code&gt;the&lt;/code&gt; and a few other fillers, plus short forms like &lt;code&gt;config&lt;/code&gt;, &lt;code&gt;fn&lt;/code&gt;, &lt;code&gt;param&lt;/code&gt;, &lt;code&gt;impl&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Level 2, plus &lt;code&gt;of&lt;/code&gt;/&lt;code&gt;that&lt;/code&gt;, some contractions, and shorter forms like &lt;code&gt;cfg&lt;/code&gt;, &lt;code&gt;btwn&lt;/code&gt;, &lt;code&gt;w/o&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Level 2 is the default. If an agent starts misreading instructions, drop to level 1. Level 3 is lossier; only use it once you've checked the output.&lt;/p&gt;

&lt;p&gt;You can override per request with headers: &lt;code&gt;x-grunt: off&lt;/code&gt; forwards the body unchanged, and &lt;code&gt;x-grunt-level: 0–3&lt;/code&gt; / &lt;code&gt;x-grunt-terse: auto|always|off&lt;/code&gt; override the config for one call.&lt;/p&gt;

&lt;h3&gt;
  
  
  The terse instruction
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;GRUNT_TERSE=auto&lt;/code&gt; (the default), the proxy adds a one-paragraph instruction only when compression already pays for it, or when the prompt is roughly 800+ estimated tokens. It tells the model to answer in normal English, skip the greeting and recap, and keep code and tool calls valid.&lt;/p&gt;

&lt;p&gt;A nice detail: the instruction is the &lt;strong&gt;same bytes for a given level&lt;/strong&gt;, so a provider's prompt cache can still reuse the prefix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Honest savings math
&lt;/h3&gt;

&lt;p&gt;This is the part I appreciate most. The README is upfront about what's measured and what's estimated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The token delta is a &lt;strong&gt;local before/after estimate&lt;/strong&gt;, not a clone of any provider's tokenizer. It's consistent, which is what a savings figure needs. When the upstream returns usage, the actual counts are stored too.&lt;/li&gt;
&lt;li&gt;Dollars are &lt;code&gt;tokens saved × price / 1,000,000&lt;/code&gt;, using approximate public list prices for known model families, or &lt;code&gt;$1&lt;/code&gt; / &lt;code&gt;$3&lt;/code&gt; per million for unknown ids.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set &lt;code&gt;GRUNT_PRICE_IN&lt;/code&gt; / &lt;code&gt;GRUNT_PRICE_OUT&lt;/code&gt; to what you actually pay&lt;/strong&gt;, otherwise the estimate won't match your invoice (and may overstate savings if your provider is cheaper than list price).&lt;/li&gt;
&lt;li&gt;Output terseness is &lt;em&gt;requested, not measured&lt;/em&gt;. &lt;code&gt;projected output $&lt;/code&gt; stays &lt;code&gt;$0&lt;/code&gt; unless you explicitly set &lt;code&gt;GRUNT_ASSUME_OUTPUT_CUT&lt;/code&gt;, which is a labeled guess.&lt;/li&gt;
&lt;li&gt;Failed upstream calls count as failures and add &lt;code&gt;$0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The response headers &lt;code&gt;x-grunt-original-tokens&lt;/code&gt;, &lt;code&gt;x-grunt-compressed-tokens&lt;/code&gt;, and &lt;code&gt;x-grunt-saved-tokens&lt;/code&gt; expose the estimate per call. Saved tokens can even be &lt;strong&gt;negative&lt;/strong&gt; when the terse instruction costs more than compression saved — and the proxy says so instead of hiding it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you set &lt;code&gt;GRUNT_LOG_FILE&lt;/code&gt;, you get a JSONL savings ledger (prompts are not written), and &lt;code&gt;grunt-proxy --report .grunt-savings.jsonl&lt;/code&gt; reprints a report from it without starting the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Safe defaults
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Binds to &lt;code&gt;127.0.0.1&lt;/code&gt; by default, so the proxy doesn't become a public credit sink.&lt;/li&gt;
&lt;li&gt;No proxy-side request timeout by default, so long Claude Code or Cursor streams aren't cut off. Disconnecting the client aborts the upstream request.&lt;/li&gt;
&lt;li&gt;Oversized bodies (default limit 32 MiB) are rejected with HTTP 413.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Credit where it's due
&lt;/h2&gt;

&lt;p&gt;grunt-proxy is &lt;strong&gt;inspired by &lt;a href="https://github.com/JuliusBrussee/caveman" rel="noopener noreferrer"&gt;caveman&lt;/a&gt;&lt;/strong&gt; by Julius Brussee — the idea that you can strip a lot of words from what goes to and from a model without losing meaning. grunt-proxy takes that idea and packages it as a transparent proxy with a savings report, while keeping replies in normal English.&lt;/p&gt;

&lt;h2&gt;
  
  
  About AtmoRouter: a cheap AI API gateway and OpenRouter alternative
&lt;/h2&gt;

&lt;p&gt;Full disclosure: grunt-proxy's default upstream is AtmoRouter, but any OpenAI- or Anthropic-compatible upstream works. &lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;AtmoRouter&lt;/a&gt; is a &lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;cheap AI API gateway&lt;/a&gt; that speaks both the OpenAI Chat Completions API and the Anthropic Messages API, so one key reaches 100+ models from Cursor, Cline, Claude Code, or any compatible client. It is pay-per-token with no subscription, accepts QRIS and USDC, and prices sit well below official rates: AtmoRouter advertises an average of 83.7% below official prices, which makes it an affordable LLM API and a practical OpenRouter alternative for agent workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you run coding agents all day and want to see where your tokens go, give it a spin:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/bayuh85/grunt-proxy" rel="noopener noreferrer"&gt;github.com/bayuh85/grunt-proxy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Issues and PRs welcome — especially reports of prompts where a compression level changed the model's behavior, since that's exactly what the level knob is for.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
    <item>
      <title>AtmoRouter: A Cheaper OpenRouter Alternative (OpenAI-Compatible, Pay-Per-Token)</title>
      <dc:creator>bayuh85</dc:creator>
      <pubDate>Thu, 15 Oct 2026 17:16:35 +0000</pubDate>
      <link>https://dev.to/bayuh85/atmorouter-a-cheaper-openrouter-alternative-openai-compatible-pay-per-token-nco</link>
      <guid>https://dev.to/bayuh85/atmorouter-a-cheaper-openrouter-alternative-openai-compatible-pay-per-token-nco</guid>
      <description>&lt;p&gt;OpenRouter made a great idea mainstream: one API, many models. Subscription coding tools like Command Code bundle model credits into monthly plans. Both are solid options. But if your main goal is &lt;strong&gt;paying less per token for frontier models without signing up for a plan&lt;/strong&gt;, there's another option worth benchmarking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;AtmoRouter&lt;/a&gt; is an &lt;strong&gt;OpenAI-compatible API gateway&lt;/strong&gt;. You get one &lt;code&gt;ar-&lt;/code&gt; key and one base URL, pay per million tokens, and requests are routed to healthy capacity. Moving existing OpenRouter code over takes two lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why look for an OpenRouter alternative?
&lt;/h2&gt;

&lt;p&gt;OpenRouter is transparent about how it charges. Per its FAQ, it passes provider pricing through &lt;strong&gt;without markup&lt;/strong&gt; and makes money from a &lt;strong&gt;5.5% fee (minimum $0.80) on credit purchases&lt;/strong&gt;, or a 5% fee for crypto payments. That's fair, but it also means you pay roughly the official list price for every model.&lt;/p&gt;

&lt;p&gt;Command Code takes a different approach. It sells &lt;strong&gt;monthly plans&lt;/strong&gt; (for example Go at $1/mo, GOAT at $10/mo, Pro at $20/mo, Max 10× at $100/mo, all plus a processing fee) that include a pool of credits. Those credits have rolling &lt;strong&gt;5-hour and weekly usage caps&lt;/strong&gt;, and per-model allowances on the lower tiers. Pay-as-you-go API access through its Provider plan costs $15/mo.&lt;/p&gt;

&lt;p&gt;AtmoRouter takes a third approach: &lt;strong&gt;no subscription, no seats, no minimum, and per-token rates listed below official pricing for many models.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Price comparison: AtmoRouter vs OpenRouter (checked October 16, 2026)
&lt;/h2&gt;

&lt;p&gt;All prices are in USD per 1M tokens. They were pulled from each service's public &lt;code&gt;/models&lt;/code&gt; API on Oct 16, 2026. Prices change, so check the live lists before you decide.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;OpenRouter (input / output)&lt;/th&gt;
&lt;th&gt;AtmoRouter model ID&lt;/th&gt;
&lt;th&gt;AtmoRouter (input / output)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$2.00 / $10.00&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cc/claude-sonnet-5-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$0.50 / $2.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 4.6&lt;/td&gt;
&lt;td&gt;$3.00 / $15.00&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cc/claude-sonnet-4-6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$0.75 / $3.75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.6&lt;/td&gt;
&lt;td&gt;$5.00 / $25.00&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cc/claude-opus-4-6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$1.25 / $6.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Astra&lt;/td&gt;
&lt;td&gt;$10.00 / $50.00&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cx/gpt-6-astra&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$0.10 / $0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Sol&lt;/td&gt;
&lt;td&gt;$2.00 / $10.00&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cx/gpt-6-sol&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$0.045 / $0.225&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenRouter's numbers are its pass-through list prices. Its 5.5% credit-purchase fee comes on top.&lt;/li&gt;
&lt;li&gt;AtmoRouter often lists the same model under several upstream families (for example &lt;code&gt;ag/&lt;/code&gt;, &lt;code&gt;cc/&lt;/code&gt;, and &lt;code&gt;cx/&lt;/code&gt; prefixes), each with its own price, context window, and capacity. The table shows one option per model. Check &lt;code&gt;/v1/models&lt;/code&gt; for every variant.&lt;/li&gt;
&lt;li&gt;Command Code isn't in the table because it's mainly priced as subscription bundles, not one public per-token rate per model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you get with AtmoRouter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One key, all models.&lt;/strong&gt; It's a drop-in replacement for OpenAI: change the base URL and keep your SDK. Anthropic-compatible clients work too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing to healthy capacity.&lt;/strong&gt; Exhausted or degraded sources are skipped automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exact per-token metering.&lt;/strong&gt; Input and output are priced separately, based on the upstream usage report.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real streaming.&lt;/strong&gt; Full SSE passthrough in OpenAI's format, with usage on the final frame. Stop the stream, stop paying.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keys with guardrails.&lt;/strong&gt; Create unlimited keys with per-key rate limits and model allowlists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditable usage.&lt;/strong&gt; Every request is logged with tokens, latency, and cost, and you can export it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failed upstream requests are never billed.&lt;/strong&gt; Every response also includes an &lt;code&gt;X-Atmorouter-Cost&lt;/code&gt; header showing the exact amount deducted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migrating from OpenRouter in two lines
&lt;/h2&gt;

&lt;p&gt;If you use the OpenAI SDK with OpenRouter, change the base URL and the key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;# base_url="https://openrouter.ai/api/v1",   # before
&lt;/span&gt;    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://atmorouter.dev/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# after
&lt;/span&gt;    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ar-xxxxxxxxxxxx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="c1"&gt;# your AtmoRouter key
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cc/claude-sonnet-5-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# use an AtmoRouter model ID
&lt;/span&gt;    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain PKCE.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node.js works the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://atmorouter.dev/v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// was https://openrouter.ai/api/v1&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ATMOROUTER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For CLIs and coding agents, environment variables are enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://atmorouter.dev/v1"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ar-xxxxxxxxxxxx"&lt;/span&gt;

&lt;span class="c"&gt;# List every model (with pricing) you can call:&lt;/span&gt;
curl &lt;span class="nv"&gt;$OPENAI_BASE_URL&lt;/span&gt;/models &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$OPENAI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Model IDs differ between the two services (OpenRouter uses &lt;code&gt;anthropic/claude-sonnet-5.5&lt;/code&gt;, AtmoRouter uses &lt;code&gt;cc/claude-sonnet-5-5&lt;/code&gt;), so update your model strings when you switch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How AtmoRouter billing works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Deposit &lt;strong&gt;USDC on Solana&lt;/strong&gt; or pay with &lt;strong&gt;QRIS&lt;/strong&gt;. No card on file, no recurring charge.&lt;/li&gt;
&lt;li&gt;Input and output tokens are counted separately. Cached input bills at 10% of the input rate.&lt;/li&gt;
&lt;li&gt;The cost formula is public:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cost = ((in − cached) × input_price + cached × input_price × 0.10 + out × output_price) / 1M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Your balance is deducted when a request settles. An empty balance blocks new calls and never overdraws.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OpenRouter vs Command Code vs AtmoRouter at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;OpenRouter&lt;/th&gt;
&lt;th&gt;Command Code&lt;/th&gt;
&lt;th&gt;AtmoRouter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pricing model&lt;/td&gt;
&lt;td&gt;Pass-through provider pricing + 5.5% credit fee&lt;/td&gt;
&lt;td&gt;Monthly plans with included credits; $15/mo Provider API plan&lt;/td&gt;
&lt;td&gt;Pay-per-token, no subscription&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage caps&lt;/td&gt;
&lt;td&gt;Provider rate limits&lt;/td&gt;
&lt;td&gt;5-hour and weekly caps on plan credits&lt;/td&gt;
&lt;td&gt;Per-key rate limits you set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI-compatible&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (Provider plan)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment&lt;/td&gt;
&lt;td&gt;Cards, AliPay, USDC&lt;/td&gt;
&lt;td&gt;Subscription + top-ups&lt;/td&gt;
&lt;td&gt;USDC on Solana, QRIS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Is AtmoRouter right for you?
&lt;/h2&gt;

&lt;p&gt;If you want the widest catalog and provider-level routing controls, OpenRouter is excellent. If you want a coding agent with bundled credits, Command Code fits. If you want &lt;strong&gt;frontier models at per-token rates below list price, no monthly plan, and a migration measured in lines of code&lt;/strong&gt;, give AtmoRouter a try:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account (email only).&lt;/li&gt;
&lt;li&gt;Top up with USDC on Solana or QRIS.&lt;/li&gt;
&lt;li&gt;Generate an &lt;code&gt;ar-&lt;/code&gt; key.&lt;/li&gt;
&lt;li&gt;Point your existing client at &lt;code&gt;https://atmorouter.dev/v1&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://atmorouter.dev" rel="noopener noreferrer"&gt;Try AtmoRouter at atmorouter.dev&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Questions or model requests? Join the AtmoRouter Discord, linked from the site.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pricing sources, checked Oct 16, 2026: openrouter.ai/docs/faq, openrouter.ai/api/v1/models, commandcode.ai/pricing, commandcode.ai/docs/resources/usage-limits, atmorouter.dev/v1/models.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>openrouter</category>
      <category>api</category>
    </item>
  </channel>
</rss>
