<?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: Federico</title>
    <description>The latest articles on DEV Community by Federico (@federico_6767aae26b9b71f4).</description>
    <link>https://dev.to/federico_6767aae26b9b71f4</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2695475%2F62dbd2f4-9927-4d23-9559-8e559ea730b4.png</url>
      <title>DEV Community: Federico</title>
      <link>https://dev.to/federico_6767aae26b9b71f4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/federico_6767aae26b9b71f4"/>
    <language>en</language>
    <item>
      <title>I got tired of writing commit messages. So I built a CLI.</title>
      <dc:creator>Federico</dc:creator>
      <pubDate>Tue, 14 Apr 2026 11:21:50 +0000</pubDate>
      <link>https://dev.to/federico_6767aae26b9b71f4/i-got-tired-of-writing-commit-messages-so-i-built-a-cli-44fa</link>
      <guid>https://dev.to/federico_6767aae26b9b71f4/i-got-tired-of-writing-commit-messages-so-i-built-a-cli-44fa</guid>
      <description>&lt;p&gt;Here's a workflow problem that isn't dramatic but adds up: you finish a focused two-hour refactor, stage the changes, and then sit there staring at the commit prompt. "fix stuff" isn't it. You write something real, but it takes three minutes and you're not fully satisfied with it anyway.&lt;/p&gt;

&lt;p&gt;That's the thing that made me build devtools-ai. Not a grand vision — just a small, persistent friction point that I kept noticing.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I built
&lt;/h3&gt;

&lt;p&gt;devtools-ai is a Python CLI with five AI-powered commands for common dev tasks. Install it once, point it at an OpenRouter API key, and it's available in any terminal on any OS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;devtools-ai-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three tools are free. Two are Pro ($29 one-time, no subscription).&lt;/p&gt;

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

&lt;p&gt;All five commands follow the same pattern: read your code or diff, send it to an AI model via OpenRouter, return structured output in the terminal. No browser tab, no copy-pasting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commit messages from your staged diff:&lt;/strong&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="nv"&gt;$ &lt;/span&gt;git add src/auth/
&lt;span class="nv"&gt;$ &lt;/span&gt;devtools-ai commit

Suggested message:
refactor&lt;span class="o"&gt;(&lt;/span&gt;auth&lt;span class="o"&gt;)&lt;/span&gt;: extract token validation into standalone service

Separates JWT parsing and expiry logic from the request handler.
Simplifies future OAuth provider additions.

Commit with this message? &lt;span class="o"&gt;[&lt;/span&gt;y/N]: y
&lt;span class="o"&gt;[&lt;/span&gt;main 4f3a91c] refactor&lt;span class="o"&gt;(&lt;/span&gt;auth&lt;span class="o"&gt;)&lt;/span&gt;: extract token validation into standalone service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;-y&lt;/code&gt; to skip the confirmation and commit immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI code review before opening a PR:&lt;/strong&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="nv"&gt;$ &lt;/span&gt;devtools-ai review

&lt;span class="nt"&gt;---&lt;/span&gt; Review: src/payments/checkout.py &lt;span class="nt"&gt;---&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;SECURITY] Line 42: User-supplied redirect_url is not validated.
An attacker could supply an open redirect. Consider allowlisting
known domains or using urllib.parse to enforce the same origin.

&lt;span class="o"&gt;[&lt;/span&gt;PERFORMANCE] Line 78: get_user_cart&lt;span class="o"&gt;()&lt;/span&gt; is called inside the loop.
This hits the DB once per item. Move the call outside the loop
or use a bulk fetch.

3 issues found &lt;span class="o"&gt;(&lt;/span&gt;1 high, 1 medium, 1 low&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause analysis from an error log:&lt;/strong&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="nv"&gt;$ &lt;/span&gt;devtools-ai bug deploy_error.log &lt;span class="nt"&gt;--context&lt;/span&gt; src/config/loader.py

Root cause: config/loader.py attempts to call .get&lt;span class="o"&gt;()&lt;/span&gt; on the &lt;span class="k"&gt;return
&lt;/span&gt;value of load_yaml&lt;span class="o"&gt;()&lt;/span&gt;, but load_yaml&lt;span class="o"&gt;()&lt;/span&gt; returns None when the config
file is missing or empty &lt;span class="o"&gt;(&lt;/span&gt;line 34&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

The file path is constructed from ENV_CONFIG_PATH, which is not &lt;span class="nb"&gt;set
&lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;the production environment.

Likely fix: validate ENV_CONFIG_PATH before attempting to &lt;span class="nb"&gt;read&lt;/span&gt;,
or add a None check after load_yaml&lt;span class="o"&gt;()&lt;/span&gt; returns.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other two commands: &lt;code&gt;explain &amp;lt;file&amp;gt;&lt;/code&gt; gives a plain-English walkthrough of any source file, and &lt;code&gt;doc &amp;lt;file&amp;gt;&lt;/code&gt; generates Google or NumPy-style docstrings for functions that are missing them.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenRouter integration
&lt;/h3&gt;

&lt;p&gt;devtools-ai doesn't hardcode a provider. It routes through OpenRouter, which means you configure one API key in &lt;code&gt;~/.devtools-ai/config.yaml&lt;/code&gt; and can use GPT-4o, Claude 3.5 Sonnet, Gemini, Llama 3, or any other model OpenRouter supports. Switch models with a single line change. Your key stays local — I never see it.&lt;/p&gt;

&lt;p&gt;Cost per command with GPT-4o is typically $0.005–$0.02 depending on diff size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free vs Pro
&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;Free&lt;/th&gt;
&lt;th&gt;Pro&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;commit&lt;/code&gt; — semantic commit messages&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;explain&lt;/code&gt; — plain-English file explainer&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;doc&lt;/code&gt; — docstring generator&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;review&lt;/code&gt; — staged diff code review&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;bug&lt;/code&gt; — error log root cause analysis&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;$29 one-time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Get started in 3 steps
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;devtools-ai-cli

&lt;span class="c"&gt;# 2. Add your OpenRouter API key to ~/.devtools-ai/config.yaml&lt;/span&gt;
&lt;span class="c"&gt;#    Get a free key at https://openrouter.ai/&lt;/span&gt;

&lt;span class="c"&gt;# 3. Run your first command&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
devtools-ai commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you buy Pro, activate with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;devtools-ai activate YOUR-LICENSE-KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;The code is on GitHub: &lt;a href="https://github.com/Eutectico/devtools-ai" rel="noopener noreferrer"&gt;https://github.com/Eutectico/devtools-ai&lt;/a&gt;&lt;br&gt;
PyPI: &lt;a href="https://pypi.org/project/devtools-ai-cli/" rel="noopener noreferrer"&gt;https://pypi.org/project/devtools-ai-cli/&lt;/a&gt;&lt;br&gt;
Pro License: &lt;a href="https://3697225130452.gumroad.com/l/apqzbp" rel="noopener noreferrer"&gt;https://3697225130452.gumroad.com/l/apqzbp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One honest caveat: on very large diffs, the commit message quality degrades — it tends to go generic. That's something I'm actively working on. Everything else has been reliable in daily use.&lt;/p&gt;

&lt;p&gt;If you try it, feedback is welcome — issues, pull requests, or just a comment here.&lt;/p&gt;

</description>
      <category>python</category>
      <category>cli</category>
      <category>ai</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
