<?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: Mayne</title>
    <description>The latest articles on DEV Community by Mayne (@mayne-x).</description>
    <link>https://dev.to/mayne-x</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%2F3990135%2F1f40403a-91cd-4fd8-a473-ba41cdcf323c.png</url>
      <title>DEV Community: Mayne</title>
      <link>https://dev.to/mayne-x</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mayne-x"/>
    <language>en</language>
    <item>
      <title>I reclaimed 12GB from my SSD with one terminal command (and you can too)</title>
      <dc:creator>Mayne</dc:creator>
      <pubDate>Thu, 18 Jun 2026 11:21:50 +0000</pubDate>
      <link>https://dev.to/mayne-x/i-reclaimed-12gb-from-my-ssd-with-one-terminal-command-and-you-can-too-3g9k</link>
      <guid>https://dev.to/mayne-x/i-reclaimed-12gb-from-my-ssd-with-one-terminal-command-and-you-can-too-3g9k</guid>
      <description>&lt;p&gt;I've been a developer for over a decade, and if there's one thing I've learned, it's that our hard drives are basically landfills for build artifacts.&lt;/p&gt;

&lt;p&gt;A few weeks ago, I was down to &lt;strong&gt;2GB free&lt;/strong&gt; on my main SSD. Not because I had tons of important files — but because I had years worth of &lt;code&gt;node_modules&lt;/code&gt; folders, &lt;code&gt;.next&lt;/code&gt; build caches, &lt;code&gt;dist&lt;/code&gt; directories, and other junk scattered across dozens of old projects.&lt;/p&gt;

&lt;p&gt;I tried reclaiming space manually. You know the drill — &lt;code&gt;du -sh&lt;/code&gt; to find the culprits, then &lt;code&gt;rm -rf&lt;/code&gt; them one by one. It works, but it's tedious. I tried &lt;code&gt;npkill&lt;/code&gt; — it only handles &lt;code&gt;node_modules&lt;/code&gt;. I tried &lt;code&gt;wipe-modules&lt;/code&gt; — same limitation. I wanted something that could find everything at once and let me pick what to delete in one go.&lt;/p&gt;

&lt;p&gt;So I built ZapDir.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ZapDir?
&lt;/h2&gt;

&lt;p&gt;ZapDir is a terminal cleanup tool that scans your projects for heavy build artifacts and lets you delete them through a beautiful interactive interface. It's completely free, open-source under MIT, and runs on Windows, macOS, and Linux with Node.js 18+.&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; &lt;span class="nt"&gt;-g&lt;/span&gt; zapdir
zapdir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run that, and you'll see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚡  ZapDir  —  Terminal Cleanup Tool

  🗑  Junk Found — Total recoverable:  1.47 GB

  ██████████  node_modules    /node_modules      245.23 MB
  ██████░░░░  .next           /.next               1.23 GB
  ██░░░░░░░░  dist            /dist                89.45 MB

  ✔  Delete 3 item(s) freeing 1.47 GB?  ·  Yes

  ✔  Freed 1.47 GB of disk space!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The colored size bars make it obvious what's eating the most space at a glance — red for anything over 500MB, yellow for over 100MB, and green for the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it different from npkill or wipe-modules?
&lt;/h2&gt;

&lt;p&gt;There are other cleanup tools out there, but most of them only handle one pattern. Here's how ZapDir compares:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ZapDir&lt;/th&gt;
&lt;th&gt;npkill&lt;/th&gt;
&lt;th&gt;wipe-modules&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Patterns detected&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;13+&lt;/td&gt;
&lt;td&gt;1 (node_modules)&lt;/td&gt;
&lt;td&gt;1 (node_modules)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interactive TUI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Color-coded sizes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dry-run preview&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Async scanner&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ZapDir detects: &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.next&lt;/code&gt;, &lt;code&gt;.nuxt&lt;/code&gt;, &lt;code&gt;.turbo&lt;/code&gt;, &lt;code&gt;dist&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;.cache&lt;/code&gt;, &lt;code&gt;coverage&lt;/code&gt;, &lt;code&gt;out&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt;, &lt;code&gt;.parcel-cache&lt;/code&gt;, &lt;code&gt;__pycache__&lt;/code&gt;, and &lt;code&gt;.DS_Store&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it found on my machine
&lt;/h2&gt;

