<?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: Wahaj</title>
    <description>The latest articles on DEV Community by Wahaj (@mwahaj36).</description>
    <link>https://dev.to/mwahaj36</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%2F4000080%2F37d640c3-4ff9-4fc7-ad88-9bb8281df642.gif</url>
      <title>DEV Community: Wahaj</title>
      <link>https://dev.to/mwahaj36</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mwahaj36"/>
    <language>en</language>
    <item>
      <title>Why I Didn't Use an AST Parser in My AI Code Commenter (And Why That's the Right Call)</title>
      <dc:creator>Wahaj</dc:creator>
      <pubDate>Wed, 24 Jun 2026 08:15:26 +0000</pubDate>
      <link>https://dev.to/mwahaj36/why-i-didnt-use-an-ast-parser-in-my-ai-code-commenter-and-why-thats-the-right-call-233j</link>
      <guid>https://dev.to/mwahaj36/why-i-didnt-use-an-ast-parser-in-my-ai-code-commenter-and-why-thats-the-right-call-233j</guid>
      <description>&lt;p&gt;Every AI code documentation tool I looked at had the same problem: it sent your entire file to an LLM and asked for it back with comments added.&lt;/p&gt;

&lt;p&gt;That means the model is rewriting your code. Every single line. Including the ones it wasn't supposed to touch.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://www.npmjs.com/package/devsplain" rel="noopener noreferrer"&gt;devsplain&lt;/a&gt; to solve this. It adds JSDoc and inline comments to 22 programming languages using any LLM you choose — without touching a single line of your executable code. If it can't guarantee that, it aborts.&lt;/p&gt;

&lt;p&gt;Here's the architecture decision that made that possible, and why the "obvious" solution would have destroyed the tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Every Other Tool
&lt;/h2&gt;

&lt;p&gt;Tools like &lt;code&gt;ai-docs&lt;/code&gt; and &lt;code&gt;jsdoc-builder&lt;/code&gt; solve the safety problem correctly: they use an &lt;strong&gt;AST parser&lt;/strong&gt;. Instead of asking the LLM to reproduce your whole file, they parse your code into a syntax tree, extract just the function signatures, ask the LLM only for comment text, and splice it back in programmatically.&lt;/p&gt;

&lt;p&gt;The LLM never sees your logic. It can't corrupt what it can't touch.&lt;/p&gt;

&lt;p&gt;There's one catch: &lt;strong&gt;AST parsers are language-specific.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ai-docs&lt;/code&gt; → JavaScript/TypeScript only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jsdoc-builder&lt;/code&gt; → JavaScript/TypeScript only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GenAIScript&lt;/code&gt; (Microsoft) → TypeScript only
Every single one of them. Because Babel and Acorn only understand JS/TS. If you want Python, you need a Python AST library. If you want Java, you need a Java AST library. If you want to support 22 languages the way &lt;code&gt;devsplain&lt;/code&gt; does — JavaScript, TypeScript, Python, Go, Rust, Swift, Kotlin, C, C++, Java, C#, Ruby, PHP, Dart, and more — you'd need &lt;code&gt;tree-sitter&lt;/code&gt; with native C++ grammar binaries compiled for every language and every platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That turns a zero-dependency CLI that installs in under a second into a native compilation nightmare that fails on half the machines you try it on.&lt;/p&gt;

&lt;p&gt;So: use an AST and lose the polyglot story, or don't use an AST and lose the safety guarantee.&lt;/p&gt;

&lt;p&gt;Neither was acceptable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Third Option: Line-Splicing With Round-Trip Verification
&lt;/h2&gt;

&lt;p&gt;The insight was this: you don't need an AST to guarantee code safety. You need a &lt;strong&gt;verification step&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's what &lt;code&gt;devsplain&lt;/code&gt; does instead:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Prepend line numbers to the source before sending it&lt;/strong&gt;&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateBackoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="mi"&gt;3&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;&lt;strong&gt;2. Ask the LLM to return only a JSON array — no source code&lt;/strong&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"// [ds] Calculates exponential backoff with a 30s ceiling"&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 LLM acts as a pure inference engine. It reads your code, decides where comments belong and what they should say, and returns structured data. It never touches the source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The CLI does the actual splicing&lt;/strong&gt;&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="c1"&gt;// Sort descending so earlier insertions don't shift later indices&lt;/span&gt;
&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;comment&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;&lt;strong&gt;4. Round-trip verification before any write&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the part that makes the safety claim checkable:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reconstructed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isDevsplainComment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&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;original&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originalLines&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;arraysEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reconstructed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Integrity check failed. Aborting.&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;Filter out every inserted comment. What remains must be byte-for-byte identical to the original source. If a single line has shifted, been modified, or gone missing — for any reason — the write is aborted.&lt;/p&gt;

&lt;p&gt;This verification works for every language without any language-specific logic. It's just array comparison.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;[ds]&lt;/code&gt; Tag: Making AI Comments Fully Reversible
&lt;/h2&gt;

