<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" 
  xmlns:atom="http://www.w3.org/2005/Atom" 
  xmlns:content="http://purl.org/rss/1.0/modules/content/" 
  xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Saadman Rafat's Blog</title>
    <description>Insights on Python development, serverless architecture, Docker, Kubernetes, and AI research by Saadman Rafat.</description>
    <link>https://saadman.dev/blog/</link>
    <atom:link href="https://saadman.dev/rss.xml" rel="self" type="application/rss+xml" />
    <language>en-us</language>
    <lastBuildDate>Thu, 23 Jul 2026 04:53:56 GMT</lastBuildDate>
    <generator>Saadman.dev Blog Engine</generator>
    <copyright>© 2026 Saadman Rafat</copyright>
    <ttl>60</ttl>
    <image>
      <url>https://avatars.githubusercontent.com/u/13638758?s=400&amp;u=2b3265acd3fa799a558f8b95d147053cbf4e7767&amp;v=4</url>
      <title>Saadman Rafat's Blog</title>
      <link>https://saadman.dev/blog/</link>
      <width>144</width>
      <height>144</height>
    </image>
    
  <item>
    <title><![CDATA[Halfhand: The Flight Recorder for Your AI Coding Agents]]></title>
    <link>https://saadman.dev/blog/2026-07-23-halfhand-the-flight-recorder-for-your-ai-coding-agents/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2026-07-23-halfhand-the-flight-recorder-for-your-ai-coding-agents/</guid>
    <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[Rust]]></category>
    <category><![CDATA[CLI]]></category>
    <category><![CDATA[AI Agents]]></category>
    <category><![CDATA[Claude Code]]></category>
    <category><![CDATA[Developer Tools]]></category>
    <category><![CDATA[Open Source]]></category>
    <category><![CDATA[Observability]]></category>
    <category><![CDATA[Local-first]]></category>
    <description><![CDATA[Halfhand (hh) is a local-first Rust CLI that records AI coding agent sessions — Claude Code, Codex CLI, Gemini CLI — and replays them faithfully. Zero network calls, zero cloud, 100% yours.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> Halfhand (hh) wraps any AI agent command in a PTY and records its terminal output, file diffs, and — for Claude Code, Claude Desktop, Codex CLI, and Gemini CLI — its internal tool calls, all to a local SQLite database. Replay sessions faithfully in a TUI, inspect them non-interactively, search across them, and scrub secrets before you ever share one. No servers, no uploads, zero network calls from hh itself.</p>
      <p>Your agent just finished a 40-minute run. It touched two dozen files, rewrote a config, deleted a module you didn’t expect it to touch, and reported “done.” Now you’re staring at a diff you didn’t watch happen, trying to reconstruct <em>why</em> — scrolling back through a terminal buffer that’s already scrolled off, or digging through a chat transcript that doesn’t quite line up with what actually landed on disk.</p>
<p>I hit this often enough running Claude Code, Codex CLI, and Gemini CLI back to back on the same repo that I built a tool to fix it. It’s called <strong><a href="https://halfhand.org">Halfhand</a></strong>, and it’s a local-first flight recorder for AI agents, written in Rust.</p>
<h2 id="what-halfhand-actually-does">What Halfhand Actually Does</h2><p>Halfhand ships as a single binary, <code>hh</code>. Point it at any agent command and it wraps that command in a PTY, recording terminal output and file changes as they happen — regardless of what you run. For agents it recognizes, it goes a level deeper and also captures the agent’s internal turns: prompts, tool calls, and tool results, as structured events.</p>
<pre><code class="language-bash">hh run -- claude              # record a Claude Code session (or any command)
hh replay last                # faithfully play it back in an interactive TUI
hh inspect last                # non-interactive summary + step table
hh list                        # every recording, newest first
hh delete last --yes           # remove one</code></pre><p>Everything — terminal bytes, file diffs, structured agent events — lands in a single local SQLite database plus a content-addressed blob store. There’s no server component, no account, no dashboard to log into. Recordings never leave your machine.</p>
<h2 id="recording-a-session-looks-like-this">Recording a Session Looks Like This</h2><p>Here’s <code>hh inspect</code> on a real Claude Code session — a step-by-step timeline of every file it touched, in order, with timestamps:</p>
<p><img src="/assets/images/blog/halfhand-inspect-timeline.png" alt="hh inspect output showing a step-by-step file-change timeline from a recorded Claude Code session"></p>
<p>87 steps, 23 files changed, 36 minutes, one command. That table alone answers the question “what did it actually do” faster than re-reading the whole transcript.</p>
<h2 id="replaying-it-faithfully">Replaying It Faithfully</h2><p><code>hh replay</code> drops you into a keyboard-driven TUI: a timeline pane on the left, the detail for whatever step you’re on the right, and a keymap overlay (<code>?</code>) if you forget the bindings. <code>j</code>/<code>k</code> to move, <code>J</code> to jump to a timestamp, <code>/</code> to filter by kind or summary text, <code>t</code> to toggle terminal-output segments, <code>d</code> to jump straight to the next file diff.</p>
<p><img src="/assets/images/blog/halfhand-replay-tui.png" alt="Halfhand&#39;s interactive replay TUI showing the timeline, file diff detail pane, and keymap overlay"></p>
<p>Worth being precise about what this is: <code>hh replay</code> is a <strong>faithful transcript</strong>, not deterministic re-execution. It renders the agent’s text, tool calls, and file diffs in the exact order they happened, but the agent is never re-invoked, no API calls are made, and no side effects are reproduced. A session that got cut short (<code>hh</code> killed mid-run) replays the partial timeline and is honestly marked <code>interrupted</code> rather than pretending otherwise.</p>
<h2 id="zooming-into-a-single-step">Zooming Into a Single Step</h2><p>For scripting or quick audits, <code>hh inspect &lt;id&gt; --step N --diff</code> skips the TUI entirely and prints the exact unified diff for that step:</p>
<p><img src="/assets/images/blog/halfhand-inspect-diff.png" alt="hh inspect --step --diff output showing the exact unified diff for a single recorded step"></p>
<p>Add <code>--json</code> and the same data comes back structured, so you can pipe a session’s file changes into whatever review or CI tooling you already have. <code>--failed</code> filters down to steps where something went wrong — useful when an agent run silently regressed something and you don’t want to page through 80 steps to find where.</p>
<h2 id="installing-it">Installing It</h2><p>Pick one, both install the <code>hh</code> binary:</p>
<pre><code class="language-bash"># cargo, any OS with a Rust toolchain
cargo install halfhand

# shell installer (macOS &amp; Linux), prebuilt binary + SHA-256 checksum
curl --proto &#039;=https&#039; --tlsv1.2 -LsSf https://github.com/halfhandorg/halfhand/releases/latest/download/halfhand-installer.sh | sh</code></pre><p><code>hh --help</code> gives you the full command surface — every subcommand also takes <code>--help</code> with its own usage example:</p>
<p><img src="/assets/images/blog/halfhand-cli-help.png" alt="Halfhand hh --help output listing all subcommands: run, replay, inspect, list, delete, mcp-proxy, doctor, gc, stats, scan, redact, export, import, search, completions"></p>
<p>A few worth calling out beyond the obvious ones:</p>
<table>
<thead>
<tr>
<th>Command</th>
<th>What it does</th>
</tr>
</thead>
<tbody><tr>
<td><code>hh search &lt;query&gt;</code></td>
<td>Full-text search over recorded events (FTS5), filterable by <code>--agent</code>, <code>--kind</code></td>
</tr>
<tr>
<td><code>hh scan &lt;id|last|--all&gt;</code></td>
<td>Reports secrets found in a recording — never the secret itself</td>
</tr>
<tr>
<td><code>hh redact &lt;id|last&gt;</code></td>
<td>Irreversibly strips detected secrets from a recording, in place</td>
</tr>
<tr>
<td><code>hh export &lt;id|last&gt;</code></td>
<td>JSON bundle, portable <code>--bundle</code>, or a self-contained <code>--html</code> page — redacted by default</td>
</tr>
<tr>
<td><code>hh mcp-proxy -- &lt;server&gt;</code></td>
<td>Wraps an MCP server in a recording stdio proxy, so you can see the MCP traffic too</td>
</tr>
<tr>
<td><code>hh doctor</code> / <code>hh gc</code> / <code>hh stats</code></td>
<td>Health check, disk reclaim, and store summary (sessions, disk usage, largest recordings)</td>
</tr>
</tbody></table>
<h2 id="adapters-more-than-just-a-terminal-recording">Adapters: More Than Just a Terminal Recording</h2><p>Every <code>hh run</code> captures terminal output and file changes no matter what you point it at. On top of that, Halfhand auto-detects a handful of agents and records their internal turns as structured events instead of just raw bytes: <strong>Claude Code</strong>, <strong>Claude Desktop</strong>, <strong>OpenAI Codex CLI</strong>, and <strong>Google Gemini CLI</strong> — force a specific one with <code>--adapter</code> if detection guesses wrong. If it can’t find a transcript for whatever you’re running, it degrades gracefully: you still get the complete terminal and file-change recording, just without the structured breakdown.</p>
<h2 id="secrets-are-a-first-class-concern-not-an-afterthought">Secrets Are a First-Class Concern, Not an Afterthought</h2><p>Recorded sessions can contain secrets present in prompts and tool outputs — that’s just the reality of recording an agent that has your API keys in its environment. Halfhand treats this as a first-class problem instead of a footnote:</p>
<ul>
<li><code>hh scan</code> reports what it found, without ever printing the secret itself</li>
<li><code>hh redact</code> removes detected secrets from a recording in place, irreversibly</li>
<li><code>hh export</code> is redacted by default, whether you’re producing a JSON bundle, a portable <code>.hh</code> archive, or a self-contained HTML replay page</li>
</ul>
<p>You can also turn on redaction at record time, before anything touches disk:</p>
<pre><code class="language-toml">[redaction]
at_record = true   # default: false — scrub secrets before they hit disk
entropy = true     # default: true — conservative high-entropy detector
rules = [
  { name = &quot;anthropic-api-key&quot;, pattern = &quot;sk-ant-[a-zA-Z0-9\\-_]{32,}&quot; },
  { name = &quot;openai-api-key&quot;,    pattern = &quot;sk-(?:proj|svcacct|[A-Za-z0-9]{2})-[A-Za-z0-9]{32,}&quot; },
  { name = &quot;stripe-secret-key&quot;, pattern = &quot;sk_(?:live|test)_[a-zA-Z0-9]{24,}&quot; },
]</code></pre><p>Built-in detectors already cover AWS, GitHub, GitLab, Slack, PEM keys, and JWTs — the <code>rules</code> block just fills gaps for anything Halfhand doesn’t know about yet. A malformed rule is a hard error at startup, on purpose: silently dropping a detector would be a redaction hole, not a convenience.</p>
<h2 id="why-local-first">Why Local-First</h2><p>This is the part I care about most, and it’s not a marketing line — it’s enforced:</p>
<ul>
<li><strong>Zero network calls.</strong> The <code>hh</code> binary links no HTTP client. A CI check fails the build if that ever changes.</li>
<li><strong>You own the data.</strong> One SQLite file plus a content-addressed blob store, in a data directory you control (<code>HH_DATA_DIR</code>). <code>hh delete</code> and it’s actually gone.</li>
<li><strong>Stable interface.</strong> The <code>--json</code> schema is frozen going into 1.0, CLI flags are additive-only, and database migrations only ever run forward.</li>
</ul>
<p>No account, no telemetry, no “anonymous usage stats.” If you’re recording agent sessions that touch client code, production configs, or anything under an NDA, that constraint isn’t a nice-to-have.</p>
<h2 id="what-developers-are-saying">What Developers Are Saying</h2><blockquote>
<p>“This is super cool! It’s like a VCR for LLMs.”<br>— <a href="https://kennethreitz.org">Kenneth Reitz</a>, creator of <a href="https://requests.readthedocs.io/en/latest/">Requests</a>, <a href="https://github.com/certifi/python-certifi">Certifi</a>, <a href="https://pipenv.pypa.io/en/latest/">Pipenv</a> &amp; <a href="https://github.com/kennethreitz">more</a></p>
</blockquote>
<h2 id="try-it">Try It</h2><pre><code class="language-bash">cargo install halfhand
hh run -- claude
hh replay last</code></pre><p>Halfhand is Apache-2.0 licensed and open to contributions — read <code>CONTRIBUTING.md</code> before opening a PR. Source, docs, and the full command reference live at <a href="https://github.com/halfhandorg/halfhand">github.com/halfhandorg/halfhand</a>; the project site is <a href="https://halfhand.org">halfhand.org</a>.</p>
<p>I built this because I kept losing track of what my own agents were doing to my own repos. If you run Claude Code, Codex, or Gemini CLI against anything you’d want to audit later, it might save you the same headache it saved me.</p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/halfhand-cover.png" length="0" type="image/jpeg" />
  </item>
  <item>
    <title><![CDATA[The Modern Way to Install Python CLI Tools Globally Without Breaking PEP 668 Compliance]]></title>
    <link>https://saadman.dev/blog/2025-08-10-the-modern-way-to-install-python-cli-tools-globally-without-breaking-pep-668-compliance/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2025-08-10-the-modern-way-to-install-python-cli-tools-globally-without-breaking-pep-668-compliance/</guid>
    <pubDate>Sat, 09 Aug 2025 23:20:00 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[python]]></category>
    <category><![CDATA[cli]]></category>
    <category><![CDATA[pipx]]></category>
    <category><![CDATA[uv]]></category>
    <category><![CDATA[pep-668]]></category>
    <category><![CDATA[developer-tools]]></category>
    <description><![CDATA[Ubuntu 24.04 enforces PEP 668, blocking global pip installs. Learn how to use pipx, uv, and other modern methods to install Python CLI tools globally while staying compliant.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> PEP 668 blocks global pip installs. Use pipx or uv for safe, isolated global CLI tool installs.</p>
      <h1 id="the-modern-way-to-install-python-cli-tools-globally-without-breaking-pep-668-compliance">The Modern Way to Install Python CLI Tools Globally Without Breaking PEP 668 Compliance</h1><p>You’ve built a slick Python CLI tool—clean help messages, intuitive flags, everything just works. Time to install it globally:</p>
<pre><code class="language-bash">pip install .</code></pre><p>And then… this happens:</p>
<pre><code class="language-bash">error: externally-managed-environment

× This environment is externally managed
╰─ To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.</code></pre><p>I wrote a <a href="https://saadman.dev/blog/2025-05-15-the-reason-pip-refuses-to-install-globally/">blog post</a> about this error, explaining why it exists and how to work around it. But today, I want to focus on a more elegant solution that respects PEP 668 compliance while allowing you to install Python CLI tools globally.</p>
<h2 id="welcome-to-the-pep-668-era-">Welcome to the PEP 668 era.</h2><p>PEP 668 introduced “externally managed environments” to protect your OS from broken dependencies, especially on Linux distributions like Ubuntu 24.04. It’s a good safeguard—but it also means your old pip install habits for global CLI tools no longer work without hacks.</p>
<p>This post isn’t about fighting PEP 668. It’s about working with it—using modern, safe methods to install CLI tools globally without wrecking your system.</p>
<h2 id="the-problem-we-all-face">The Problem We All Face</h2><p>Here’s the thing: PEP 668 isn’t trying to ruin your day. It’s actually solving a real problem that anyone who’s maintained Linux systems has experienced:</p>
<ul>
<li><strong>Dependency hell</strong>: Your system’s Python packages conflicting with pip-installed ones</li>
<li><strong>Broken OS tools</strong>: That time <code>apt</code> stopped working because you installed the wrong version of <code>requests</code></li>
<li><strong>The Friday afternoon panic</strong>: When your deployment scripts fail because someone globally installed a package that broke system dependencies</li>
</ul>
<p>But here’s what PEP 668 <em>didn’t</em> solve: how to elegantly install the CLI tools we actually want to use globally. Tools like <code>black</code>, <code>ruff</code>, <code>httpie</code>, or that awesome script you just wrote.</p>
<h2 id="what-actually-works-in-2025">What Actually Works in 2025</h2><p>Let me save you some time. Here are the approaches that actually work, ranked by how much your future self will thank you:</p>
<h3 id="1-pipx-the-tool-you-should-be-using">1. pipx: The Tool You Should Be Using</h3><p>If you’re not using <code>pipx</code> yet, you’re missing out. It’s specifically designed for this exact problem:</p>
<pre><code class="language-bash"># Install pipx (once)
sudo apt install pipx  # Ubuntu/Debian
brew install pipx      # macOS

# Install CLI tools the right way
pipx install black
pipx install ruff
pipx install your-awesome-cli-tool</code></pre><p>What makes <code>pipx</code> brilliant:</p>
<ul>
<li>Each tool gets its own isolated virtual environment</li>
<li>Tools are globally accessible from <code>~/.local/bin</code></li>
<li>Zero dependency conflicts between tools</li>
<li>Dead simple management</li>
</ul>
<pre><code class="language-bash">pipx list                    # See what&#039;s installed
pipx upgrade black          # Update one tool
pipx upgrade-all            # Update everything
pipx uninstall black        # Clean removal</code></pre><h3 id="2-uv-the-speed-demon">2. uv: The Speed Demon</h3><p>If you haven’t tried <code>uv</code> yet, prepare to have your mind blown. It’s like <code>pipx</code> but written in Rust and ridiculously fast:</p>
<pre><code class="language-bash"># Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install tools at light speed
uv tool install ruff
uv tool install black
uv tool install your-cli-tool

# Management is equally fast
uv tool list
uv tool upgrade --all</code></pre><p>The performance difference is noticeable. What takes <code>pipx</code> 30 seconds, <code>uv</code> does in 3.</p>
<h3 id="3-pip-user-when-you-need-simple">3. pip –user: When You Need Simple</h3><p>Sometimes you just need something installed quickly without the overhead:</p>
<pre><code class="language-bash">pip install --user black</code></pre><p>This installs to <code>~/.local/lib/python3.x/site-packages</code> and puts binaries in <code>~/.local/bin</code>. It’s fast, simple, and respects PEP 668.</p>
<p><strong>The catch?</strong> No isolation. If two tools need conflicting dependencies, you’re back to dependency hell.</p>
<h3 id="4-the-dedicated-virtual-environment-approach">4. The Dedicated Virtual Environment Approach</h3><p>For maximum control, create a dedicated virtual environment for your CLI tools:</p>
<pre><code class="language-bash"># Set up once
python -m venv ~/.venv/cli-tools
source ~/.venv/cli-tools/bin/activate
pip install black ruff httpie your-awesome-tool

# Add to your shell config (~/.bashrc, ~/.zshrc)
alias black=&#039;~/.venv/cli-tools/bin/black&#039;
alias ruff=&#039;~/.venv/cli-tools/bin/ruff&#039;
alias http=&#039;~/.venv/cli-tools/bin/http&#039;</code></pre><p>This gives you complete control but requires more setup and maintenance.</p>
<h2 id="real-world-example-setting-up-a-development-environment">Real-World Example: Setting Up a Development Environment</h2><p>Here’s how I set up CLI tools on a new machine in 2025:</p>
<pre><code class="language-bash"># Install uv (my current preference for speed)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Essential development tools
uv tool install black          # Code formatting
uv tool install ruff           # Linting and more formatting
uv tool install mypy           # Type checking
uv tool install pytest         # Testing
uv tool install cookiecutter   # Project scaffolding
uv tool install httpie         # API testing
uv tool install poetry         # Dependency management

# Verify everything is working
uv tool list</code></pre><p>Total time: under 2 minutes. Everything isolated, everything working.</p>
<h2 id="the-but-i-have-a-script-problem">The “But I Have a Script” Problem</h2><p>“That’s great for published packages, but I just have a Python script I want to install globally.” </p>
<p>I hear you. Here’s the modern approach:</p>
<h3 id="option-1-make-it-a-proper-package-recommended-">Option 1: Make it a proper package (recommended)</h3><p>Create a minimal <code>pyproject.toml</code>:</p>
<pre><code class="language-toml">[build-system]
requires = [&quot;hatchling&quot;]
build-backend = &quot;hatchling.build&quot;

[project]
name = &quot;my-awesome-script&quot;
version = &quot;0.1.0&quot;
dependencies = []

[project.scripts]
my-script = &quot;my_awesome_script:main&quot;</code></pre><p>Then install with <code>pipx</code> or <code>uv tool install .</code></p>
<h3 id="option-2-use-pipx-run-for-one-offs">Option 2: Use pipx run for one-offs</h3><pre><code class="language-bash">pipx run --spec . my-script</code></pre><p>This runs your script without permanently installing it.</p>
<h3 id="option-3-direct-installation-with-pipx">Option 3: Direct installation with pipx</h3><pre><code class="language-bash">pipx install .  # From your script&#039;s directory</code></pre><h2 id="what-about-docker-and-ci-cd-">What About Docker and CI/CD?</h2><p>In containerized environments, you can often ignore PEP 668 since you control the entire environment:</p>
<pre><code class="language-dockerfile"># In a Dockerfile, this is fine
RUN pip install --break-system-packages my-cli-tool</code></pre><p>But even there, consider using <code>uv</code> for speed:</p>
<pre><code class="language-dockerfile">RUN curl -LsSf https://astral.sh/uv/install.sh | sh
RUN uv tool install my-cli-tool</code></pre><h2 id="the-migration-path">The Migration Path</h2><p>If you’re currently using <code>--break-system-packages</code> everywhere, here’s your migration strategy:</p>
<ol>
<li><strong>Audit what you have</strong>: <code>pip list --user</code> to see what’s installed</li>
<li><strong>Start fresh</strong>: Use <code>pipx</code> or <code>uv tool</code> for new installations</li>
<li><strong>Migrate gradually</strong>: Replace <code>--break-system-packages</code> installs one by one</li>
<li><strong>Update your scripts</strong>: Change deployment scripts to use modern tools</li>
</ol>
<h2 id="performance-comparison">Performance Comparison</h2><p>I tested installing 5 common CLI tools on a fresh Ubuntu 24.04 system:</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Time</th>
<th>Isolation</th>
<th>Conflicts</th>
</tr>
</thead>
<tbody><tr>
<td><code>uv tool install</code></td>
<td>12s</td>
<td>Perfect</td>
<td>None</td>
</tr>
<tr>
<td><code>pipx install</code></td>
<td>45s</td>
<td>Perfect</td>
<td>None</td>
</tr>
<tr>
<td><code>pip --user</code></td>
<td>8s</td>
<td>None</td>
<td>Possible</td>
</tr>
<tr>
<td>Virtual env + pip</td>
<td>15s</td>
<td>Perfect</td>
<td>None</td>
</tr>
</tbody></table>
<p><code>uv</code> is clearly the speed winner, but <code>pipx</code> has better ecosystem maturity.</p>
<h2 id="what-s-next-">What’s Next?</h2><p>The Python packaging ecosystem keeps evolving. Keep an eye on:</p>
<ul>
<li><strong>PEP 704</strong>: Requiring virtual environments (might make this even more important)</li>
<li><strong>uv’s rapid development</strong>: New features land frequently</li>
<li><strong>pipx improvements</strong>: Still the most stable choice</li>
</ul>
<h2 id="bottom-line">Bottom Line</h2><p>PEP 668 isn’t the enemy—it’s pushing us toward better practices. Instead of fighting it:</p>
<ol>
<li><strong>Use <code>pipx</code> or <code>uv tool</code></strong> for CLI applications</li>
<li><strong>Embrace isolation</strong> instead of global package soup</li>
<li><strong>Update your deployment scripts</strong> to use modern tools</li>
<li><strong>Never use <code>--break-system-packages</code></strong> in production</li>
</ol>
<p>Your future self (and your sysadmin) will thank you. Trust me on this one.</p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/modern-way-to-install-cli-tools-globally.png" length="0" type="image/jpeg" />
  </item>
  <item>
    <title><![CDATA[AI's New Frontier: Reimagining Your Terminal with Intelligent Agents]]></title>
    <link>https://saadman.dev/blog/2025-06-26-reimagining-your-terminal-with-intelligent-agents/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2025-06-26-reimagining-your-terminal-with-intelligent-agents/</guid>
    <pubDate>Thu, 26 Jun 2025 05:23:41 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Productivity]]></category>
    <category><![CDATA[Developer Tools]]></category>
    <category><![CDATA[Command Line]]></category>
    <category><![CDATA[Terminal]]></category>
    <category><![CDATA[Gemini CLI]]></category>
    <category><![CDATA[Arkterm]]></category>
    <category><![CDATA[Warp.dev]]></category>
    <category><![CDATA[Software Engineering]]></category>
    <category><![CDATA[Developer Experience]]></category>
    <description><![CDATA[Discover how AI-driven tools like Google Gemini CLI, Arkterm, and Warp.dev are transforming the command-line experience with natural language interaction, real-time context, and intelligent automation.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> Explore the rise of AI-powered terminals and how tools like Gemini CLI, Arkterm, and Warp.dev are making development faster, safer, and more intuitive through natural language commands and intelligent agent orchestration.</p>
      <h2 id="rethinking-the-command-line-with-ai">Rethinking the Command Line with AI</h2><p>The terminal—long hailed as the developer’s power tool—has historically required an intimidating level of expertise. It demanded precise syntax, obscure flags, and near-religious mastery of text-based commands. But that era is evolving rapidly. AI is now embedding itself into the command-line interface (CLI), transforming it from a memorization gauntlet into an intuitive, intent-driven experience.</p>
<p>This change marks a deeper transformation: instead of focusing on how to do something, developers can now focus on what they want to achieve. It’s a major leap forward in developer experience, onboarding, and productivity—lowering the barrier for beginners and supercharging experts.</p>
<p>In this article, we examine three next-gen AI tools reimagining the terminal: Google Gemini CLI, Arkterm, and Warp.dev. Each offers a unique take on the AI-powered development environment, from large-context agents to fast command assistants and full multi-agent orchestration.</p>
<h2 id="google-gemini-cli-your-ai-powered-terminal-assistant">Google Gemini CLI: Your AI-Powered Terminal Assistant</h2><p>The Gemini CLI from Google brings Gemini 1.5 Pro’s capabilities directly to the terminal. With its massive 1 million-token context window, it can reason over entire codebases, conversations, or documentation.</p>
<p>Key Features:</p>
<ul>
<li>Natural Language Interface: Ask it to generate code, fix bugs, or research APIs.</li>
<li>Search Grounding: Integrates live Google Search results into its responses.</li>
<li>Script-Friendly: Works in both interactive and non-interactive (scripted) modes.</li>
<li>Open Source: Licensed under Apache 2.0 and open to community contributions.</li>
<li>Generous Free Tier: 60 requests/minute and 1,000/day—free with a Google account.</li>
</ul>
<p><img src="https://saadman.dev/assets/images/blog/1be2c652-10eb-49dc-883e-95bdfb87d3e1.gif" alt="Google Gemini CLI in action"></p>
<p>Developers can provide custom context via Model Context Protocol (MCP) to tailor results to a project or company stack. It also shares functionality with Gemini Code Assist in VS Code, enabling multi-step plans, recovery from failures, and more.</p>
<h2 id="arkterm-fast-safe-and-linux-centric">ArkTerm: Fast, Safe, and Linux-Centric</h2><p><a href="https://saadman.dev/blog/2025-05-31-shell-shocked-wire-llm-directly-in-linux-terminal/">ArkTerm</a> is a lightweight, safety-first assistant for Linux users that translates natural language into precise CLI commands using LLAMA 3.1 via Grok’s LLM API.</p>
<p>What Sets It Apart:</p>
<ul>
<li>Sub-Second Responses: Delivers rapid results essential for terminal workflows.</li>
<li>Command Safety: Never auto-executes commands. Warns users of destructive operations like rm -rf.</li>
<li>Context-Aware: Detects your current directory/project type (e.g., Python, Rust) to fine-tune suggestions.</li>
<li>Interactive Mode: Conversational follow-ups in-session.</li>
<li>Whether you’re a sysadmin trying to compress logs or a dev managing Git repos, Arkterm reduces the time spent googling flags or parsing man pages—making Linux more approachable without sacrificing control.</li>
</ul>
<h2 id="warp-dev-terminal-as-a-multi-agent-workspace">Warp.dev: Terminal as a Multi-Agent Workspace</h2><p><a href="https://app.warp.dev/referral/EEGVZM">Warp.dev</a> pushes the boundaries further by evolving the terminal into a multi-agent development environment. Rather than a single assistant, Warp uses multiple coordinated agents that collaborate across tasks.</p>
<p>Agentic Workflow:</p>
<ol>
<li>Tell: Express your goal in natural language.</li>
<li>Agents Write: Different agents generate and modify code across repos.</li>
<li>Run in Parallel: Multiple agents execute workflows—debugging, building, deploying—all at once.</li>
</ol>
<h2 id="warp-s-enterprise-level-capabilities-">Warp’s Enterprise-Level Capabilities:</h2><ul>
<li>Warp Drive &amp; MCP: Share knowledge between agents for cohesive execution.</li>
<li>Security First: BYO LLM, Zero Data Retention, and step-level user control.</li>
<li>End-to-End Automation: From building full-stack apps to fixing bugs linked from Linear, Warp handles complete development lifecycles.</li>
</ul>
<p>This agentic model hints at a near-future where developers orchestrate AI teams like engineers lead human ones—delegating tasks while maintaining high-level oversight.</p>
<h2 id="final-thoughts-the-terminal-becomes-a-collaborator">Final Thoughts: The Terminal Becomes a Collaborator</h2><p>AI-powered terminals mark a shift in how we write, test, and deploy code. Whether you prefer the raw power of Gemini CLI, the speed and safety of Arkterm, or the orchestration of Warp.dev, one thing is clear: the terminal is no longer just a tool—it’s becoming a partner.<br>This evolution could fundamentally reshape the path into software development. Learning to code might start with learning to communicate with AI agents rather than memorizing Bash syntax. That means more access, faster learning, and ultimately—smarter software development.<br>Welcome to the AI-native command line.</p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/4bb72519-deec-43aa-bb39-f58bc68e6f55.png" length="0" type="image/jpeg" />
  </item>
  <item>
    <title><![CDATA[Ten Actually Useful Open Source LLM Tools You Should Know (2025 Edition)]]></title>
    <link>https://saadman.dev/blog/2025-06-09-ten-actually-useful-open-source-llm-tool-you-should-know-2025-edition/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2025-06-09-ten-actually-useful-open-source-llm-tool-you-should-know-2025-edition/</guid>
    <pubDate>Mon, 09 Jun 2025 04:15:47 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[Open Source]]></category>
    <category><![CDATA[LLM]]></category>
    <category><![CDATA[Developer Tools]]></category>
    <category><![CDATA[AI Engineering]]></category>
    <category><![CDATA[LangChain]]></category>
    <category><![CDATA[VSCode]]></category>
    <category><![CDATA[Prompt Engineering]]></category>
    <category><![CDATA[AI Agents]]></category>
    <description><![CDATA[Discover six powerful open-source LLM tools and startups revolutionizing AI development in 2025 — including Langfuse, Flowise, Continue and more. Stay ahead with tools that are actually useful and privacy-friendly.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> A curated list of open-source LLM tools in 2025 that are production-ready, privacy-respecting, and genuinely useful — from Langfuse to Continue.</p>
      <p>Developers building AI-powered apps need solid tools for productivity, observability, and deployment. In fact, a recent developer survey found 76% of respondents are using or planning to use AI tools in their <a href="https://survey.stackoverflow.co/2024/ai">workflow</a>. </p>
<p>Open-source LLM projects have surged in 2024–2025, making it easier to experiment and ship with confidence. This post highlights 10 fully open-source, actively maintained LLM tools and libraries that deliver real developer value. Each section explains what the tool does, why it’s useful, and even shows a quick code snippet or GitHub link to get started.</p>
<h2 id="continue-dev-custom-ai-code-assistants">Continue.dev – Custom AI Code Assistants</h2><p>Continue.dev is an open-source platform for creating custom AI assistants <a href="http://github.com/continuedev/continue">inside your IDE</a>,  It provides free VS Code and JetBrains extensions (Apache-2.0 license) so that teams can build “AI-native” coding tools. With Continue, you can configure code autocompletion, in-editor chat, code edits, or even agentic tasks tailored to your <a href="http://github.com/continuedev/continue">codebase</a>. For example, Continue stores its settings in <code>~/.continue/config.json</code>. </p>
<p>A typical config might look like:</p>
<pre><code class="language-json">{
  &quot;models&quot;: [
    {
      &quot;model&quot;: &quot;YourModelName&quot;,
      &quot;title&quot;: &quot;MyAssistant&quot;,
      &quot;apiBase&quot;: &quot;http://localhost:8080/v1/&quot;,
      &quot;provider&quot;: &quot;openai&quot;
    }
  ]
}</code></pre><p>Here you define which LLM endpoints the assistant should call. Once set up, you can press a hotkey to autocomplete code (tab-to-complete), ask questions about your code, or let an agent refactor it. The Continue GitHub has the VSCode/IDE plugin code and detailed docs. In short, Continue empowers developers to share and reuse AI-backed coding rules and prompts with familiar IDE tooling.</p>
<h2 id="langfuse-llm-observability-amp-tracing">Langfuse – LLM Observability &amp; Tracing</h2><p>Langfuse is an open-source LLM engineering platform (Apache-2.0, self-hostable) that provides end-to-end observability for <a href="https://github.com/langfuse/langfuse">AI apps</a>.  It helps teams track, debug and improve LLM calls, logging each prompt and response as a trace. For example, Langfuse offers a simple decorator you wrap around your LLM calls:</p>
<pre><code class="language-python">from langfuse import observe
from langfuse.openai import openai

@observe()
def query():
    return openai.chat.completions.create(
        model=&quot;gpt-4o&quot;,
        messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Explain Langfuse&quot;}],
    ).choices[0].message.content

query()</code></pre><p>This <code>@observe()</code> decorator automatically logs the prompt, tokens, latency, and model parameters to <a href="https://github.com/langfuse/langfuse">Langfuse</a>. The data appears in the Langfuse UI (local or cloud) so you can visualize sessions, spot errors, compare model performance, and manage prompts. Langfuse also includes versioned prompt management and evaluation tools, making it much easier to monitor costs and quality in production. See Langfuse on GitHub and try <a href="https://github.com/langfuse/langfuse">their quickstart for more integration examples</a>.</p>
<h2 id="flowise-visual-llm-flow-builder">Flowise – Visual LLM Flow Builder</h2><p>Flowise’s drag-and-drop interface lets you connect LLM nodes, agents, and tools into a workflow. Flowise (MIT license) is a no-code/low-code LLM orchestration tool. It provides modular building blocks (“nodes”) so you can visually chain together models, prompt templates, retrieval tools, and decision <a href="https://flowiseai.com/">logic</a>. This is ideal for quickly prototyping AI assistants or autonomous agents without writing boilerplate code.</p>
<p>Getting started is easy: install the CLI, then launch the local UI with:</p>
<pre><code class="language-bash">npm install -g flowise
npx flowise start</code></pre><p>This opens a web UI at <code>localhost:3000</code> where you can drag in nodes, set their parameters, and link them. For example, you might drop a chat model node, a function-calling node, and a data retriever into a flow. Flowise also supports multiple agents, human-in-the-loop review steps, and integrations with vector databases. In short, Flowise “provides modular building blocks … <a href="https://github.com/FlowiseAI/Flowise">to build any agentic system, from simple workflows to autonomous agents</a>”. </p>
<p>Check out the Flowise GitHub for code <a href="https://github.com/FlowiseAI/Flowise">and deployment options (Docker, cloud, etc)</a>.</p>
<h2 id="dust-multi-agent-ai-platform">Dust – Multi-Agent AI Platform</h2><p>Dust (MIT license) is a Rust/TypeScript-based platform for building AI agents and assistants. It started as a developer toolkit but now aims at enterprise AI deployments. Essentially, Dust lets you spin up multiple specialized agents (bots) that collaborate and use external tools, databases, or company knowledge bases. Unlike a single chatbot, Dust supports context-aware multi-agent workflows. For example, you might create a “Research Agent” to gather data and an “Analysis Agent” to interpret it, then orchestrate them in a pipeline. Dust includes a web UI and APIs for agent management. Developers can self-host it – the repo provides Docker Compose files to launch Dust’s services. In practice, using Dust involves running <code>docker-compose up</code> (per the repository) and then interacting with its REST API or UI to create agents. On GitHub the description summarizes it succinctly:<a href="https://github.com/dust-tt/dust"> “Dust – Custom AI agent platform to speed up your work.”</a>. With 1.1k+ stars, Dust is battle-tested and integrates with tools like Slack, Notion, and more via connectors. See the dust-tt/dust GitHub for docs and quickstart.</p>
<h2 id="openllm-deploy-any-open-llm-as-a-server">OpenLLM – Deploy Any Open LLM as a Server</h2><p>OpenLLM (Apache-2.0, by BentoML) makes running open-source LLMs in production a breeze. <a href="https://github.com/bentoml/OpenLLM">It provides a unified CLI/SDK so you can treat any model like an OpenAI API endpoint</a>. </p>
<p>For example, installing OpenLLM is as simple as:</p>
<pre><code class="language-bash">pip3 install openllm # install the package
openllm hello # try the example</code></pre><p>You can then serve models with one command. For instance:</p>
<pre><code class="language-bash">openllm serve llamacpp       # serves a local Llama model via HTTP
openllm serve starcoder:1.0  # fetches and serves StarCoder 1.0, GPU-optimized</code></pre><p>Behind the scenes, OpenLLM integrates high-performance backends (Deepseek, vLLM, Ollama, etc.) and even offers a basic chat UI. It also handles Docker/Kubernetes deployment. Crucially, OpenLLM supports dozens of open LLMs (Llama 3.3, Qwen, Gemma, etc.) and will automatically download weights if needed. By conforming to the OpenAI API spec, your code doesn’t need to change if you swap a model from proprietary to open. OpenLLM is battle-tested (used in BentoCloud) and ideal for teams wanting to switch to local or custom models while keeping a familiar API. See the OpenLLM GitHub for more examples.</p>
<h2 id="langchain-llm-integration-framework">LangChain – LLM Integration Framework</h2><p><a href="https://github.com/langchain-ai/langchain">LangChain (MIT license) remains the go-to Python framework for building LLM apps</a>. It provides a standard interface for LLMs, embeddings, vector stores, and tools, making it easy to glue components together. LangChain’s core concept is “chains”: modular sequences of calls (to models, searches, tools, etc.) that handle complex workflows. For instance, a simple chain might read a question, query a vector DB, and then prompt an LLM with context – all with a few lines of code. Installation and use are straightforward:</p>
<pre><code class="language-bash">pip install -U langchain   # install LangChain</code></pre><pre><code class="language-python">from langchain.llms import OpenAI
llm = OpenAI(model_name=&quot;gpt-4o&quot;)
print(llm(&quot;Tell me a joke&quot;))</code></pre><p>LangChain shines in RAG (retrieval-augmented generation), agent simulation, and multi-step pipelines. It also supports dozens of integrations (OpenAI, HuggingFace, Chroma, Weaviate, etc.), so you can swap backends easily. Importantly, LangChain’s abstractions future-proof your code – if a better model emerges, you just change a config. <a href="https://github.com/langchain-ai/langchain">For more, see the LangChain docs or the GitHub repo</a>.</p>
<h2 id="llamaindex-data-augmentation-amp-knowledge-indexing">LlamaIndex – Data Augmentation &amp; Knowledge Indexing</h2><p><a href="https://github.com/run-llama/llama_index">LlamaIndex</a> (MIT license) is a “data framework” for building LLM applications over your data. It provides tools to ingest documents, create indices, and run queries that augment prompts with relevant context. For example, suppose you have a directory of PDFs. </p>
<p>LlamaIndex lets you load them and build a vector store index in minutes:</p>
<pre><code class="language-bash">pip install llama-index-core llama-index-llms-openai llama-index-embeddings-huggingface</code></pre><pre><code class="language-python">from llama_index.core import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader(&quot;path/to/docs&quot;).load_data()
index = VectorStoreIndex.from_documents(documents)
response = index.query(&quot;What does the report say about feature X?&quot;)</code></pre><p>Under the hood, LlamaIndex handles tokenization, embeddings, and retrieval. You can use any LLM for querying (OpenAI, HuggingFace, Replicate, etc.) and any vector database. The library also supports other index types (trees, graphs). Essentially, LlamaIndex makes it trivial to add RAG to your app. Its rich ecosystem (300+ integrations) means it works with AWS, Pinecone, Supabase, and more. In short, <strong>LlamaIndex helps you “augment LLMs with private data</strong>, boosting accuracy and relevancy. <a href="https://github.com/run-llama/llama_index">Check out the LlamaIndex GitHub for guides and API details</a>.</p>
<h2 id="guidance-structured-prompting-language">Guidance – Structured Prompting Language</h2><p>Guidance (MIT license, by Anthropic) offers a new paradigm for prompt engineering. It introduces a <strong>domain-specific language embedded in Python</strong> to control generation. With Guidance, you can write templates with loops, conditionals, and even regex constraints, all inline. For example:</p>
<pre><code class="language-bash">pip install guidance</code></pre><pre><code class="language-python">from guidance import gen, models

llm = models.LlamaCpp(&quot;path/to/model.bin&quot;)
prompt = llm | gen(&quot;Tell me a poem about {topic}.&quot;, stop=&quot;.&quot;)
print(prompt(topic=&quot;sunrise&quot;))</code></pre><p>This code first loads an Llama model, then uses a template that injects a variable. Guidance ensures structured output and can drastically reduce off-topic responses. It even supports stateful control: you can programmatically manipulate generations, feed outputs back in, or use the <code>#[ ]</code> notation to “inscribe” values. Importantly, Guidance claims better cost and latency than naive prompting by optimizing tokens and reusing the same model session. It’s great for scenarios where precise output format is critical. See Guidance on GitHub for more examples and advanced features.</p>
<h2 id="text-generation-inference-tgi-high-performance-llm-server">Text Generation Inference (TGI) – High-Performance LLM Server</h2><p>Finally, Hugging Face’s Text Generation Inference (TGI) is a production-grade LLM server (Apache-2.0) written in Rust and Python. It’s optimized for low-latency, high-throughput inference on GPUs or CPUs. TGI supports models like Llama, Falcon, StarCoder, BLOOM, etc., and adds features like token streaming and batching. Getting started is simple with Docker. For example:</p>
<pre><code class="language-bash">docker run --gpus all --shm-size 1g -p 8080:80 \
    ghcr.io/huggingface/text-generation-inference:3.3.2 \
    --model-id HuggingFaceH4/zephyr-7b-beta</code></pre><p>This command launches a REST API on <code>localhost:8080</code>. You can then call the <code>/generate_stream</code> or OpenAI-compatible <code>/v1/chat/completions</code> endpoints to chat with the <a href="https://github.com/huggingface/text-generation-inference">model</a>. Under the hood, TGI uses parallelism across GPUs and supports tracing (Prometheus, OpenTelemetry) for metrics. In short, TGI lets you turn any open LLM into a scalable cloud endpoint, without crafting your own serving code. <a href="https://github.com/huggingface/text-generation-inference">The TGI GitHub has extensive docs, performance tips, and examples</a>.</p>
<h2 id="arkterm-ai-native-shell-terminal">Arkterm – AI-Native Shell Terminal</h2><p><a href="https://github.com/saadmanrafat/arkterm">Arkterm’s</a> AI-powered terminal interface, running an LLM inside your shell. <a href="https://saadman.dev/blog/2025-05-31-shell-shocked-wire-llm-directly-in-linux-terminal/">Arkterm (MIT licensed) literally wires a large language model into your Linux terminal</a>. You can speak natural language commands, ask for explanations, or have the LLM automate tasks. Arkterm is built by yours truly (inspired by Warp.dev). For example, after installing Arkterm, run:</p>
<pre><code class="language-bash">git clone https://github.com/saadmanrafat/arkterm.git
cd arkterm
uv pip install -e .
arkterm --setup</code></pre><p>This sets up a YAML config <code>(~/.aiterm/config.yaml)</code> where you specify an LLM and API key (e.g. via Groq). Once configured, your shell prompt becomes an AI assistant. Arkterm boosts productivity by letting devs query and extend their CLI with AI – great for scripting help or command automation. Check out Arkterm on GitHub for details.</p>
<h2 id="conclusion">Conclusion</h2><p>Open-source LLM tooling is exploding — and these projects are leading the way. Whether you’re building apps, monitoring them, prototyping agents, or just playing around with new workflows, there’s likely a tool here you can adopt today.</p>
<p>More importantly, many of these are built by small teams or indie founders who care deeply about transparency, control, and developer experience. Supporting them means supporting the future of open AI infrastructure.</p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/tool.png" length="0" type="image/jpeg" />
  </item>
  <item>
    <title><![CDATA[Shell Shocked: Wire an LLM Directly into Your Linux Terminal]]></title>
    <link>https://saadman.dev/blog/2025-05-31-shell-shocked-wire-llm-directly-in-linux-terminal/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2025-05-31-shell-shocked-wire-llm-directly-in-linux-terminal/</guid>
    <pubDate>Sat, 31 May 2025 00:00:00 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[LLM]]></category>
    <category><![CDATA[Linux]]></category>
    <category><![CDATA[CLI]]></category>
    <category><![CDATA[AI Tools]]></category>
    <category><![CDATA[Groq]]></category>
    <category><![CDATA[Terminal Automation]]></category>
    <category><![CDATA[Developer Tools]]></category>
    <category><![CDATA[bash]]></category>
    <category><![CDATA[zsh]]></category>
    <category><![CDATA[fish]]></category>
    <description><![CDATA[Transform your Linux terminal into an AI-powered assistant that understands natural language commands, explains actions, and automates tasks using Groq's LLM API.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> Transform your Linux terminal into an AI-powered assistant that understands natural language commands, explains actions, and automates tasks using Groq's LLM API.</p>
      <h1 id="shell-shocked-wire-an-llm-directly-into-your-linux-terminal">Shell Shocked: Wire an LLM Directly into Your Linux Terminal</h1><h2 id="the-philosophy-the-terminal-revolution-you-didn-t-see-coming">The Philosophy: The Terminal Revolution You Didn’t See Coming</h2><p>Inspired by a <a href="https://app.warp.dev/referral/EEGVZM">Wrap.dev</a> engineer’s description of their interface as a way to “tell your computer what to do” rather than just chat, I explored <a href="https://app.warp.dev/referral/EEGVZM">Wrap.dev</a> and developed my own minimal viable product for development. This project draws inspiration from <a href="https://app.warp.dev/referral/EEGVZM">Wrap.dev</a>, aiming to create a comparable tool, though less sophisticated.</p>
<p>Picture this: You’re staring at a blank terminal, trying to remember the exact <code>find</code> command syntax to locate all Python files modified in the last week. Instead of fumbling through man pages or Stack Overflow, you type:</p>
<p><img src="/assets/images/blog/arkterm.gif" alt="Arkterm an open-source alternative to Warp.dev"></p>
<pre><code class="language-bash">&gt; find all python files changed in the last 7 days</code></pre><p>And your terminal responds with the exact command you need, explains what it does, and offers to run it for you. Welcome to <strong>arkterm</strong> – where your Linux terminal becomes an AI-native workspace.</p>
<h2 id="what-is-arkterm-">What Is arkterm?</h2><p><code>arkterm</code> is an open-source tool that transforms your terminal into an intelligent assistant by integrating large language models directly into your shell environment. Unlike traditional chatbots that live in browsers, arkterm brings AI into the very heart of your development workflow – the command line.</p>
<p>Built with Python and powered by Groq’s lightning-fast LLM API, arkterm bridges the gap between natural language and shell commands, making your terminal understand context, intent, and even your current project structure.</p>
<h2 id="why-your-terminal-needs-ai-and-why-now-">Why Your Terminal Needs AI (And Why Now)</h2><h3 id="the-problem-with-traditional-command-lines">The Problem with Traditional Command Lines</h3><p>Modern developers juggle hundreds of commands across multiple tools, frameworks, and systems. The cognitive overhead of remembering syntax, flags, and edge cases slows down even experienced engineers. Traditional solutions like aliases and scripts only scratch the surface.</p>
<h3 id="the-ai-advantage">The AI Advantage</h3><p>arkterm leverages the power of models like Llama 3.1 and Mixtral to:</p>
<ul>
<li><strong>Understand Context</strong>: Knows your current directory, project type, and system environment</li>
<li><strong>Translate Intent</strong>: Converts natural language queries into precise shell commands</li>
<li><strong>Explain Actions</strong>: Shows you what commands do before executing them</li>
<li><strong>Learn Patterns</strong>: Adapts to your workflow and preferences</li>
</ul>
<h2 id="getting-started-from-zero-to-ai-terminal-in-60-seconds">Getting Started: From Zero to AI Terminal in 60 Seconds</h2><h3 id="prerequisites">Prerequisites</h3><ul>
<li>Python 3.10 or higher</li>
<li>A free Groq API key (<a href="https://groq.com/">get one here</a>)</li>
<li>uv package manager (optional but recommended)</li>
</ul>
<h3 id="installation">Installation</h3><pre><code class="language-bash"># Clone the repository
git clone https://github.com/saadmanrafat/arkterm.git
cd arkterm

# Install with uv (recommended)
uv pip install -e .

# Or with pip
pip install -e .</code></pre><h3 id="initial-setup">Initial Setup</h3><pre><code class="language-bash"># Create configuration file
arkterm --setup</code></pre><p>This creates a YAML configuration file at <code>~/.aiterm/config.yaml</code>:</p>
<pre><code class="language-yaml">API:
  api_key: &quot;YOUR_GROQ_API_KEY_HERE&quot;
  model: &quot;deepseek-r1-distill-llama-70b&quot;
  api_base: &quot;https://api.groq.com/openai/v1&quot;

SETTINGS:
  allow_command_execution: false
  max_tokens: 2048</code></pre><p>Add your Groq API key and you’re ready to go!</p>
<h2 id="core-features-that-make-arkterm-shine">Core Features That Make arkterm Shine</h2><h3 id="1-natural-language-command-generation">1. Natural Language Command Generation</h3><p>Instead of memorizing complex syntax, describe what you want:</p>
<pre><code class="language-bash">&gt; arkterm &quot;compress all log files older than 30 days&quot;</code></pre><p><strong>Output:</strong></p>
<pre><code class="language-bash">I&#039;ll help you compress log files older than 30 days. Here&#039;s the command:

find /var/log -name &quot;*.log&quot; -type f -mtime +30 -exec gzip {} \;

This command:
- Searches in /var/log directory
- Finds files with .log extension
- Filters files older than 30 days (-mtime +30)
- Compresses each file with gzip
</code></pre><h3 id="2-interactive-mode-for-continuous-assistance">2. Interactive Mode for Continuous Assistance</h3><p>Launch interactive mode for ongoing AI assistance:</p>
<pre><code class="language-bash">arkterm -i</code></pre><pre><code class="language-">&gt; check disk usage for directories over 1GB

Here&#039;s how to check disk usage for large directories:

```bash
du -h --max-depth=1 | grep &#039;[0-9]G&#039;


This command shows directory sizes and filters for those over 1GB.

&gt; !exec du -h --max-depth=1 | grep &#039;[0-9]G&#039;
Executing: du -h --max-depth=1 | grep &#039;[0-9]G&#039;
2.1G    ./node_modules
1.4G    ./datasets
3.2G    ./models</code></pre><h3 id="3-context-aware-project-detection">3. Context-Aware Project Detection</h3><p>arkterm automatically detects your project type and provides relevant suggestions:</p>
<pre><code class="language-python">def _detect_project_type(cwd: str) -&gt; str:
    &quot;&quot;&quot;Detects project type based on files in current directory&quot;&quot;&quot;
    files = os.listdir(cwd) if os.path.exists(cwd) else []
    
    if &quot;package.json&quot; in files:
        return &quot;Node.js project detected&quot;
    elif &quot;requirements.txt&quot; in files or &quot;pyproject.toml&quot; in files:
        return &quot;Python project detected&quot;
    elif &quot;Cargo.toml&quot; in files:
        return &quot;Rust project detected&quot;
    elif &quot;go.mod&quot; in files:
        return &quot;Go project detected&quot;
    elif &quot;.git&quot; in files:
        return &quot;Git repository detected&quot;
    else:
        return &quot;General directory&quot;</code></pre><p>This context gets passed to the AI, enabling project-specific suggestions.</p>
<h3 id="4-safety-first-command-execution">4. Safety-First Command Execution</h3><p>arkterm prioritizes safety with built-in protections:</p>
<pre><code class="language-python"># Safety guidelines built into the system prompt
SAFETY_RULES = &quot;&quot;&quot;
- Never execute commands that could damage the system
- Always warn about destructive operations (rm -rf, format, etc.)
- Prefer safer alternatives (mv to trash vs rm, etc.)
- Ask before modifying system files or configurations
&quot;&quot;&quot;</code></pre><h2 id="advanced-use-cases">Advanced Use Cases</h2><h3 id="devops-automation">DevOps Automation</h3><pre><code class="language-bash">&gt; arkterm &quot;show me all docker containers using more than 500MB memory&quot;</code></pre><p><strong>Response:</strong></p>
<pre><code class="language-bash">docker stats --format &quot;table {{.Container}}\t{{.Name}}\t{{.MemUsage}}&quot; --no-stream | awk &#039;NR&gt;1 &amp;&amp; $3 ~ /[5-9][0-9][0-9]M|[0-9]+G/ {print}&#039;</code></pre><h3 id="log-analysis">Log Analysis</h3><pre><code class="language-bash">&gt; arkterm &quot;find all 404 errors in nginx logs from last hour&quot;</code></pre><p><strong>Response:</strong></p>
<pre><code class="language-bash">grep &quot;404&quot; /var/log/nginx/access.log | awk -v cutoff=$(date -d &#039;1 hour ago&#039; &#039;+%d/%b/%Y:%H:%M:%S&#039;) &#039;$4 &gt; &quot;[&quot;cutoff&#039;</code></pre><h3 id="system-monitoring">System Monitoring</h3><pre><code class="language-bash">&gt; arkterm &quot;alert me if CPU usage goes above 80%&quot;</code></pre><p><strong>Response:</strong></p>
<pre><code class="language-bash">while true; do
  cpu=$(top -bn1 | grep &quot;Cpu(s)&quot; | awk &#039;{print $2}&#039; | sed &#039;s/%us,//&#039;)
  if (( $(echo &quot;$cpu &gt; 80&quot; | bc -l) )); then
    notify-send &quot;High CPU Usage&quot; &quot;CPU usage is ${cpu}%&quot;
  fi
  sleep 60
done</code></pre><h2 id="the-technical-architecture">The Technical Architecture</h2><h3 id="core-components">Core Components</h3><p>To begin, establish a repository using <a href="https://saadman.dev/blog/2025-05-15-a-no-nonsense-guide-to-uv-a-python-package-manager/">UV, a Python package manager</a> designed to streamline dependency management and virtual environments. Next, navigate to <a href="https://groq.com/">Groq</a> (not Grok), register for their service, and get a free API key from their developer portal. Groq offers production-ready models such as <code>gemma2-9b-it</code>, <code>meta-llama/Llama-Guard-4-12B</code>, <code>llama-3.1-8b-instant</code> and more at no cost, though API rate limits and context token window restrictions apply. A comprehensive list of their <a href="https://console.groq.com/docs/models">supported models is provided</a>.</p>
<p>The initial approach considered LangChain, but its intricate nature led to its exclusion from this project. As a result, we will use the <code>requests</code> library to communicate with Groq’s LLMs through API calls. It is important to note that Groq is not currently supported by LangChain. However, as engineers, we are adept at finding solutions.</p>
<pre><code class="language-python"># Core workflow
def fetch_response(query: str, config: Dict[str, Any]) -&gt; str:
    &quot;&quot;&quot;Main AI interaction pipeline&quot;&quot;&quot;
    
    # 1. Gather system context
    system_info = fetch_system_info()
    
    # 2. Create enhanced prompt with context
    system_prompt = get_enhanced_system_prompt(system_info)
    
    # 3. Call Groq API
    payload = {
        &quot;model&quot;: config[&quot;API&quot;][&quot;model&quot;],
        &quot;messages&quot;: [
            {&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: system_prompt},
            {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: query}
        ],
        &quot;max_tokens&quot;: config[&quot;SETTINGS&quot;][&quot;max_tokens&quot;],
        &quot;temperature&quot;: 0.7
    }
    
    # 4. Parse and return response
    response = requests.post(api_endpoint, json=payload)
    return response.json()[&quot;choices&quot;][0][&quot;message&quot;][&quot;content&quot;]</code></pre><h3 id="command-block-parsing">Command Block Parsing</h3><p>arkterm intelligently extracts executable commands from AI responses:</p>
<pre><code class="language-python">def parse_command_blocks(response: str) -&gt; list[str]:
    &quot;&quot;&quot;Extract command blocks from markdown-formatted AI responses&quot;&quot;&quot;
    command_blocks = []
    current_block = []
    in_code_block = False
    
    for line in response.splitlines():
        stripped = line.strip()
        if stripped.startswith(&quot;```&quot;):
            in_code_block = not in_code_block
            if not in_code_block and current_block:
                command_blocks.append(&quot;\n&quot;.join(current_block))
                current_block.clear()
        elif in_code_block:
            current_block.append(line)
    
    return command_blocks</code></pre><h2 id="why-groq-the-speed-advantage">Why Groq? The Speed Advantage</h2><p>arkterm uses Groq’s inference engine for several key reasons:</p>
<ol>
<li><strong>Blazing Fast</strong>: Sub-second response times for real-time terminal interaction</li>
<li><strong>Cost Effective</strong>: Free tier with generous limits perfect for CLI usage</li>
<li><strong>Multiple Models</strong>: Access to Llama 3.1, Mixtral, and Gemma models</li>
<li><strong>High Quality</strong>: Production-ready models with excellent code generation capabilities</li>
</ol>
<h2 id="configuration-and-customization">Configuration and Customization</h2><h3 id="model-selection">Model Selection</h3><p>Switch between different models based on your needs:</p>
<pre><code class="language-bash"># In interactive mode
&gt; !model

Available Groq Models:
- llama3-8b-8192 (Fast, efficient)
- llama3-70b-8192 (More powerful)
- mixtral-8x7b-32768 (Mixtral model)
- gemma-7b-it (Google&#039;s Gemma model)
- deepseek-r1-distill-llama-70b (Distilled Llama model)

Currently using: llama3-8b-8192
Change model? (Enter model name or press Enter to keep current):</code></pre><h3 id="safety-configuration">Safety Configuration</h3><p>Control command execution permissions:</p>
<pre><code class="language-yaml">SETTINGS:
  allow_command_execution: true  # Enable command execution
  max_tokens: 4096              # Increase for longer responses</code></pre><h2 id="best-practices-and-pro-tips">Best Practices and Pro Tips</h2><h3 id="1-be-specific-with-context">1. Be Specific with Context</h3><pre><code class="language-bash"># Instead of: &quot;delete old files&quot;
# Try: &quot;delete log files older than 7 days in /var/log&quot;</code></pre><h3 id="2-use-interactive-mode-for-complex-tasks">2. Use Interactive Mode for Complex Tasks</h3><p>For multi-step operations, interactive mode maintains context:</p>
<pre><code class="language-bash">arkterm -i
&gt; I need to set up a Python web scraper project
&gt; create a virtual environment for it
&gt; install requests and beautifulsoup4
&gt; create a basic scraper template</code></pre><h3 id="3-leverage-project-detection">3. Leverage Project Detection</h3><p>arkterm automatically understands your project context:</p>
<pre><code class="language-bash"># In a Node.js project
&gt; arkterm &quot;add a new dependency for testing&quot;
# Suggests: npm install --save-dev jest

# In a Python project  
&gt; arkterm &quot;add a new dependency for testing&quot;
# Suggests: pip install pytest</code></pre><h2 id="the-future-of-ai-native-terminals">The Future of AI-Native Terminals</h2><p><code>arkterm</code> represents just the beginning of AI-native command-line interfaces. Future developments might include:</p>
<ul>
<li><strong>Local Model Support</strong>: Running models like Code Llama locally for offline operation</li>
<li><strong>Shell Integration</strong>: Direct shell hooks for seamless AI assistance</li>
<li><strong>Team Knowledge Bases</strong>: Shared command libraries and best practices</li>
<li><strong>Automated Workflows</strong>: AI-generated scripts for complex multi-step operations</li>
</ul>
<h2 id="getting-involved">Getting Involved</h2><p><code>arkterm</code> is open-source and welcomes contributions:</p>
<ul>
<li><strong>GitHub</strong>: <a href="https://github.com/saadmanrafat/arkterm">saadmanrafat/arkterm</a></li>
<li><strong>Issues</strong>: Report bugs or request features</li>
<li><strong>Pull Requests</strong>: Contribute code improvements</li>
<li><strong>Discussions</strong>: Share use cases and ideas</li>
</ul>
<h2 id="conclusion-your-terminal-supercharged">Conclusion: Your Terminal, Supercharged</h2><p><code>arkterm</code> transforms the humble terminal from a command interpreter into an intelligent assistant that understands your intent, explains its actions, and helps you work more efficiently. By bringing large language models directly into the command line, we’re not just changing how we interact with our computers – we’re fundamentally rethinking what a terminal can be.</p>
<p>Whether you’re a DevOps engineer managing complex infrastructure, a developer navigating unfamiliar codebases, or a power user who lives in the terminal, arkterm offers a glimpse into the future of human-computer interaction.</p>
<p>The age of typing cryptic commands from memory is ending. The era of conversational computing has begun.</p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/arkterm_blog.png" length="0" type="image/jpeg" />
  </item>
  <item>
    <title><![CDATA[Seeing the World: A Beginner's Guide to CNNs using PyTorch]]></title>
    <link>https://saadman.dev/blog/2025-05-21-seeing-the-world-a-beginners-guide-to-convolution-neural-networks-using-pytorch/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2025-05-21-seeing-the-world-a-beginners-guide-to-convolution-neural-networks-using-pytorch/</guid>
    <pubDate>Wed, 21 May 2025 00:00:00 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Machine Learning]]></category>
    <category><![CDATA[Deep Learning]]></category>
    <category><![CDATA[Computer Vision]]></category>
    <category><![CDATA[PyTorch]]></category>
    <category><![CDATA[Convolutional Neural Network]]></category>
    <category><![CDATA[tutorial]]></category>
    <category><![CDATA[guide]]></category>
    <description><![CDATA[Learn the fundamentals of Convolutional Neural Networks (CNNs) using PyTorch. A beginner-friendly introduction to how machines see images.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> A comprehensive guide to understanding and implementing Convolutional Neural Networks using PyTorch, covering everything from basic concepts to practical implementation.</p>
      <h1 id="seeing-the-world-a-beginner-s-guide-to-convolutional-neural-networks-using-pytorch">Seeing the World: A Beginner’s Guide to Convolutional Neural Networks using PyTorch</h1><p>Welcome to the fascinating world of deep learning! If you’ve ever wondered how computers can recognize objects in images, distinguish between different types of clouds, or even power automated passport control systems, you’re about to uncover one of the key technologies behind it: <strong>Convolutional Neural Networks</strong> (CNNs).</p>
<p>These powerful neural networks are specifically designed to handle image data and have revolutionized computer vision over the past decade.</p>
<p><img src="https://saadman.dev/assets/images/blog/cnn-architecture.svg" alt="Visualization of a CNN architecture showing input image, convolutional layers, pooling layers, and output classification"></p>
<h2 id="how-computers-see-images">How Computers See Images</h2><p>Before diving into CNNs, let’s understand how computers perceive images. Digital images are made up of tiny squares called <strong>pixels</strong>. </p>
<p>In a grayscale image, each pixel holds a numerical value representing a shade of gray, typically from 0 (black) to 255 (white). For color images, each pixel usually has three numerical values representing the intensity of Red, Green, and Blue (RGB) channels.</p>
<p>These values are organized into a tensor (like a multi-dimensional array) with dimensions for:</p>
<ul>
<li>Color channels (e.g., 3 for RGB)</li>
<li>Height (number of pixel rows)</li>
<li>Width (number of pixel columns)</li>
</ul>
<h2 id="the-problem-with-traditional-neural-networks-for-images">The Problem with Traditional Neural Networks for Images</h2><p>You might recall that traditional neural networks use linear layers where every input neuron is connected to every output neuron (fully connected networks). This architecture works well for data with a small number of features, but images pose a significant challenge.</p>
<p>Consider a simple grayscale image of 256×256 pixels:</p>
<ul>
<li>This single image has over 65,000 input features</li>
<li>If you used a linear layer with even a modest 1,000 neurons, you’d end up with over 65 million parameters just in that first layer</li>
<li>For color images, this number jumps significantly</li>
</ul>
<p>Such a large number of parameters creates multiple problems:</p>
<ol>
<li>Training becomes extremely slow</li>
<li>The risk of overfitting increases dramatically</li>
<li>Most critically, linear layers don’t inherently understand spatial patterns</li>
</ol>
<p>If a linear layer learns to detect a feature, like a cat’s ear, in one corner of an image, it won’t automatically recognize the same ear if it appears in a different location. Images are all about patterns and their spatial relationships!</p>
<h2 id="introducing-convolutional-layers">Introducing Convolutional Layers</h2><p>This is where convolutional layers come in. CNNs use <strong>convolutional layers</strong> as a much more efficient and effective way to process images.</p>
<p>Instead of connecting every input pixel to every neuron, convolutional layers use small grids of parameters called <strong>filters</strong> (or kernels). These filters slide over the input image (or a feature map from a previous layer), performing a convolution operation at each position.</p>
<p><img src="https://saadman.dev/assets/images/blog/convolution-animation.svg" alt="Animation showing a convolutional filter sliding over an input image"></p>
<p>The convolution operation is essentially a dot product between the filter and a patch of the input data covered by the filter. The results of this sliding operation at each position are collected to create a <strong>feature map</strong>.</p>
<h3 id="key-advantages-of-convolutional-layers-">Key advantages of convolutional layers:</h3><ol>
<li><strong>Parameter efficiency</strong>: They use far fewer parameters than linear layers for images</li>
<li><strong>Location invariance</strong>: If a filter learns to detect a pattern, it can recognize that pattern regardless of its location in the input</li>
<li><strong>Hierarchical feature learning</strong>: Early layers can detect simple features like edges and textures, while deeper layers combine these to detect complex features like shapes and objects</li>
</ol>
<p>In PyTorch, you define a convolutional layer using <code>nn.Conv2d</code>. You specify the number of input and output feature maps (or channels) and the kernel size:</p>
<pre><code class="language-python"># 3 input channels (RGB), 32 output feature maps, 3x3 filter size
conv_layer = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3)</code></pre><h2 id="essential-cnn-operations-padding-and-pooling">Essential CNN Operations: Padding and Pooling</h2><p>Two other common operations in CNNs are zero padding and pooling.</p>
<h3 id="zero-padding">Zero Padding</h3><p>Often, zeros are added around the borders of the input before applying a convolutional layer. This technique helps:</p>
<ul>
<li>Control the spatial dimensions of the output</li>
<li>Ensure that pixels at the border of the image are treated equally</li>
<li>Prevent information loss at the edges</li>
</ul>
<p>In PyTorch, you can specify padding using the <code>padding</code> argument in <code>nn.Conv2d</code>:</p>
<pre><code class="language-python"># Add 1 pixel of padding around the borders
conv_layer = nn.Conv2d(
    in_channels=3, 
    out_channels=32, 
    kernel_size=3, 
    padding=1
 )</code></pre><h3 id="max-pooling">Max Pooling</h3><p>This operation typically follows convolutional layers. A non-overlapping window slides over the feature map, and at each position, the maximum value within the window is selected.</p>
<p><img src="https://saadman.dev/assets/images/blog/max-pooling.svg" alt="Illustration of max pooling with a 2x2 window"></p>
<p>Using a 2×2 window, for instance, halves the height and width of the feature map. Max pooling helps to:</p>
<ul>
<li>Reduce the spatial dimensions</li>
<li>Decrease the number of parameters and computational complexity</li>
<li>Make the model more invariant to small shifts and distortions</li>
</ul>
<p>In PyTorch, you implement max pooling with <code>nn.MaxPool2d</code>:</p>
<pre><code class="language-python"># 2x2 max pooling
pool_layer = nn.MaxPool2d(kernel_size=2)</code></pre><h2 id="building-a-cnn-architecture">Building a CNN Architecture</h2><p>A typical CNN for image classification has two main parts: a <strong>feature extractor</strong> and a <strong>classifier</strong>.</p>
<h3 id="1-feature-extractor">1. Feature Extractor</h3><p>This part is usually composed of repeated blocks of:</p>
<ul>
<li>Convolutional layers</li>
<li>Activation functions</li>
<li>Max pooling layers</li>
</ul>
<p>Its purpose is to process the raw pixel data and extract relevant features.</p>
<h3 id="2-classifier">2. Classifier</h3><p>This part takes the flattened output of the feature extractor (which is now a vector) and passes it through one or more linear layers to make the final prediction. The output dimension of the last linear layer matches the number of target classes.</p>
<p>Here’s a simple CNN architecture in PyTorch:</p>
<pre><code class="language-python">import torch
import torch.nn as nn

class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        
        # Feature extractor
        self.features = nn.Sequential(
            # First block
            nn.Conv2d(in_channels=3, out_channels=16, 
                      kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
            
            # Second block
            nn.Conv2d(in_channels=16, out_channels=32, 
                      kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
            
            # Third block
            nn.Conv2d(in_channels=32, out_channels=64, 
                      kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        
        # Classifier
        self.classifier = nn.Sequential(
            # Assuming input image was 32x32, 
            # after 3 pooling layers it&#039;s 4x4
            
            nn.Flatten(),  # Flatten the 4x4x64 feature maps
            nn.Linear(4 * 4 * 64, 128),
            nn.ReLU(),
            nn.Linear(128, num_classes)
        )
    
    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x</code></pre><h2 id="activation-functions-for-cnns">Activation Functions for CNNs</h2><p>Like other neural networks, CNNs need nonlinearity to learn complex patterns. Activation functions are crucial for this.</p>
<p>For the hidden layers within the feature extractor, common choices include:</p>
<h3 id="relu-rectified-linear-unit-">ReLU (Rectified Linear Unit)</h3><ul>
<li>Outputs the input value if positive, and zero otherwise</li>
<li>Avoids the vanishing gradients problem for positive inputs</li>
<li>Available as <code>nn.ReLU</code></li>
<li>The most common choice for CNNs</li>
</ul>
<h3 id="leaky-relu">Leaky ReLU</h3><ul>
<li>A variation of ReLU that outputs a small non-zero value for negative inputs</li>
<li>Prevents the “dying neuron” problem sometimes seen with standard ReLU</li>
<li>Available as <code>nn.LeakyReLU</code> with a <code>negative_slope</code> argument</li>
</ul>
<p>For the output layer:</p>
<ul>
<li><strong>Sigmoid</strong> is typically used for binary classification</li>
<li><strong>Softmax</strong> is used for multiclass classification</li>
</ul>
<h2 id="handling-image-data-in-pytorch">Handling Image Data in PyTorch</h2><p>To train a CNN, you need to prepare your image data. PyTorch’s <code>torchvision</code> library is very helpful here.</p>
<p>With a directory structure where each class has its own folder, you can use <code>ImageFolder</code> to create a dataset:</p>
<pre><code class="language-python">import torch
import torchvision
import torchvision.transforms as transforms

from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader


# Define transformations for images
image_transforms = transforms.Compose([
    transforms.ToTensor(),        # Convert PIL Image to PyTorch Tensor
    transforms.Resize((128, 128)) # Resize image to 128x128
])

# Create a dataset using ImageFolder
# Assumes data is in a directory structure like:
# cloud_train/
#   ├── class1/
#   │   └── img1.jpg
#   └── class2/
#       └── img2.jpg

train_dataset = ImageFolder(root=&#039;cloud_train&#039;,transform=image_transforms)

# Create a DataLoader for efficient batching and shuffling
train_loader = DataLoader(train_dataset,batch_size=32, shuffle=True)</code></pre><h2 id="data-augmentation-making-your-model-robust">Data Augmentation: Making Your Model Robust</h2><p>A powerful technique for image data, especially to combat overfitting, is <strong>data augmentation</strong>. This involves applying random transformations to the training images, such as:</p>
<ul>
<li><strong>Random Rotation</strong>: Exposes the model to objects at different angles</li>
<li><strong>Horizontal Flip</strong>: Simulates different viewpoints</li>
<li><strong>Color Jitter</strong>: Simulates different lighting conditions</li>
</ul>
<p><img src="https://saadman.dev/assets/images/blog/data-augmentation.svg" alt="Examples of data augmentation techniques applied to a sample image"></p>
<p>These transformations artificially increase the size and diversity of your training set, making the model more robust to variations found in real-world images.</p>
<p>Implementation in PyTorch is straightforward:</p>
<pre><code class="language-python">train_transforms = transforms.Compose([
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.RandomRotation(degrees=15),
    transforms.ColorJitter(brightness=0.1, contrast=0.1),
    transforms.ToTensor(),
    transforms.Resize((128, 128))
])

# Data augmentation only for training data, not validation/test
train_dataset = torchvision.datasets.ImageFolder(
    root=&#039;cloud_train&#039;,
    transform=train_transforms
)</code></pre><p>Remember to choose augmentations that are appropriate for your specific task. Some augmentations could change the meaning of the image (e.g., flipping a “W” vertically might make it look like an “M”).</p>
<h2 id="training-your-cnn">Training Your CNN</h2><p>Training a CNN involves the standard deep learning training loop:</p>
<ol>
<li>Define a loss function (e.g., <code>nn.CrossEntropyLoss</code> for multiclass classification)</li>
<li>Choose an optimizer (e.g., <code>optim.Adam</code> or <code>optim.SGD</code>)</li>
<li>Loop through multiple epochs (full passes through the training data)</li>
<li>Inside each epoch, process batches of data from the data loader</li>
</ol>
<p>Here’s a complete training loop:</p>
<pre><code class="language-python">import torch.optim as optim

# Instantiate model, loss function, and optimizer
model = SimpleCNN(num_classes=10)
criterion = nn.CrossEntropyLoss()  # For multiclass classification
optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam optimizer

device = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)
model.to(device)

# Training loop
num_epochs = 10
for epoch in range(num_epochs):
    model.train()  # Set model to training mode
    running_loss = 0.0
    
    for inputs, labels in train_loader:
        # Move data to the same device as model
        inputs, labels = inputs.to(device), labels.to(device)
        
        # Zero the parameter gradients
        optimizer.zero_grad()
        
        # Forward pass
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        
        # Backward pass and optimize
        loss.backward()
        optimizer.step()
        
        running_loss += loss.item()
    
    print(f&quot;Epoch {epoch+1}/{num_epochs}, Loss: {running_loss/len(train_loader):.4f}&quot;)</code></pre><h2 id="evaluating-your-cnn">Evaluating Your CNN</h2><p>Evaluating your model’s performance is crucial. Data is typically split into training, validation, and test sets.</p>
<p>Key evaluation metrics for classification include:</p>
<ul>
<li><strong>Accuracy</strong>: The overall frequency of correct predictions</li>
<li><strong>Precision</strong>: The fraction of correct positive predictions among all positive predictions</li>
<li><strong>Recall</strong>: The fraction of all positive examples that were correctly predicted</li>
<li><strong>F1 Score</strong>: The harmonic mean of precision and recall</li>
</ul>
<p>Here’s an evaluation loop:</p>
<pre><code class="language-python"># Evaluation loop
model.eval()  # Set model to evaluation mode
correct = 0
total = 0

with torch.no_grad():  # Disable gradient calculation
    for inputs, labels in test_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        
        # Forward pass
        outputs = model(inputs)
        
        # Get predicted class
        _, predicted = torch.max(outputs, 1)
        
        # Update statistics
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

accuracy = 100 * correct / total
print(f&quot;Test Accuracy: {accuracy:.2f}%&quot;)</code></pre><p>Tracking training loss vs. validation loss (and accuracy) is key to detecting overfitting; if training loss keeps decreasing but validation loss starts to rise, your model is overfitting.</p>
<h2 id="fighting-overfitting-in-cnns">Fighting Overfitting in CNNs</h2><p>Besides data augmentation, other strategies to fight overfitting include:</p>
<h3 id="1-dropout">1. Dropout</h3><p>Randomly deactivating a fraction of neurons during training, preventing over-reliance on specific features:</p>
<pre><code class="language-python">nn.Conv2d(32, 64, 3),
nn.ReLU(),
nn.Dropout(p=0.25),  # 25% dropout after activation
nn.MaxPool2d(2)</code></pre><h3 id="2-batch-normalization">2. Batch Normalization</h3><p>Normalizing the activations of the previous layer to speed up training and add some regularization:</p>
<pre><code class="language-python">nn.Conv2d(32, 64, 3),
nn.BatchNorm2d(64),  # Batch normalization after convolution
nn.ReLU()</code></pre><h3 id="3-weight-decay">3. Weight Decay</h3><p>Adding a penalty to the loss function to encourage smaller weights:</p>
<pre><code class="language-python">optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)</code></pre><h3 id="4-early-stopping">4. Early Stopping</h3><p>Monitoring validation performance and stopping training when it starts to degrade.</p>
<h2 id="modern-cnn-architectures">Modern CNN Architectures</h2><p>While our example used a simple CNN, many powerful architectures have been developed:</p>
<ul>
<li><strong>VGG</strong>: Uses very small 3×3 filters with many layers</li>
<li><strong>ResNet</strong>: Introduces skip connections to help train very deep networks</li>
<li><strong>Inception/GoogLeNet</strong>: Uses parallel paths with different filter sizes</li>
<li><strong>EfficientNet</strong>: Scales depth, width, and resolution together for efficiency</li>
</ul>
<p>Many of these are available pre-trained in <code>torchvision.models</code> and can be used for transfer learning.</p>
<h2 id="conclusion">Conclusion</h2><p>CNNs are the backbone of modern computer vision. By understanding how they process images through convolutional filters, pooling, and activation functions, you’ve taken a significant step in building powerful models that can truly “see” the world.</p>
<p>The key insights to remember:</p>
<ul>
<li>CNNs use sliding filters to detect patterns regardless of their location</li>
<li>They build hierarchical representations from simple features to complex ones</li>
<li>Techniques like pooling and padding help control spatial dimensions</li>
<li>Data augmentation and regularization techniques like dropout are essential for robust models</li>
</ul>
<p>Now it’s time to experiment and build your own CNN models! Whether you’re interested in image classification, object detection, or more advanced tasks like image segmentation, the principles covered here will serve as your foundation.</p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/cnn.png" length="0" type="image/jpeg" />
  </item>
  <item>
    <title><![CDATA[Deta Surf: Reclaim Your Digital World]]></title>
    <link>https://saadman.dev/blog/2025-05-20-deta-surf-reclaim-your-digital-world/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2025-05-20-deta-surf-reclaim-your-digital-world/</guid>
    <pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Productivity]]></category>
    <category><![CDATA[Browser]]></category>
    <category><![CDATA[Deta]]></category>
    <category><![CDATA[Local-first]]></category>
    <category><![CDATA[Surf]]></category>
    <category><![CDATA[UX]]></category>
    <category><![CDATA[Tools]]></category>
    <description><![CDATA[Explore how a new browser is rethinking the way we organize our digital lives using AI, local-first principles, and spatial design.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> Deta Surf is a new browser that combines AI, local-first data storage, and spatial interfaces to help users organize and access their digital content more effectively.</p>
      <h1 id="deta-surf-reclaim-your-digital-world">Deta Surf: Reclaim Your Digital World</h1><p>We spend so much time in our web browsers today—they’ve become the central hub of our digital lives. But more often than not, it feels like we’re just navigating a scattered mess. We’re constantly switching between remote apps and services where our information lives, disconnected from context.</p>
<p>Enter <strong>Deta Surf</strong> — a new, invite-only alpha browser handcrafted in Berlin that rethinks what a browser can be.</p>
<blockquote>
<p>Deta’s mission is to elevate minds through delightfully human computing—tools that enhance our intelligence and keep us in control.</p>
</blockquote>
<p>Surf blends the power of a browser, a file manager, and an AI assistant into one coherent interface. It aims to bring back the “personal” in personal computing by keeping your data local, contextual, and always accessible.</p>
<hr>
<h2 id="bringing-your-stuff-together">Bringing Your “Stuff” Together</h2><p>One of Surf’s key features is the concept of “Stuff.” This is a central place where you can collect and manage various types of digital content. This isn’t limited to just bookmarks; you can:</p>
<ul>
<li>Save websites as tabs  </li>
<li>Drag in files from your local machine  </li>
<li>Add images, YouTube videos, PDFs  </li>
<li>Capture super-powered screenshots</li>
</ul>
<p>The idea is that it’s all multimedia, all in one place, and easily searchable.</p>
<blockquote>
<p>“It’s a game changer for collecting wisdom, research, and notes,” said Limhi, a life guide and former law graduate, who appreciates that Surf keeps everything in one place and makes it easy to trust it to surface what’s needed.</p>
</blockquote>
<hr>
<h2 id="organizing-with-contexts">Organizing with Contexts</h2><p>To help manage the complexity of different projects or areas of your life, Surf introduces “Contexts.” These are like smart, focused environments—whether for work, reading, programming, or personal finances.</p>
<p>Within a context, you can:</p>
<ul>
<li>Open tabs (like a traditional browser)</li>
<li>Add long-term storage items</li>
<li>Visually arrange items on a “desktop”</li>
</ul>
<p>Surf remembers where you left off in each context, and you can switch between them freely.</p>
<p>You can create contexts manually or use “Smart Contexts” by simply describing a topic—like “linear algebra” or “ramen recipes”—and Surf will automatically group related content from your Stuff. These are dynamic and update live.</p>
<hr>
<h2 id="ai-that-actually-makes-sense">AI That Actually Makes Sense</h2><p>Surf’s standout feature is its thoughtful AI integration—not just AI for AI’s sake.</p>
<h3 id="-what-you-see-is-what-you-chat-">“What You See is What You Chat”</h3><p><img src="/assets/images/blog/deta_surf_video.gif" alt="deta_surf_video.gif"></p>
<p>You can select any area on your screen (like a block of text, image, or a YouTube video), draw a rectangle around it, and ask a question. The AI takes visual and contextual input into account to explain technical concepts or summarize information.</p>
<p>Surf can process:</p>
<ul>
<li>Dozens of open tabs  </li>
<li>Thousands of pages of content  </li>
<li>Transcripts from YouTube videos</li>
</ul>
<p>All without you needing to copy/paste or switch context.</p>
<h3 id="use-ai-with-any-source">Use AI with Any Source</h3><p>You can ask questions based on:</p>
<ul>
<li>The active tab  </li>
<li>All open tabs  </li>
<li>A specific “Stuff” collection  </li>
<li>A particular Context</li>
</ul>
<p>Surf will cite its sources so you can click and jump back to the original document or tab.</p>
<p>Currently supported LLMs include:</p>
<ul>
<li>Claude 3.7 Sonnet (default)</li>
<li>GPT-4o</li>
<li>Gemini Flash</li>
</ul>
<p>(Some models may not support vision features yet.)</p>
<hr>
<h2 id="spatial-browsing-and-local-data">Spatial Browsing and Local Data</h2><p>Taking cues from older desktop operating systems, Surf introduces spatial interfaces.</p>
<p>Each context can have its own desktop—an open canvas where you can arrange:</p>
<ul>
<li>Tabs  </li>
<li>PDFs  </li>
<li>Images  </li>
<li>Notes  </li>
<li>Other contexts</li>
</ul>
<p>You can set a photo as the background to personalize each space. Think of it as infinite desktops, linked together like a mind map.</p>
<h3 id="local-first-cloud-second-">Local First. Cloud Second.</h3><p>Deta Surf is built on a strong privacy foundation: your data lives locally.</p>
<ul>
<li>Files are stored on your device  </li>
<li>Local database powers search  </li>
<li>Local embeddings fuel the AI  </li>
<li>Even the local LLM can run offline</li>
</ul>
<p>Cloud features will exist, but as a secondary option—similar to Obsidian’s sync model.</p>
<hr>
<h2 id="who-is-using-surf-">Who is Using Surf?</h2><p>Deta Surf is already drawing attention from students, engineers, designers, and knowledge workers.</p>
<p><a href="https://deta.surf/people/limhi">Limhi’s experience</a> shows how Surf helps with:</p>
<ul>
<li>Organizing research  </li>
<li>Synthesizing large topics  </li>
<li>Revisiting trusted information</li>
</ul>
<hr>
<h2 id="alpha-status-and-the-road-ahead">Alpha Status and the Road Ahead</h2><p>Surf is still in its early days—invite-only, alpha-stage software. That means:</p>
<ul>
<li>You’ll encounter bugs  </li>
<li>It’s not ready as a full Chrome replacement (yet)  </li>
<li>It’s completely free for now</li>
</ul>
<p>In the future, Deta plans to charge for cloud-based features but promises to keep the local-first experience intact. Their broader mission is to create joyful tools that enhance human intelligence—not replace it.</p>
<hr>
<h2 id="try-it-out">Try It Out</h2><p>If you’re tired of feeling scattered—and excited by the idea of a browser that works with your context, understands your content, and respects your local data—then Deta Surf is absolutely worth watching.</p>
<p>🌐 <a href="https://deta.space/surf">Apply for early access</a></p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/deta_surf.jpg" length="0" type="image/jpeg" />
  </item>
  <item>
    <title><![CDATA[A no nonsense guide to UV (a Python Package Manager)]]></title>
    <link>https://saadman.dev/blog/2025-05-15-a-no-nonsense-guide-to-uv-a-python-package-manager/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2025-05-15-a-no-nonsense-guide-to-uv-a-python-package-manager/</guid>
    <pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[PIP]]></category>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[Python Package Manager]]></category>
    <category><![CDATA[UV]]></category>
    <category><![CDATA[Rust]]></category>
    <category><![CDATA[Development Tools]]></category>
    <category><![CDATA[tutorial]]></category>
    <category><![CDATA[guide]]></category>
    <description><![CDATA[A fast, modern, all-in-one Python package manager that simplifies dependency management, environments, and Python versions.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> UV is a blazing-fast Python package manager written in Rust that combines pip, virtualenv, pip-tools, and pyenv into one tool, offering 10-100x speed improvements.</p>
      <h1 id="a-no-nonsense-guide-to-uv-a-python-package-manager-">A no nonsense guide to UV (a Python Package Manager)</h1><p>UV, a modern Python package manager, comes from an unexpected place — it’s written in Rust. It’s<br>10-100 times faster than traditional package managers. However, its true strength lies in addressing<br>long-standing issues in Python package managers, such as <strong>complex dependency resolution</strong>, inefficient<br>virtual environment handling, and fragmented tooling, offering a more reliable and streamlined experience<br>for developers. It’s designed to be a drop-in replacement for <code>pip</code>, <code>virtualenv</code>, <code>pip-tools</code>, and sometimes<br>even <code>Poetry</code>.</p>
<p>Why Use UV?</p>
<ul>
<li>Instant environment creation</li>
<li>Fast, deterministic dependency resolution</li>
<li>Built-in virtualenv management</li>
<li>Lockfile support (uv.lock)</li>
<li>Built-in CLI tool isolation (uvx)</li>
<li>Python version management via .python-version</li>
<li>Single binary, no runtime deps</li>
</ul>
<p>Think of it as:<br><code>pip</code> + <code>virtualenv</code> + <code>pip-tools</code> + <code>pyenv</code> + <code>parts of Poetry</code> + <code>parts of Conda</code>, all in one CLI tool<br>— but up to 100x faster.</p>
<p>Install UV with <a href="https://github.com/astral-sh/uv#installation">Astral’s Installer</a>:</p>
<pre><code class="language-bash">curl -Ls https://astral.sh/uv/install.sh | bash</code></pre><h3 id="initialize-a-new-project">Initialize a new project</h3><pre><code class="language-bash">uv init</code></pre><p>This command creates a <code>pyproject.toml</code> and <code>.venv</code> with your current Python version. <code>uv init</code><br>can prompt for additional project metadata.</p>
<h3 id="add-dependencies">Add Dependencies</h3><pre><code class="language-bash">uv add requests</code></pre><h3 id="run-your-project-in-the-environment">Run your project in the environment</h3><pre><code class="language-bash">uv pip list
uv pip freeze
uv run main.py</code></pre><p>You can also activate the venv manually</p>
<pre><code class="language-bash">uv venv .venv  
source .venv/bin/activate</code></pre><h3 id="it-s-compatible-with-existing-code-pip-code-projects-or-code-requirements-txt-code-">It’s compatible with existing <code>pip</code> projects or <code>requirements.txt</code></h3><p>You can still install traditional requirements.txt:</p>
<pre><code class="language-bash">uv pip install -r requirements.txt</code></pre><h3 id="install-from-lockfile">Install from lockfile</h3><pre><code class="language-bash">uv install</code></pre><p>This installs all dependencies based on <code>pyproject.toml</code> and <code>uv.lock</code></p>
<h3 id="upgrade-dependencies">Upgrade dependencies</h3><pre><code class="language-bash">uv update</code></pre><p>Resolves and upgrades all packages, updating the lockfile.</p>
<h4 id="okay-so-far-what-do-we-have-">Okay so far what do we have?</h4><pre><code class="language-bash">$ tree
├── main.py
├── pyproject.toml
├── .python-version
├── README.md
└── uv.lock</code></pre><h3 id="using-any-python-version-with-code-uv-code-even-if-it-s-not-installed-">Using Any Python Version with <code>uv</code> (even if it’s not installed)</h3><p>One of UV’s most powerful features is how it handles Python versions seamlessly — even if you<br>don’t have that version installed locally.</p>
<pre><code class="language-bash">uv venv -p 3.6 .venv</code></pre><ul>
<li>This will create a virtual environment using Python 3.6.</li>
<li>If you don’t have Python 3.6 installed, UV will:<ul>
<li>Automatically detect that it’s missing.</li>
<li>Suggest installing it via pyenv, which UV supports natively.</li>
<li>Use <code>.python-version</code> to track which Python version the project is using.</li>
</ul>
</li>
</ul>
<h3 id="switching-python-versions-per-project">Switching Python Versions Per Project</h3><p>You can use <code>.python-version</code> manually:</p>
<pre><code class="language-bash">echo &quot;3.12.0&quot; &gt; .python-version
uv venv</code></pre><p>UV reads the version, creates the venv with that interpreter, and tracks everything in<br><code>pyproject.toml</code></p>
<h3 id="removing-package">Removing Package</h3><pre><code class="language-bash">uv remove requests </code></pre><h3 id="cleanup">Cleanup</h3><pre><code class="language-bash">rm -rf .venv uv.lock</code></pre><p>This makes UV fully self-contained and version-aware, solving one of the biggest pain points in Python development.</p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/python-package-manager.jpg" length="0" type="image/jpeg" />
  </item>
  <item>
    <title><![CDATA[The Reason pip Suddenly Refuses to Install Globally]]></title>
    <link>https://saadman.dev/blog/2025-05-15-the-reason-pip-refuses-to-install-globally/</link>
    <guid isPermaLink="true">https://saadman.dev/blog/2025-05-15-the-reason-pip-refuses-to-install-globally/</guid>
    <pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate>
    <dc:creator><![CDATA[Saadman Rafat]]></dc:creator>
    <category><![CDATA[python]]></category>
    <category><![CDATA[pep668]]></category>
    <category><![CDATA[pip]]></category>
    <category><![CDATA[linux]]></category>
    <category><![CDATA[debian]]></category>
    <category><![CDATA[ubuntu]]></category>
    <category><![CDATA[best practices]]></category>
    <category><![CDATA[virtual environments]]></category>
    <description><![CDATA[We will discuss Python Enhancement Proposal 668, specifically the error regarding externally managed environments, and elaborate on its benefits.]]></description>
    <content:encoded><![CDATA[
      <p><strong>TL;DR:</strong> PEP 668 prevents pip from installing packages globally on system-managed Python installations to protect critical OS components from breaking.</p>
      <h1 id="why-pip-suddenly-refuses-to-install-globally-">Why pip Suddenly Refuses to Install Globally?</h1><h2 id="let-s-try-to-recreate-the-error-message-">Let’s try to recreate the error message.</h2><pre><code class="language-md">$ pip3 install pandas
$ error: externally-managed-environment

× This environment is externally managed
╰─&gt; To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non\-Debian\-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3\-full installed.

    If you wish to install a non\-Debian\-packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.

    See /usr/share/doc/python3.12/README.venv for more information.

note: If you believe this is a mistake, please contact your Python 
installation or OS distribution provider. 
You can override this, at the risk of breaking your Python installation or OS, 
by passing --break-system-packages. 

hint: See PEP 668 for the detailed specification.</code></pre><blockquote>
<p>“Errors should never pass silently—unless explicitly silenced.” — The Zen of Python</p>
</blockquote>
<p>One such error is <code>externally-managed-environment</code>.</p>
<p>This message indicates that your Python environment is controlled by your operating system’s package manager (like <code>apt</code>, <code>dnf</code>, or <code>yum</code>), not by you. In simpler terms:</p>
<blockquote>
<p>Your OS manages this Python installation. Hands off.</p>
</blockquote>
<p>These system-managed environments are used to run critical tools, and direct modifications via <code>pip</code> could break essential components. That’s why these environments restrict you from installing or uninstalling packages freely. This isn’t a core Python change; it’s a coordinated effort between Python packaging tools and Linux distribution maintainers to improve system stability. Starting in 2022, distributions like <strong>Debian</strong>, <strong>Ubuntu 22.04+</strong>, and <strong>Fedora</strong> began enforcing this policy.</p>
<p>It’s also why tools like Docker can run into complications—making the transition to <a href="https://peps.python.org/pep-0668/">PEP 668</a> a frustrating experience, to say the least.</p>
<h2 id="how-should-you-install-python-packages-in-2025-">How Should You Install Python Packages in 2025?</h2><pre><code class="language-bash">$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip3 install pandas # example</code></pre><p>When creating virtual environments, check out <a href="https://saadman.dev/blog/2025-05-15-a-no-nonsense-guide-to-uv-a-python-package-manager/">UV — a Python package manager</a> written in Rust, which is lightning fast. This gives you a self-contained environment with full control over what gets installed, without interference with the system Python and without the need to use <code>sudo</code>.</p>
<h2 id="why-we-re-better-off-with-pep668">Why We’re Better Off With PEP668</h2><p>Installing Python packages globally with <code>pip</code> has always been risky on Linux. It worked—until it didn’t. A single bad install could break tools like <code>apt</code>, disable automation scripts, or prevent Python from launching entirely. I&#39;ve been on the wrong side of this a few times, and it’s not a pleasant experience.</p>
<p>This change doesn’t remove functionality—it just puts a guardrail in place. If you know what you’re doing, you can still override it. But for most users, it helps avoid subtle, frustrating bugs that only show up when it’s too late to undo them easily.</p>
<h2 id="break-system-packages-flag">Break System Packages Flag</h2><p>What happens when we <code>--break-system-packages</code>?</p>
<pre><code class="language-bash">$ pip install &lt;your-package&gt; --break-system-packages</code></pre><p>This tells <code>pip</code>:</p>
<pre><code class="language-undefined">Yes, I know I’m about to mess with a system-managed Python install. Let me do it anyway.</code></pre><p>Not exactly safe—but it works if you know what you’re doing.</p>
<p><strong>Risks Include</strong></p>
<ul>
<li>You may overwrite system-critical packages like <code>urllib3</code>, <code>certifi</code>, or <code>requests</code>.</li>
<li>It can break utilities like <code>apt</code>, <code>dnf</code>, or even the system’s python3 command.</li>
<li>Uninstalling packages later may fail or remove components needed by your OS.</li>
<li>Updates from your system package manager could conflict with or undo your changes.</li>
</ul>
<h2 id="using-system-package-manager-to-install-python-packages">Using System Package Manager to Install Python Packages</h2><pre><code class="language-bash">$ sudo apt install python3-requests</code></pre><p>The downside of using <code>apt</code> for Python packages is that they are often several versions behind the official releases on PyPI. Some packages may not be available. Finally, dependency conflicts can arise when mixing <code>apt</code> and <code>pip</code> installations.</p>
<h2 id="final-thoughts">Final Thoughts</h2><p>PEP668 isn’t here to ruin your workflow—it’s here to protect your system and nudge you toward better habits. Yes, the error was annoying the first time, but honestly, it forces us all to improve our Python hygiene—and that’s not a bad thing.</p>

    ]]></content:encoded>
    <enclosure url="https://saadman.dev/assets/images/blog/pep668.jpg" length="0" type="image/jpeg" />
  </item>
  </channel>
</rss>