&lt;p&gt;I ran ZapDir across my development machine and here's what turned up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;What was there&lt;/th&gt;
&lt;th&gt;Space recovered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;~/Projects/legacy-app&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;node_modules&lt;/code&gt; + &lt;code&gt;.next&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1.8 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Projects/side-project&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;node_modules&lt;/code&gt; + &lt;code&gt;build&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;640 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Projects/old-tutorial&lt;/td&gt;
&lt;td&gt;&lt;code&gt;node_modules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;420 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/.cache&lt;/td&gt;
&lt;td&gt;Various caches&lt;/td&gt;
&lt;td&gt;340 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Various Rust projects&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;target/&lt;/code&gt; directories&lt;/td&gt;
&lt;td&gt;2.1 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old Next.js projects&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.next&lt;/code&gt; caches&lt;/td&gt;
&lt;td&gt;3.4 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abandoned monorepos&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;node_modules&lt;/code&gt; × 5&lt;/td&gt;
&lt;td&gt;2.2 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python projects&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;__pycache__&lt;/code&gt; + &lt;code&gt;.cache&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;180 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Total reclaimed: roughly 12 GB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's 12 GB I got back without touching a single file I actually needed. The biggest wins were old Next.js projects with massive &lt;code&gt;.next&lt;/code&gt; caches (some over 1 GB each) and Rust projects where &lt;code&gt;target/&lt;/code&gt; directories had accumulated to over 2 GB combined.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it safe?
&lt;/h2&gt;

&lt;p&gt;Short answer: yes, if you use &lt;code&gt;--dry-run&lt;/code&gt; first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zapdir &lt;span class="nt"&gt;--dry-run&lt;/span&gt; ~/Projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This scans and shows you exactly what would be deleted without actually removing anything. The output shows every item with its size, path, and a visual bar so you know exactly what you're getting into.&lt;/p&gt;

&lt;p&gt;When you're satisfied with the preview, run it without the flag. You'll be prompted to confirm before anything is deleted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔  Delete 3 item(s) freeing 1.47 GB?  ·  Yes / No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scanner uses &lt;code&gt;fs.promises.readdir&lt;/code&gt; with async parallel traversal for speed, and deletion is done with &lt;code&gt;Promise.allSettled&lt;/code&gt; so a permission error on one file won't crash the entire operation — it just skips that file and continues. I've been using it for weeks and haven't lost a single file I needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the scanning works
&lt;/h2&gt;

&lt;p&gt;The core logic is surprisingly straightforward. It walks directories recursively using Node.js's built-in &lt;code&gt;fs.promises&lt;/code&gt;, matches directory names against a list of known artifact patterns, calculates sizes using a streaming iterator (to avoid memory issues on huge folders), and presents everything in an interactive selection menu powered by &lt;code&gt;@clack/prompts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hidden directories like &lt;code&gt;.git&lt;/code&gt; are skipped by default for speed. The entire scan of my home directory — thousands of folders — completes in under 10 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical tip
&lt;/h2&gt;

&lt;p&gt;Here's what I do now: once a month, I run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zapdir &lt;span class="nt"&gt;--dry-run&lt;/span&gt; ~/Projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives me a quick health check of how much junk has accumulated. When the number crosses 1 GB, I run it for real and reclaim the space. It's become part of my regular maintenance routine, right alongside &lt;code&gt;brew update&lt;/code&gt; and &lt;code&gt;npm outdated&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give it a shot
&lt;/h2&gt;

&lt;p&gt;If you're running low on disk space and have old projects lying around, try it:&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; &lt;span class="nt"&gt;-g&lt;/span&gt; zapdir
zapdir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes five seconds to install and could free up gigabytes. The code is open source under MIT, so you can inspect exactly what it does before running it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Mayne-X/Zapdir" rel="noopener noreferrer"&gt;⭐ Star the repo on GitHub&lt;/a&gt;&lt;/strong&gt; if you find it useful — it helps other developers discover it too.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;ZapDir is MIT licensed and open source. Contributions, feature requests, and bug reports are welcome on GitHub.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cli</category>
      <category>productivity</category>
      <category>node</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Blazing-fast terminal cleanup tool for heavy build artifacts</title>
      <dc:creator>Mayne</dc:creator>
      <pubDate>Thu, 18 Jun 2026 08:45:25 +0000</pubDate>
      <link>https://dev.to/mayne-x/blazing-fast-terminal-cleanup-tool-for-heavy-build-artifacts-2doo</link>
      <guid>https://dev.to/mayne-x/blazing-fast-terminal-cleanup-tool-for-heavy-build-artifacts-2doo</guid>
      <description>&lt;p&gt;I've been a developer for over a decade, and if there's one thing I've learned, it's that our hard drives are basically landfills for build artifacts.&lt;/p&gt;

