<?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>Forem: I.D.</title>
    <description>The latest articles on Forem by I.D. (@idieod).</description>
    <link>https://forem.com/idieod</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%2F3656738%2F536113ef-4fa4-4a53-ac78-f0c286e08967.png</url>
      <title>Forem: I.D.</title>
      <link>https://forem.com/idieod</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/idieod"/>
    <language>en</language>
    <item>
      <title>I made a CLI tool to search "TODO"s and distributed on Homebrew</title>
      <dc:creator>I.D.</dc:creator>
      <pubDate>Tue, 30 Dec 2025 13:19:03 +0000</pubDate>
      <link>https://forem.com/idieod/i-made-a-cli-tool-to-search-todos-and-distributed-on-homebrew-4a87</link>
      <guid>https://forem.com/idieod/i-made-a-cli-tool-to-search-todos-and-distributed-on-homebrew-4a87</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In this post, I will write about the story I made a CLI tool and distributed it, also serves as note to self.&lt;br&gt;
This post includes following contents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A brief explanation of Rust&lt;/li&gt;
&lt;li&gt;Notes on how to distribute your tools via homebrew&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The tool I made
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/I-Dieod" rel="noopener noreferrer"&gt;
        I-Dieod
      &lt;/a&gt; / &lt;a href="https://github.com/I-Dieod/TodoScanner" rel="noopener noreferrer"&gt;
        TodoScanner
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;TodoScanner&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A fast, friendly tool to scan your codebase for TODOs, FIXMEs, and other actionable comments. Built in Rust for speed and reliability.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Scan directories recursively for annotated comments (e.g., &lt;code&gt;TODO&lt;/code&gt;, &lt;code&gt;FIXME&lt;/code&gt;, &lt;code&gt;NOTE&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Supports common source file extensions (Rust, JS/TS, Python, etc.)&lt;/li&gt;
&lt;li&gt;Configurable markers and ignore patterns&lt;/li&gt;
&lt;li&gt;Clean, readable output with file paths and line numbers&lt;/li&gt;
&lt;li&gt;Exit codes suitable for CI workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;brew tap I-Dieod/tap
brew install tds&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Scan current directory&lt;/span&gt;
tds

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Scan a specific path with default markers&lt;/span&gt;
tds path/to/project&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Example Output&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/I-Dieod/TodoScanner/static/usage_example.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FI-Dieod%2FTodoScanner%2Fstatic%2Fusage_example.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Roadmap&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Markdown/HTML report output&lt;/li&gt;
&lt;li&gt;Interactive TUI&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contributing&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Issues and PRs are welcome. Please open an issue with a clear description and minimal repro if you find a bug.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;License&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;MIT License. See &lt;code&gt;LICENSE&lt;/code&gt; for details.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Acknowledgments&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Inspired by the need to keep TODOs visible and actionable during daily development and CI.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/I-Dieod/TodoScanner" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  Target readers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Someone who wants to distribute their own tools&lt;/li&gt;
&lt;li&gt;Someone who wants to know how to read files and extract comments using Rust&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;These days I was programming a logic to extract comment lines(Like "TODO:", "FIX:" or something else) for Zed extension, similar to &lt;a href="https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree" rel="noopener noreferrer"&gt;TodoTree&lt;/a&gt;.&lt;br&gt;
However I realized it would be more difficult than I thought, and a &lt;a href="https://github.com/thedadams/zed-comment" rel="noopener noreferrer"&gt;similar extension&lt;/a&gt; already existed. Another reason was that &lt;code&gt;zed_extension_api&lt;/code&gt; couldn't interact Zed's UI from extension. So I froze that project indefinitely, and I thought, "Why not distribute the byproduct logic via Homebrew?"&lt;br&gt;
There is already a &lt;a href="https://github.com/tsoding/snitch" rel="noopener noreferrer"&gt;leading project&lt;/a&gt; doing this, but I thought "My logic might run faster because it's made with Rust" and "the leading project stopped updating 4 years ago." You know what, I just decided to do it anyway.&lt;/p&gt;
&lt;h1&gt;
  
  
  How does the tool work?
&lt;/h1&gt;

&lt;p&gt;Actually it's simple logic.&lt;br&gt;
The &lt;code&gt;args&lt;/code&gt; value receives arguments for command, at the same time, the tool will get current directory the tool is running by &lt;code&gt;env&lt;/code&gt; library, and declares to scan from current directory if &lt;code&gt;args&lt;/code&gt; is empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cdu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;current_dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string_lossy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;root_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&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;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cdu&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🔍 Scanning for COMMENT items in: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root_path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The tool determine if it's directory or not, by &lt;code&gt;is_dir()&lt;/code&gt; method in &lt;code&gt;Path&lt;/code&gt; Object, and scan files recursively.&lt;br&gt;
&lt;code&gt;path.extension()&lt;/code&gt; checks the file extension.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.is_dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;scan_directory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.extension&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.and_then&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_str&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Check if it's a supported file type&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"rs"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"js"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"ts"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"jsx"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"tsx"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"py"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"java"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"go"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"c"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"cpp"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"h"&lt;/span&gt;
                &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"hpp"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"php"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"rb"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"swift"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"kt"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"scala"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"html"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"css"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"vue"&lt;/span&gt;
                &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"md"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"txt"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"yaml"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"yml"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"json"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"toml"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"xml"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"sh"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"bash"&lt;/span&gt;
                &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"zsh"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;scan_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;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;Whan the tool finds a file with a matching extension, it reads the file contents using the &lt;code&gt;scan_file&lt;/code&gt; function.&lt;br&gt;
Then it extracts lines start with &lt;code&gt;TODO:&lt;/code&gt;, &lt;code&gt;FIX:&lt;/code&gt;, &lt;code&gt;NOTE:,&lt;/code&gt; etc. from comment lines beginning with &lt;code&gt;//&lt;/code&gt;, &lt;code&gt;#&lt;/code&gt;, or other comment markers, and adds them to a struct.&lt;br&gt;
After scanning all directories, the tool declares a &lt;code&gt;BTreeMap&lt;/code&gt; object to sort items by comment type, and pushes items into it one by one.&lt;br&gt;
Finally, it iterates through a predefined order array using a &lt;code&gt;for&lt;/code&gt; loop, and prints the content of each item grouped by comment type using &lt;code&gt;println!&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;comment_type&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;type_order&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;by_type&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;comment_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🏷️  {} Items:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;comment_type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="nf"&gt;.repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comment_type&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&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="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"    📁 {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file_todos&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"        📌 Line {}: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="py"&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="n"&gt;todo&lt;/span&gt;&lt;span class="py"&gt;.text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="nd"&gt;println!&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Distribute via Homebrew
&lt;/h1&gt;

&lt;p&gt;To distribute the tool via Homebrew, I created a new repository named "homebrew-tap", and put a &lt;code&gt;Formula&lt;/code&gt; file written by &lt;code&gt;Ruby&lt;/code&gt; into the repo.&lt;br&gt;


&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/I-Dieod" rel="noopener noreferrer"&gt;
        I-Dieod
      &lt;/a&gt; / &lt;a href="https://github.com/I-Dieod/homebrew-tap" rel="noopener noreferrer"&gt;
        homebrew-tap
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;homebrew-tap&lt;/h1&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/I-Dieod/homebrew-tap" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;



&lt;p&gt;&lt;br&gt;&lt;br&gt;
At this time, make sure that the package name defined in &lt;code&gt;Cargo.toml&lt;/code&gt;, the &lt;code&gt;Formula&lt;/code&gt; file name, and the class name inside it are all the same.&lt;br&gt;&lt;br&gt;
If they don't match, you'll get an error when running &lt;code&gt;brew tap&lt;/code&gt;, which I'll mention later.&lt;br&gt;&lt;br&gt;
I left the coding of the &lt;code&gt;Formula&lt;/code&gt; file to Claude Sonnet 4.5.&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ！This class name！&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tds&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Formula&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Fast TODO comment scanner for your codebase"&lt;/span&gt;
  &lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/I-Dieod/TodoScanner"&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/I-Dieod/TodoScanner/archive/refs/tags/v0.2.0.tar.gz"&lt;/span&gt;
  &lt;span class="n"&gt;sha256&lt;/span&gt; &lt;span class="s2"&gt;"f2afe11e469036e8af3344e546d85df70fbe6340241c156309b6b904f520befa"&lt;/span&gt;
  &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="s2"&gt;"MIT"&lt;/span&gt;

  &lt;span class="n"&gt;depends_on&lt;/span&gt; &lt;span class="s2"&gt;"rust"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:build&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;install&lt;/span&gt;
    &lt;span class="nb"&gt;system&lt;/span&gt; &lt;span class="s2"&gt;"cargo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"install"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;std_cargo_args&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Create a test file with a TODO comment&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testpath&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="s2"&gt;"test.rs"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;EOS&lt;/span&gt;&lt;span class="sh"&gt;
      // TODO: Test comment
      fn main() {}
&lt;/span&gt;&lt;span class="no"&gt;    EOS&lt;/span&gt;

    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shell_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tds &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;testpath&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;assert_match&lt;/span&gt; &lt;span class="s2"&gt;"Found 1 TODO items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the actual content, you need to prepare the &lt;code&gt;tarball&lt;/code&gt; link you can get from your repository releases and &lt;code&gt;SHA256&lt;/code&gt; hash you can get from that &lt;code&gt;tarball&lt;/code&gt; link.&lt;br&gt;
You need to write them in &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;sha256&lt;/code&gt; fields.&lt;br&gt;
If you're using &lt;code&gt;Nushell&lt;/code&gt; or &lt;code&gt;Bash&lt;/code&gt; shells, you can get the hash by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;tarball_url] | shasum &lt;span class="nt"&gt;-a&lt;/span&gt; 256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Treatment of hashes, and URLs
&lt;/h4&gt;

&lt;p&gt;The hash is validation information, not a secret.&lt;br&gt;
In fact, by publishing it, users can verify that the downloaded file hasn't been tampered with.&lt;br&gt;
This is a security-enhancing mechanism.&lt;/p&gt;
&lt;h4&gt;
  
  
  The thing you should never publish
&lt;/h4&gt;

&lt;p&gt;On the other hand, you must never include the following things in your &lt;code&gt;Fomula&lt;/code&gt;.&lt;br&gt;
❌ API keys, tokens&lt;br&gt;
❌ Password&lt;br&gt;
❌ Access tokens of private repository&lt;br&gt;
❌ Secret keys&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After you push the &lt;code&gt;Formula&lt;/code&gt; file you created to the remote repository, run these commands in your CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap &lt;span class="o"&gt;[&lt;/span&gt;your_user_name]/tap
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;your_user_name]/[package_name_you_want]
&lt;span class="c"&gt;# or&lt;/span&gt;
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;your_user_name]/tap/[package_name_you_want]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can use the tool you made in your CLI via Homebrew.&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.amazonaws.com%2Fuploads%2Farticles%2Ff35f4eqeb6uiixsxebc2.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.amazonaws.com%2Fuploads%2Farticles%2Ff35f4eqeb6uiixsxebc2.png" alt="The output when my tool working"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  How to update brew packages
&lt;/h4&gt;

&lt;p&gt;Basically, you only have to replace &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;sha256&lt;/code&gt; in &lt;code&gt;Formula&lt;/code&gt;, to newest version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;I really wanted to make Zed extension, but I feel frustrated that it already existed. My idea was beaten.&lt;br&gt;
I just did it anyway, thinking I should start creating tools and contributing OSS to OSS, taking this as an opportunity.&lt;br&gt;
I'm grad if this post helps someone who wants to distribute something via Homebrew.&lt;br&gt;
I'm always open to complaints about my tool, PRs on GitHub, or corrections if I said something wrong. Also, I'm Japanese, and I'm writing this post in English as much as I can by myself. If you can't understand some parts, feel free to ask questions in the comments.&lt;br&gt;
Thank you for reading!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>rust</category>
      <category>homebrew</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
