<?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: OanhDuong</title>
    <description>The latest articles on DEV Community by OanhDuong (@oanhduong).</description>
    <link>https://dev.to/oanhduong</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%2F3888189%2F041d603a-5169-4dcf-ad48-7b1f43c38d0f.png</url>
      <title>DEV Community: OanhDuong</title>
      <link>https://dev.to/oanhduong</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oanhduong"/>
    <language>en</language>
    <item>
      <title>I got tired of wasting AI tokens on Linux commands</title>
      <dc:creator>OanhDuong</dc:creator>
      <pubDate>Wed, 09 Sep 2026 09:25:22 +0000</pubDate>
      <link>https://dev.to/oanhduong/i-got-tired-of-wasting-ai-tokens-on-linux-commands-102e</link>
      <guid>https://dev.to/oanhduong/i-got-tired-of-wasting-ai-tokens-on-linux-commands-102e</guid>
      <description>&lt;p&gt;I didn’t plan to build another open source project.&lt;/p&gt;

&lt;p&gt;I was just too cheap to waste tokens.&lt;/p&gt;

&lt;p&gt;While using Claude, I’d ask things like git status, git diff, or docker ps in natural language… then realize my terminal could’ve answered in 20ms. More than once, I hit Ctrl+C halfway through thinking, “Nope, not spending tokens on that.” 😌&lt;/p&gt;

&lt;p&gt;That annoyance became a weekend project.&lt;/p&gt;

&lt;p&gt;Token ninja: &lt;a href="https://github.com/oanhduong/token-ninja" rel="noopener noreferrer"&gt;https://github.com/oanhduong/token-ninja&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvm6vj5d10z54d1671n4m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvm6vj5d10z54d1671n4m.png" alt="how token-ninja works" width="799" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ironically, I learned much more than I expected about Claude Code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UserPromptSubmit enables true zero-token interception before a prompt reaches the model.&lt;/li&gt;
&lt;li&gt;Good AI tooling isn’t always about adding more AI, it’s often about knowing when not to use it.&lt;/li&gt;
&lt;li&gt;The hardest part wasn’t executing commands, it was deciding which requests should stay local and which deserved Claude.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I started by trying to save a few tokens.&lt;/p&gt;

&lt;p&gt;I ended up learning a lot about Claude Code, prompt hooks, MCP, and where the boundary between deterministic software and LLMs should be.&lt;/p&gt;

&lt;p&gt;Sometimes the best learning starts with being annoyed enough to build something.&lt;/p&gt;

&lt;p&gt;Open source: &lt;a href="https://github.com/oanhduong/token-ninja" rel="noopener noreferrer"&gt;https://github.com/oanhduong/token-ninja&lt;/a&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>opensource</category>
      <category>tools</category>
    </item>
    <item>
      <title>How I Built token-ninja — A Claude Code Companion That Saves Tokens on Shell Commands</title>
      <dc:creator>OanhDuong</dc:creator>
      <pubDate>Mon, 20 Apr 2026 04:47:20 +0000</pubDate>
      <link>https://dev.to/oanhduong/how-i-built-token-ninja-a-claude-code-companion-that-saves-tokens-on-shell-commands-6pb</link>
      <guid>https://dev.to/oanhduong/how-i-built-token-ninja-a-claude-code-companion-that-saves-tokens-on-shell-commands-6pb</guid>
      <description>&lt;p&gt;I use Claude Code every day. And every day, I watched it burn tokens on things like &lt;code&gt;git status&lt;/code&gt;, &lt;code&gt;docker ps&lt;/code&gt;, &lt;code&gt;npm test&lt;/code&gt; — commands my shell could answer in milliseconds.&lt;br&gt;
It bothered me enough to build something about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;br&gt;
When you're deep in a coding session, you ask Claude a lot of small operational questions. What branch am I on? Are there uncommitted changes? What's running on port 3000?&lt;br&gt;
None of these need a language model. They need a shell. But because they're typed into Claude's input, they become API calls — with input tokens, output tokens, latency, and cost.&lt;br&gt;
Multiply that across a full workday and it adds up fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The idea&lt;/strong&gt;&lt;br&gt;
What if something sat between you and Claude, caught those deterministic commands, ran them locally, and handed the result back — before Claude ever saw them?&lt;br&gt;
That's token-ninja.&lt;br&gt;
When you type a command it recognizes, runs it locally, and returns the result in original colors with a small dimmed footer line showing the save. The model is never invoked. Zero input tokens. Zero output tokens. Everything conversational flows through to Claude untouched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The install experience&lt;/strong&gt;&lt;br&gt;
I wanted zero friction. One command, no config, works on the next session.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npm install -g token-ninja&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A postinstall hook registers token-ninja as an MCP server in Claude Code, Cursor, and Claude Desktop automatically. Existing configs are preserved. A backup is created before any file is touched. Run ninja uninstall to remove everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I learned building it&lt;/strong&gt;&lt;br&gt;
A few things surprised me along the way.&lt;br&gt;
Safety is harder than classification. Getting the classifier right took a weekend. Getting the safety layer right — especially evasion tricks like homoglyph attacks, base64-encoded commands, and chained operators — took much longer. I ended up checking safety twice: once on the raw input, once on the resolved command after template expansion.&lt;br&gt;
Natural language is messier than I expected. "Show me recent commits" and "what did we push last" and "list the last 10 commits" all mean the same thing. Building keyword groups that cover real usage patterns without false positives required a lot of iteration and real command fixtures.&lt;br&gt;
Auto-setup is a trust problem. Writing to someone's config files during postinstall is a big ask. I added backups, dry-run mode, and &lt;code&gt;ninja uninstall&lt;/code&gt; specifically because I'd want those guarantees myself before trusting any tool that touches my dev environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;npm install -g token-ninja&lt;/code&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/oanhduong/token-ninja" rel="noopener noreferrer"&gt;https://github.com/oanhduong/token-ninja&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's free and open source. If you use Claude Code daily, give it a try and let me know what commands it misses — that's the fastest way to help.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>opensource</category>
      <category>devtools</category>
      <category>token</category>
    </item>
  </channel>
</rss>