&lt;p&gt;A few weeks ago, I was down to &lt;strong&gt;2GB free&lt;/strong&gt; on my main SSD. Not because I had tons of important files — but because I had years worth of &lt;code&gt;node_modules&lt;/code&gt; folders, &lt;code&gt;.next&lt;/code&gt; build caches, &lt;code&gt;dist&lt;/code&gt; directories, and other junk scattered across dozens of old projects.&lt;/p&gt;

&lt;p&gt;I tried reclaiming space manually. You know the drill — &lt;code&gt;du -sh&lt;/code&gt; to find the culprits, then &lt;code&gt;rm -rf&lt;/code&gt; them one by one. It works, but it's tedious. I tried &lt;code&gt;npkill&lt;/code&gt; — it only handles &lt;code&gt;node_modules&lt;/code&gt;. I tried &lt;code&gt;wipe-modules&lt;/code&gt; — same limitation. I wanted something that could find everything at once and let me pick what to delete in one go.&lt;/p&gt;

&lt;p&gt;So I built ZapDir.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ZapDir?
&lt;/h2&gt;

&lt;p&gt;ZapDir is a terminal cleanup tool that scans your projects for heavy build artifacts and lets you delete them through a beautiful interactive interface. It's completely free, open-source under MIT, and runs on Windows, macOS, and Linux with Node.js 18+.&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; &lt;span class="nt"&gt;-g&lt;/span&gt; zapdir
zapdir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run that, and you'll see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚡  ZapDir  —  Terminal Cleanup Tool

  🗑  Junk Found — Total recoverable:  1.47 GB

  ██████████  node_modules    /node_modules      245.23 MB
  ██████░░░░  .next           /.next               1.23 GB
  ██░░░░░░░░  dist            /dist                89.45 MB

  ✔  Delete 3 item(s) freeing 1.47 GB?  ·  Yes

  ✔  Freed 1.47 GB of disk space!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The colored size bars make it obvious what's eating the most space at a glance — red for anything over 500MB, yellow for over 100MB, and green for the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it different from npkill or wipe-modules?
&lt;/h2&gt;

&lt;p&gt;There are other cleanup tools out there, but most of them only handle one pattern. Here's how ZapDir compares:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ZapDir&lt;/th&gt;
&lt;th&gt;npkill&lt;/th&gt;
&lt;th&gt;wipe-modules&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Patterns detected&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;13+&lt;/td&gt;
&lt;td&gt;1 (node_modules)&lt;/td&gt;
&lt;td&gt;1 (node_modules)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interactive TUI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Color-coded sizes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dry-run preview&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Async scanner&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ZapDir detects: &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.next&lt;/code&gt;, &lt;code&gt;.nuxt&lt;/code&gt;, &lt;code&gt;.turbo&lt;/code&gt;, &lt;code&gt;dist&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;.cache&lt;/code&gt;, &lt;code&gt;coverage&lt;/code&gt;, &lt;code&gt;out&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt;, &lt;code&gt;.parcel-cache&lt;/code&gt;, &lt;code&gt;__pycache__&lt;/code&gt;, and &lt;code&gt;.DS_Store&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it found on my machine
&lt;/h2&gt;

&lt;p&gt;I ran ZapDir across my development machine and here's what turned up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;What was there&lt;/th&gt;
&lt;th&gt;Space recovered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;~/Projects/legacy-app&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;node_modules&lt;/code&gt; + &lt;code&gt;.next&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1.8 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Projects/side-project&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;node_modules&lt;/code&gt; + &lt;code&gt;build&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;640 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Projects/old-tutorial&lt;/td&gt;
&lt;td&gt;&lt;code&gt;node_modules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;420 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/.cache&lt;/td&gt;
&lt;td&gt;Various caches&lt;/td&gt;
&lt;td&gt;340 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Various Rust projects&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;target/&lt;/code&gt; directories&lt;/td&gt;
&lt;td&gt;2.1 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old Next.js projects&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.next&lt;/code&gt; caches&lt;/td&gt;
&lt;td&gt;3.4 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abandoned monorepos&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;node_modules&lt;/code&gt; × 5&lt;/td&gt;
&lt;td&gt;2.2 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python projects&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;__pycache__&lt;/code&gt; + &lt;code&gt;.cache&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;180 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Total reclaimed: roughly 12 GB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's 12 GB I got back without touching a single file I actually needed. The biggest wins were old Next.js projects with massive &lt;code&gt;.next&lt;/code&gt; caches (some over 1 GB each) and Rust projects where &lt;code&gt;target/&lt;/code&gt; directories had accumulated to over 2 GB combined.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it safe?
&lt;/h2&gt;