&lt;p&gt;Every comment &lt;code&gt;devsplain&lt;/code&gt; generates is forcibly prefixed with a &lt;code&gt;[ds]&lt;/code&gt; tag:&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="c1"&gt;// [ds] Validates that the input array is non-empty before processing&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// your original comment here — untouched&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Empty input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&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;This gives you a &lt;strong&gt;local, deterministic scrubber&lt;/strong&gt; with zero API calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;devsplain lib/ &lt;span class="nt"&gt;--clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lexer scans for &lt;code&gt;[ds]&lt;/code&gt;-prefixed lines and removes only those — your manual comments are completely preserved. No LLM needed, no internet required, no API key. Just a state machine and string matching.&lt;/p&gt;

&lt;p&gt;This also means &lt;code&gt;devsplain&lt;/code&gt; is fully reversible. Run it on your codebase, decide you don't like the output, run &lt;code&gt;--clean&lt;/code&gt;, and you're back to exactly where you started — verified by the same round-trip diff.&lt;/p&gt;




&lt;h2&gt;
  
  
  String Literal Guardrails
&lt;/h2&gt;

&lt;p&gt;One edge case that breaks naive line-splicing: inserting a comment inside a multi-line string or template literal.&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="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    SELECT *        ← inserting a comment here would corrupt the string
    FROM users
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;devsplain&lt;/code&gt; tracks lexical state across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template literals (&lt;code&gt;`&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Single and double quoted strings&lt;/li&gt;
&lt;li&gt;Python triple-quote docstrings (&lt;code&gt;"""&lt;/code&gt; and &lt;code&gt;'''&lt;/code&gt;)
If the LLM targets a line that falls within a string region, the insertion is silently skipped for that line. No crash, no corruption — just a skipped comment with a warning logged.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Git Hook Automation
&lt;/h2&gt;

&lt;p&gt;This is the use case where &lt;code&gt;devsplain&lt;/code&gt; actually beats interactive AI editors on speed: &lt;strong&gt;batch documentation in CI and git hooks&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;devsplain &lt;span class="nt"&gt;--setup-hook&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs a two-stage git hook:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pre-commit&lt;/strong&gt;: Runs your test suite. Blocks the commit on failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-commit&lt;/strong&gt;: Detects modified files, runs &lt;code&gt;devsplain&lt;/code&gt; on them, and commits the generated comments automatically as &lt;code&gt;docs: auto-generated comments by devsplain&lt;/code&gt;.
The post-commit runs with &lt;code&gt;--no-verify&lt;/code&gt; to prevent recursive invocation. If the network is down or the API key is missing, it logs a warning and exits cleanly — your original commit is never blocked.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can bypass it for any commit:&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;SKIP_DEVSPLAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"my message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Zero Dependencies in Production
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;devsplain&lt;/code&gt; relies entirely on Node.js built-ins: &lt;code&gt;fs&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;readline&lt;/code&gt;. No SDK lock-in, no supply chain risk, no native compilation step. The only devDependency is Jest.&lt;/p&gt;

&lt;p&gt;This was a deliberate constraint. Adding the OpenAI SDK would have been convenient, but it would have also pulled in a dependency tree and introduced exactly the kind of install friction the tool was designed to avoid. Native &lt;code&gt;fetch&lt;/code&gt; handles the HTTP calls. A plain JSON config file in &lt;code&gt;~/.devsplainrc&lt;/code&gt; handles state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Supported Languages
&lt;/h2&gt;

&lt;p&gt;JavaScript, JSX, TypeScript, TSX, HTML, CSS, SCSS, Vue, Svelte, Python, Java, C, C++, C#, Go, Ruby, PHP, Rust, Swift, Kotlin, Dart, Shell&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run without installing&lt;/span&gt;
npx devsplain src/index.js &lt;span class="nt"&gt;--dry-run&lt;/span&gt;

&lt;span class="c"&gt;# Install globally&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; devsplain

&lt;span class="c"&gt;# Document an entire directory&lt;/span&gt;
devsplain src/ &lt;span class="nt"&gt;--full&lt;/span&gt;

&lt;span class="c"&gt;# Undo everything devsplain added&lt;/span&gt;
devsplain src/ &lt;span class="nt"&gt;--clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The setup wizard will prompt you for your LLM provider (Groq, Gemini, OpenAI, or any OpenAI-compatible endpoint like Ollama or LMStudio).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture Decision in One Sentence
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;AST tools are 100% safe for one language. &lt;code&gt;devsplain&lt;/code&gt; uses round-trip diff verification to achieve the same guarantee for every language, without a single native dependency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That tradeoff is the whole project. If you're working in a polyglot codebase — a TypeScript frontend, Python backend, and Swift iOS app in the same repo — &lt;code&gt;devsplain&lt;/code&gt; is currently the only tool that can document all three in a single pass.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/mwahaj36/devsplain" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.npmjs.com/package/devsplain" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>node</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