&lt;p&gt;Short answer: yes, if you use &lt;code&gt;--dry-run&lt;/code&gt; first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zapdir &lt;span class="nt"&gt;--dry-run&lt;/span&gt; ~/Projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This scans and shows you exactly what would be deleted without actually removing anything. The output shows every item with its size, path, and a visual bar so you know exactly what you're getting into.&lt;/p&gt;

&lt;p&gt;When you're satisfied with the preview, run it without the flag. You'll be prompted to confirm before anything is deleted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔  Delete 3 item(s) freeing 1.47 GB?  ·  Yes / No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scanner uses &lt;code&gt;fs.promises.readdir&lt;/code&gt; with async parallel traversal for speed, and deletion is done with &lt;code&gt;Promise.allSettled&lt;/code&gt; so a permission error on one file won't crash the entire operation — it just skips that file and continues. I've been using it for weeks and haven't lost a single file I needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the scanning works
&lt;/h2&gt;

&lt;p&gt;The core logic is surprisingly straightforward. It walks directories recursively using Node.js's built-in &lt;code&gt;fs.promises&lt;/code&gt;, matches directory names against a list of known artifact patterns, calculates sizes using a streaming iterator (to avoid memory issues on huge folders), and presents everything in an interactive selection menu powered by &lt;code&gt;@clack/prompts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hidden directories like &lt;code&gt;.git&lt;/code&gt; are skipped by default for speed. The entire scan of my home directory — thousands of folders — completes in under 10 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical tip
&lt;/h2&gt;

&lt;p&gt;Here's what I do now: once a month, I run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zapdir &lt;span class="nt"&gt;--dry-run&lt;/span&gt; ~/Projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives me a quick health check of how much junk has accumulated. When the number crosses 1 GB, I run it for real and reclaim the space. It's become part of my regular maintenance routine, right alongside &lt;code&gt;brew update&lt;/code&gt; and &lt;code&gt;npm outdated&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give it a shot
&lt;/h2&gt;

&lt;p&gt;If you're running low on disk space and have old projects lying around, try it:&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; &lt;span class="nt"&gt;-g&lt;/span&gt; zapdir
zapdir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes five seconds to install and could free up gigabytes. The code is open source under MIT, so you can inspect exactly what it does before running it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Mayne-X/Zapdir" rel="noopener noreferrer"&gt;⭐ Star the repo on GitHub&lt;/a&gt;&lt;/strong&gt; if you find it useful — it helps other developers discover it too.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;ZapDir is MIT licensed and open source. Contributions, feature requests, and bug reports are welcome on GitHub.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>node</category>
      <category>tooling</category>
    </item>
    <item>
      <title>PyChase: AST-Powered Duplicate Code Detection for Python</title>
      <dc:creator>Mayne</dc:creator>
      <pubDate>Thu, 18 Jun 2026 04:49:03 +0000</pubDate>
      <link>https://dev.to/mayne-x/pychase-ast-powered-duplicate-code-detection-for-python-3408</link>
      <guid>https://dev.to/mayne-x/pychase-ast-powered-duplicate-code-detection-for-python-3408</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: Hidden Code Duplication
&lt;/h2&gt;

&lt;p&gt;Every codebase accumulates copy-paste clones. They start innocently — "I'll just duplicate this function and tweak it" — but over months they compound into a maintenance tax. When a bug is fixed in one copy, the other 5 copies stay broken. When a feature needs to change, you hunt down every variant by hand.&lt;/p&gt;

&lt;p&gt;Traditional duplicate detectors fall into two camps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Line-based tools&lt;/strong&gt; (Duplo, Simian) — compare raw text, miss everything after variable renaming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token-based tools&lt;/strong&gt; (jscpd, PMD CPD) — slightly better, but still fail when identifiers change&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generic AST tools&lt;/strong&gt; (SonarQube) — heavy infrastructure, Python support is an afterthought&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PyChase takes a different approach: &lt;strong&gt;normalized AST fingerprints with MinHash + LSH&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What PyChase Catches
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Clone Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;PyChase&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Type-1&lt;/td&gt;
&lt;td&gt;Exact copy-paste&lt;/td&gt;
&lt;td&gt;Same code, same names&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;1.000 score&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type-2&lt;/td&gt;
&lt;td&gt;Renamed identifiers&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;calculate_total&lt;/code&gt; → &lt;code&gt;compute_sum&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;0.786 score&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type-3&lt;/td&gt;
&lt;td&gt;Modified logic&lt;/td&gt;
&lt;td&gt;Added/removed statements, reordered&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;0.620 score&lt;/strong&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;
  
  
  1. Parse to AST
&lt;/h3&gt;

&lt;p&gt;Each &lt;code&gt;.py&lt;/code&gt; file is parsed into a Python Abstract Syntax Tree using the standard &lt;code&gt;ast&lt;/code&gt; module.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Normalize
&lt;/h3&gt;

&lt;p&gt;Variable names, function names, attribute names, string literals, numbers, and docstrings are replaced with generic placeholders. This strips &lt;strong&gt;everything except structure&lt;/strong&gt;:&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="c1"&gt;# These two produce the SAME normalized AST:
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;subtotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subtotal&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;
    &lt;span class="n"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subtotal&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;subtotal&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tax&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;
    &lt;span class="n"&gt;fee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fee&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Shingle
&lt;/h3&gt;

&lt;p&gt;The normalized AST node sequence is broken into overlapping &lt;strong&gt;k-shingles&lt;/strong&gt; (default k=3):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(FunctionDef, arguments, arg) → (arguments, arg, Add) → ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Fingerprint + Match
&lt;/h3&gt;

&lt;p&gt;Each shingle set becomes a &lt;strong&gt;structural fingerprint&lt;/strong&gt;. &lt;strong&gt;MinHash signatures&lt;/strong&gt; compress these into compact 256-bit signatures. &lt;strong&gt;Locality Sensitive Hashing (LSH)&lt;/strong&gt; indexes them — only units sharing a bucket are compared.&lt;/p&gt;

&lt;p&gt;This reduces candidate pairs from &lt;strong&gt;O(n²) to nearly O(n)&lt;/strong&gt;. For 10,000 functions, that's 50 million comparisons avoided.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Cluster + Report
&lt;/h3&gt;

&lt;p&gt;Connected-component clustering groups related matches. Results render as text, JSON, CSV, or an &lt;strong&gt;interactive HTML report&lt;/strong&gt; with collapsible groups and syntax-highlighted code previews.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&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;pychase

&lt;span class="c"&gt;# Scan your project&lt;/span&gt;
pychase &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Generate HTML report&lt;/span&gt;
pychase &lt;span class="nt"&gt;--format&lt;/span&gt; html &lt;span class="nt"&gt;--output&lt;/span&gt; duplicates.html &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# JSON output for CI&lt;/span&gt;
pychase &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="nt"&gt;--threshold&lt;/span&gt; 0.85 ./src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why MinHash + LSH Matters
&lt;/h2&gt;

&lt;p&gt;Tools like dry4python compare every function against every other function — O(n²). With 10,000 functions, that's 50 million comparisons. PyChase's MinHash + LSH approach reduces this to near-linear time, making it viable for &lt;strong&gt;large monorepos and CI pipelines&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison 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;Tool&lt;/th&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Clone Types&lt;/th&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PyChase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MinHash+LSH (O(n))&lt;/td&gt;
&lt;td&gt;Type-1,2,3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text, JSON, CSV, HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dry4python&lt;/td&gt;
&lt;td&gt;Brute-force (O(n²))&lt;/td&gt;
&lt;td&gt;Type-2 only&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text, JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jscpd&lt;/td&gt;
&lt;td&gt;Token hash&lt;/td&gt;
&lt;td&gt;Type-1,2&lt;/td&gt;
&lt;td&gt;Node.js&lt;/td&gt;
&lt;td&gt;Text, JSON, HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SonarQube&lt;/td&gt;
&lt;td&gt;Custom AST&lt;/td&gt;
&lt;td&gt;Type-1,2&lt;/td&gt;
&lt;td&gt;DB + server + scanner&lt;/td&gt;
&lt;td&gt;Web dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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;pip &lt;span class="nb"&gt;install &lt;/span&gt;pychase
pychase &lt;span class="nt"&gt;--threshold&lt;/span&gt; 0.55 &lt;span class="nt"&gt;--min-lines&lt;/span&gt; 2 &lt;span class="nt"&gt;--min-nodes&lt;/span&gt; 10 ./src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Mayne-X/PyChase" rel="noopener noreferrer"&gt;https://github.com/Mayne-X/PyChase&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://github.com/Mayne-X/PyChase/wiki" rel="noopener noreferrer"&gt;https://github.com/Mayne-X/PyChase/wiki&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;PyPI&lt;/strong&gt;: [&lt;a href="https://pypi.org/project/pychase/" rel="noopener noreferrer"&gt;https://pypi.org/project/pychase/&lt;/a&gt;]&lt;/p&gt;

</description>
      <category>python</category>
      <category>refactoring</category>
      <category>opensource</category>
      <category>codequality</category>
    </item>
  </channel>
</rss>
