<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Vibe Coding Forem</title>
    <description>The most recent home feed on Vibe Coding Forem.</description>
    <link>https://vibe.forem.com</link>
    <atom:link rel="self" type="application/rss+xml" href="https://vibe.forem.com/feed"/>
    <language>en</language>
    <item>
      <title>Engineering the "App-Like" Experience: A Deep Dive into PWA Architecture</title>
      <dc:creator>Raziq Din</dc:creator>
      <pubDate>Sun, 24 May 2026 19:04:55 +0000</pubDate>
      <link>https://vibe.forem.com/raziq_din_bd0274cc2ac748b/engineering-the-app-like-experience-a-deep-dive-into-pwa-architecture-597j</link>
      <guid>https://vibe.forem.com/raziq_din_bd0274cc2ac748b/engineering-the-app-like-experience-a-deep-dive-into-pwa-architecture-597j</guid>
      <description>&lt;p&gt;In modern software engineering, the gap between web platforms and native mobile applications is bridged by Progressive Web Apps (PWAs). For a high-performance system or web applications, a PWA transformation isn't just about aesthetics, it's about technical resilience, cross-platform compatibility, and optimized resource management.&lt;br&gt;
 &lt;br&gt;
Below is the architectural breakdown of how the Web App Manifest and Service Worker work together to "upgrade" a standard FastAPI application.&lt;br&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  The Architecture: A Full-Stack Perspective
&lt;/h3&gt;

&lt;p&gt;To support PWA features, your project structure must treat static assets as "first-class citizens" that the browser can discover and cache. Example project structure can be referred below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myproject/
├── app/
│   ├── main.py             # FastAPI entry point &amp;amp; Uvicorn config
│   ├── templates/          # Jinja2 templates (HTML)
│   └── static/             # The PWA Asset Hub
│       ├── manifest.json   # Identity &amp;amp; Display metadata
│       └── js/
│           └── sw.js       # The Service Worker "Proxy" logic
├── Dockerfile              # Containerizing the environment
└── docker-compose.yml      # Port mapping (e.g., 8080:8000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Defining Identity with manifest.json
&lt;/h3&gt;

&lt;p&gt;The Web App Manifest is a JSON metadata file that allows your website to be "installed" on a device. It dictates the "standalone" behavior , removing the browser's address bar to provide a native look and feel.&lt;/p&gt;

&lt;p&gt;Below are the default configuration you can try and implement in your project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Titan Gym Booking System"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TitanGym"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"High-performance gym reservation and QR access system."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standalone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#1A1A1A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#CCFF00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/static/icons/icon-192.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192x192"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/static/icons/icon-512.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512x512"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: The Logic Layer with sw.js
&lt;/h3&gt;

&lt;p&gt;The Service Worker is a programmable network proxy. It runs in a background thread, separate from the main browser window, allowing it to intercept network requests and manage Offline Caching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// /static/js/sw.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CACHE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;titan-gym-v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STATIC_ASSETS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/static/css/style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/static/js/main.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/static/manifest.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// INSTALL: Pre-cache core assets for offline use&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CACHE_NAME&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STATIC_ASSETS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// FETCH: Intercept requests and serve from cache if network fails&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cachedResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cachedResponse&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Integration and Registration
&lt;/h3&gt;

&lt;p&gt;For the browser to activate these features, we must register the Service Worker in your main layout. This tells the browser: "This site is a PWA; start the background engine."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="c"&gt;&amp;lt;!-- In your base.html or home.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/static/manifest.json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/static/js/sw.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reg&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SW Registered!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SW Registration Failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion: Why Engineers Choose PWAs
&lt;/h3&gt;

&lt;p&gt;By leveraging this architecture within a Dockerized FastAPI environment, you achieve several engineering goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Network Independence&lt;/code&gt;: The Service Worker serves the "My Reservations" page even during campus Wi-Fi outages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Zero-Friction Updates&lt;/code&gt;: Unlike native apps, updating the "app" is as simple as deploying a new Docker image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Low Latency&lt;/code&gt;: Pre-caching static assets reduces the Time to Interactive (TTI), as the browser pulls files from local storage instead of making remote calls.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;This setup ensures that your web project isn't just a website, but a reliable tool that lives directly on the user's home screen.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>software</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>🔢 JS Array Playground</title>
      <dc:creator>Naimesh Rao</dc:creator>
      <pubDate>Sun, 24 May 2026 19:03:39 +0000</pubDate>
      <link>https://vibe.forem.com/naimeshrao/js-array-playground-5e8m</link>
      <guid>https://vibe.forem.com/naimeshrao/js-array-playground-5e8m</guid>
      <description>&lt;p&gt;👉 &lt;a href="https://codepen.io/naimeshrao/full/qEqmgKv" rel="noopener noreferrer"&gt;https://codepen.io/naimeshrao/full/qEqmgKv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learning array becomes way easier when you can actually play with them.&lt;/p&gt;

&lt;p&gt;Experiment with map(), filter(), reduce(), find(), and more in an interactive JavaScript learning lab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6tvb35xqk53zsmhom6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6tvb35xqk53zsmhom6a.png" alt=" " width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>codepen</category>
      <category>learning</category>
    </item>
    <item>
      <title>I built a local first AI CCTV assistant using Gemma 4 + Frigate</title>
      <dc:creator>Dhanush Reddy</dc:creator>
      <pubDate>Sun, 24 May 2026 18:48:26 +0000</pubDate>
      <link>https://vibe.forem.com/dhanushreddy29/i-built-a-local-first-ai-cctv-assistant-using-gemma-4-frigate-42m8</link>
      <guid>https://vibe.forem.com/dhanushreddy29/i-built-a-local-first-ai-cctv-assistant-using-gemma-4-frigate-42m8</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Build with Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I Built a local-first AI CCTV assistant Using Gemma 4, Frigate, and Home Assistant&lt;/p&gt;

&lt;p&gt;Most CCTV notifications are noisy and not actually useful.&lt;/p&gt;

&lt;p&gt;Typical camera alerts are mostly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Motion detected at Front Door&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That tells me &lt;em&gt;something&lt;/em&gt; happened, but not &lt;em&gt;what&lt;/em&gt; actually happened.&lt;/p&gt;

&lt;p&gt;Was it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a delivery person?&lt;/li&gt;
&lt;li&gt;someone waiting outside?&lt;/li&gt;
&lt;li&gt;a suspicious stranger?&lt;/li&gt;
&lt;li&gt;or just random movement?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted a system that could intelligently summarize what was happening on my cameras while keeping everything completely local and privacy-friendly.&lt;/p&gt;

&lt;p&gt;So I built a local AI-powered surveillance assistant using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://frigate.video/" rel="noopener noreferrer"&gt;Frigate&lt;/a&gt; for movement detection&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.home-assistant.io/" rel="noopener noreferrer"&gt;Home Assistant&lt;/a&gt; for automations&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ollama.com/library/gemma4:e2b" rel="noopener noreferrer"&gt;Gemma 4&lt;/a&gt; running locally through Ollama&lt;/li&gt;
&lt;li&gt;Mobile notifications for real-time summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now instead of generic motion alerts, I receive notifications like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“A person wearing a dark shirt approached the gate, waited briefly, and left.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All processed locally without sending any footage to the cloud.&lt;/p&gt;

&lt;p&gt;The system continuously monitors my WiFi connected CCTV cameras using Frigate. Frigate is an open-source NVR that performs real-time object detection on video feeds. You can learn more about &lt;a href="https://docs.frigate.video/" rel="noopener noreferrer"&gt;Frigate here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5l7w8e5nt6u5mtx9cqrt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5l7w8e5nt6u5mtx9cqrt.png" alt="How it works" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whenever Frigate detects a person:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Relevant frames are sent to a locally running Gemma 4 model&lt;/li&gt;
&lt;li&gt;Frigate creates an event with the video clip and the AI summary.&lt;/li&gt;
&lt;li&gt;Home Assistant automation triggers&lt;/li&gt;
&lt;li&gt;A rich notification is sent directly to my phone&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a much more useful home surveillance experience.&lt;/p&gt;

&lt;p&gt;Instead of manually checking every alert, I can instantly understand what happened from the notification itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajoo0ohybbvjzw3dttuh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajoo0ohybbvjzw3dttuh.png" alt="Demo notification" width="576" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Video demo will be added here soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;You can find the YAML configuration for Frigate and Home Assistant automations in this &lt;a href="https://gist.github.com/dhanushreddy291/79b1003475f2cc5337308b9b96665380" rel="noopener noreferrer"&gt;GitHub Gist&lt;/a&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Update prompt and other alert / review settings as per your requirements in the Frigate config&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you run into any issues while setting it up, feel free to comment below. Your WiFi cameras should provide an RTSP stream that can be integrated into the Frigate configuration. Just refer to your camera’s documentation for instructions on retrieving the stream URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Gemma 4
&lt;/h2&gt;

&lt;p&gt;I used the &lt;code&gt;gemma4:e2b&lt;/code&gt; model running locally through Ollama.&lt;/p&gt;

&lt;p&gt;The model runs inside a dedicated Proxmox container with GPU passthrough enabled for faster inference.&lt;/p&gt;

&lt;p&gt;I specifically chose E2B variant because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is lightweight enough for local deployment&lt;/li&gt;
&lt;li&gt;inference latency is low&lt;/li&gt;
&lt;li&gt;it works well for concise visual summarization tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without Gemma 4, the system would only know:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“A person was detected.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With Gemma 4, the system understands and communicates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what the person is doing&lt;/li&gt;
&lt;li&gt;where they moved&lt;/li&gt;
&lt;li&gt;how long they stayed&lt;/li&gt;
&lt;li&gt;and whether the event is important&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That dramatically improves the usefulness of camera notifications.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
      <category>automation</category>
    </item>
    <item>
      <title>Your PDF Parser Is Failing You — Here's How to Fix It With One API Call</title>
      <dc:creator>Savitar AI</dc:creator>
      <pubDate>Sun, 24 May 2026 18:44:31 +0000</pubDate>
      <link>https://vibe.forem.com/savitar_ai/how-to-extract-text-from-pdfs-using-python-api-complete-beginner-guide-29el</link>
      <guid>https://vibe.forem.com/savitar_ai/how-to-extract-text-from-pdfs-using-python-api-complete-beginner-guide-29el</guid>
      <description>&lt;p&gt;PDF documents are used everywhere — invoices, contracts, reports, receipts, scanned files, and forms. But manually extracting text from PDFs can be slow, repetitive, and difficult to automate.&lt;br&gt;
This is where AI-powered PDF extraction APIs help developers automate document workflows using simple REST APIs.&lt;br&gt;
In this beginner-friendly tutorial, we’ll learn how to extract text from PDFs using Python and the Enterprise PII Detection &amp;amp; Redaction API available on RapidAPI.&lt;/p&gt;

&lt;p&gt;You can also explore the live developer hub and workflow demo here:&lt;br&gt;
&lt;a href="https://savitar-dev-hub--savitar-dev-hub.us-east4.hosted.app" rel="noopener noreferrer"&gt;https://savitar-dev-hub--savitar-dev-hub.us-east4.hosted.app&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is PDF Text Extraction?
&lt;/h2&gt;

&lt;p&gt;PDF text extraction is the process of automatically reading and extracting text content from PDF documents.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Instead of manually copying data from files, developers can use APIs to:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;process PDFs automatically&lt;/li&gt;
&lt;li&gt;extract structured text&lt;/li&gt;
&lt;li&gt;automate document workflows&lt;/li&gt;
&lt;li&gt;build OCR pipelines&lt;/li&gt;
&lt;li&gt;analyze documents using AI&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This is especially useful for:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;SaaS applications&lt;/li&gt;
&lt;li&gt;finance automation&lt;/li&gt;
&lt;li&gt;legal document systems&lt;/li&gt;
&lt;li&gt;OCR workflows&lt;/li&gt;
&lt;li&gt;enterprise document processing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why Traditional PDF Parsing Fails
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Many PDFs are:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scanned images&lt;/li&gt;
&lt;li&gt;blurry documents&lt;/li&gt;
&lt;li&gt;photographed papers&lt;/li&gt;
&lt;li&gt;handwritten notes&lt;/li&gt;
&lt;li&gt;image-based files&lt;/li&gt;
&lt;li&gt;Traditional parsers struggle with these files.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;AI-powered OCR APIs solve this problem by combining:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;OCR (Optical Character Recognition)&lt;/li&gt;
&lt;li&gt;document AI&lt;/li&gt;
&lt;li&gt;structured extraction&lt;/li&gt;
&lt;li&gt;intelligent text recognition&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Before PDF Extraction
&lt;/h2&gt;

&lt;p&gt;The API accepts uploaded PDF files and processes them automatically. The screenshot below shows a PDF before extraction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10e2zbgq6rb3zjqa9xde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10e2zbgq6rb3zjqa9xde.png" alt=" " width="512" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before PDF extraction using AI-powered document extraction API.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  After PDF Extraction
&lt;/h2&gt;

&lt;p&gt;Once processed, the API extracts structured text from the PDF automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4afrcwn51xkjjoag83km.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4afrcwn51xkjjoag83km.png" alt=" " width="512" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After PDF extraction using AI-powered document extraction API.&lt;/p&gt;

&lt;p&gt;Live demo available on the Savitar Developer Hub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This extracted text can then be used for: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automation workflows&lt;/li&gt;
&lt;li&gt;AI pipelines&lt;/li&gt;
&lt;li&gt;analytics&lt;/li&gt;
&lt;li&gt;search indexing&lt;/li&gt;
&lt;li&gt;compliance systems&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Features of the PDF Extraction API
&lt;/h2&gt;

&lt;p&gt;The Enterprise PII Detection &amp;amp; Redaction API supports:&lt;br&gt;
✅ PDF text extraction&lt;br&gt;
 ✅ OCR for scanned documents&lt;br&gt;
 ✅ Structured JSON output&lt;br&gt;
 ✅ REST API integration&lt;br&gt;
 ✅ Batch document processing&lt;br&gt;
 ✅ AI-powered OCR workflows&lt;br&gt;
 ✅ Fast processing pipelines&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Supported formats:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;PDF&lt;/li&gt;
&lt;li&gt;DOCX&lt;/li&gt;
&lt;li&gt;PPTX&lt;/li&gt;
&lt;li&gt;XLSX&lt;/li&gt;
&lt;li&gt;PNG&lt;/li&gt;
&lt;li&gt;JPG&lt;/li&gt;
&lt;li&gt;TIFF&lt;/li&gt;
&lt;li&gt;WEBP&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step 1 — Install Python Requests
&lt;/h2&gt;

&lt;p&gt;First, install the requests library.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Python API Example
&lt;/h2&gt;

&lt;p&gt;The following Python script uploads a PDF file and extracts text automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://enterprise-pii-detection-redaction-api.p.rapidapi.com/extract&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enterprise-pii-detection-redaction-api.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sample.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace YOUR_API_KEY with your key from RapidAPI, and point sample.pdf at your document. That's the entire integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example API Response
&lt;/h2&gt;

&lt;p&gt;After processing the PDF, the API returns structured JSON output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Contractor Quotation Comparison &amp;amp; Inflation Analysis Report..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sample.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"file_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"page_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mistral-ocr-latest"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;response.json()["text"] gives you the full extracted content — ready to pipe into a database, a search index, an LLM, or any downstream system you're building.&lt;/p&gt;

&lt;p&gt;This makes it easy to integrate PDF extraction into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;web apps&lt;/li&gt;
&lt;li&gt;SaaS platforms&lt;/li&gt;
&lt;li&gt;automation workflows&lt;/li&gt;
&lt;li&gt;AI systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OCR Support for Scanned PDFs
&lt;/h2&gt;

&lt;p&gt;One of the biggest challenges in document processing is scanned PDFs.&lt;/p&gt;

&lt;p&gt;This API includes OCR support that can extract text from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scanned invoices&lt;/li&gt;
&lt;li&gt;handwritten notes&lt;/li&gt;
&lt;li&gt;photographed documents&lt;/li&gt;
&lt;li&gt;receipts&lt;/li&gt;
&lt;li&gt;screenshots&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OCR Input Example
&lt;/h2&gt;

&lt;p&gt;The API can process scanned or handwritten documents automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp8bre9hoqghclbgm2npo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp8bre9hoqghclbgm2npo.png" alt=" " width="512" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  OCR Output Example
&lt;/h2&gt;

&lt;p&gt;After OCR processing, the extracted text is returned in structured format.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fok5qugghkyjx2zvwzgxi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fok5qugghkyjx2zvwzgxi.png" alt=" " width="512" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OCR output generated from scanned handwritten documents.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This helps developers build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;intelligent document systems&lt;/li&gt;
&lt;li&gt;searchable archives&lt;/li&gt;
&lt;li&gt;AI document workflows&lt;/li&gt;
&lt;li&gt;automated business pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benefits of API-Based PDF Extraction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Using an  AI-powered PDF extraction  API helps developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;avoid building OCR systems from scratch&lt;/li&gt;
&lt;li&gt;scale document processing easily&lt;/li&gt;
&lt;li&gt;automate repetitive workflows&lt;/li&gt;
&lt;li&gt;improve accuracy&lt;/li&gt;
&lt;li&gt;save development time&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;PDF extraction APIs are widely used in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finance&lt;/li&gt;
&lt;li&gt;invoice automation&lt;/li&gt;
&lt;li&gt;receipt extraction&lt;/li&gt;
&lt;li&gt;accounting workflows&lt;/li&gt;
&lt;li&gt;HR&lt;/li&gt;
&lt;li&gt;resume parsing&lt;/li&gt;
&lt;li&gt;employee document processing&lt;/li&gt;
&lt;li&gt;LegalTech&lt;/li&gt;
&lt;li&gt;contract analysis&lt;/li&gt;
&lt;li&gt;legal document indexing&lt;/li&gt;
&lt;li&gt;Healthcare&lt;/li&gt;
&lt;li&gt;patient record digitization&lt;/li&gt;
&lt;li&gt;medical document OCR&lt;/li&gt;
&lt;li&gt;SaaS Platforms&lt;/li&gt;
&lt;li&gt;automation workflows&lt;/li&gt;
&lt;li&gt;AI document pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;AI-powered PDF extraction APIs are making document automation significantly easier for developers and businesses.&lt;br&gt;
Instead of manually copying text from PDFs or building complex OCR systems internally, developers can integrate document extraction directly into their applications using simple REST APIs.&lt;/p&gt;

&lt;p&gt;Whether you're building:&lt;/p&gt;

&lt;p&gt;OCR workflows,&lt;br&gt;
automation systems,&lt;br&gt;
AI applications,&lt;br&gt;
or enterprise document pipelines,&lt;br&gt;
PDF extraction APIs can dramatically improve efficiency and scalability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the API
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Looking for an AI-powered OCR and PDF extraction workflow?
&lt;/h4&gt;

&lt;p&gt;The Enterprise PII Detection &amp;amp; Redaction API helps developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extract text from PDFs&lt;/li&gt;
&lt;li&gt;process scanned documents&lt;/li&gt;
&lt;li&gt;automate OCR workflows&lt;/li&gt;
&lt;li&gt;build AI-powered document pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Explore the API on RapidAPI:
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://rapidapi.com/savitarai/api/enterprise-pii-detection-redaction-api" rel="noopener noreferrer"&gt;https://rapidapi.com/savitarai/api/enterprise-pii-detection-redaction-api&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Live Developer Hub:
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://savitar-dev-hub--savitar-dev-hub.us-east4.hosted.app" rel="noopener noreferrer"&gt;https://savitar-dev-hub--savitar-dev-hub.us-east4.hosted.app&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🔖 Tags: PDF extraction API · OCR API · Python · AI OCR · scanned PDF OCR · document extraction · REST API · image to text · PDF parser · document automation&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
      <category>rapidapi</category>
    </item>
    <item>
      <title>CrowdShield AI — Smart Stadium Operating System &amp; Crowd Intelligence Platform</title>
      <dc:creator>Abhisek Padhy</dc:creator>
      <pubDate>Sun, 24 May 2026 18:43:53 +0000</pubDate>
      <link>https://vibe.forem.com/abhisek_d36645346a8/crowdshield-ai-smart-stadium-operating-system-crowd-intelligence-platform-4m6e</link>
      <guid>https://vibe.forem.com/abhisek_d36645346a8/crowdshield-ai-smart-stadium-operating-system-crowd-intelligence-platform-4m6e</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;CrowdShield AI is a live platform built on the creative north star of delivering an automated "Autopilot for Stadium Operations".&lt;/p&gt;

&lt;p&gt;Borrowing heavily from high-density telemetry dashboards and digital twins, the interface is optimized for low-light command center environments. It processes dense real-time streams to track occupancy metrics, map threat matrices, and trigger automated emergency action sequences instantly when critical thresholds are breached.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Website:&lt;/strong&gt; 🔗 &lt;a href="https://crowdshield-361013050235.us-central1.run.app/" rel="noopener noreferrer"&gt;https://crowdshield-361013050235.us-central1.run.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; 🔗 &lt;a href="https://github.com/Abb2907/crowdshield" rel="noopener noreferrer"&gt;https://github.com/Abb2907/crowdshield&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The platform uses live telemetry, dynamic AI-driven spectator routing, automated emergency orchestration, and comprehensive analytics to mitigate bottlenecks, counter ticket fraud, and streamline stadium egress and ingress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Website:&lt;/strong&gt; 🔗 &lt;a href="https://crowdshield-361013050235.us-central1.run.app/" rel="noopener noreferrer"&gt;https://crowdshield-361013050235.us-central1.run.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; 🔗 &lt;a href="https://github.com/Abb2907/crowdshield" rel="noopener noreferrer"&gt;https://github.com/Abb2907/crowdshield&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ijxvypvrb5klp6e18w1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ijxvypvrb5klp6e18w1.png" alt=" " width="799" height="389"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqtel63gvsi3a6zw6v83.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqtel63gvsi3a6zw6v83.png" alt=" " width="800" height="394"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfb9rf6vcx7frc0prmld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfb9rf6vcx7frc0prmld.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12i948rav1472tc5e8q4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12i948rav1472tc5e8q4.png" alt=" " width="799" height="392"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh73cbbp8hl6dadwo40xj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh73cbbp8hl6dadwo40xj.png" alt=" " width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;The initial deployment pipeline struck a critical bottleneck. While the structural architecture for CrowdShield AI—the multi-workspace ecosystem containing the real-time React analytics client, the Node.js telemetry backend, and the Supabase database migrations—was completely mapped out, the production rollout stalled.&lt;/p&gt;

&lt;p&gt;The application was trapped in a container startup loop during the &lt;code&gt;gcloud run deploy&lt;/code&gt; phase. The root &lt;code&gt;package.json&lt;/code&gt; had its production start script bound to a local development hot-reloader (&lt;code&gt;npm run dev:backend&lt;/code&gt;), and the &lt;code&gt;Procfile&lt;/code&gt; was erroneously attempting to compile TypeScript source code (&lt;code&gt;npm run build --workspace=backend&lt;/code&gt;) at boot time within Cloud Run's resource-constrained, read-only runtime environment. This heavy mechanical overhead caused container execution to lag, missing the port binding health checks on port 8080 and resulting in an immediate deployment failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  What changed, fixed, and added to finish it up:
&lt;/h3&gt;

&lt;p&gt;To transition CrowdShield AI into a stable, operational "Autopilot for Stadium Operations," the compilation and runtime execution layers were completely decoupled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fixed the Production Runtime Pipeline:&lt;/strong&gt; Rewrote the root &lt;code&gt;package.json&lt;/code&gt; scripts to isolate the compilation phase. The production start command was stripped of development overhead and updated to directly execute the pre-compiled JavaScript bundle (&lt;code&gt;node backend/dist/index.js&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Port Binding:&lt;/strong&gt; Audited and validated the backend entry point configuration (&lt;code&gt;backend/src/index.ts&lt;/code&gt;) to ensure the application dynamically reads the environment’s target port (&lt;code&gt;process.env.PORT&lt;/code&gt;) and binds correctly to &lt;code&gt;0.0.0.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-Accelerated Core Features:&lt;/strong&gt; Leveraging GitHub Copilot as a velocity multiplier, the rest of the stadium orchestration loops were brought online. Repetitive infrastructure routing, relational telemetry table indexing, and testing boilerplate were rapidly scaffolded to ensure stable state changes—moving successfully from Green (Safe), to Amber (Warning), to Red (Critical/Incident State) as crowd density metrics fluctuate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;Using GitHub Copilot provided a critical operational velocity window, accelerating concurrent development across the frontend client, telemetry backend, and relational database layers of the CrowdShield AI architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Primary Optimization Vectors:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure Scaffolding:&lt;/strong&gt; Accelerated the deployment of structural boilerplate, database migrations, and schema definitions across workspaces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Boilerplate Generation:&lt;/strong&gt; Automated the generation of comprehensive unit and end-to-end telemetry validation suites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parser &amp;amp; Runtime Iteration:&lt;/strong&gt; Drastically reduced execution latencies when testing core event-parsing and automated operational loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alternative Implementations:&lt;/strong&gt; Enabled rapid-fire evaluation of competing algorithmic patterns and performance profiles in real time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation Pipelines:&lt;/strong&gt; Streamlined the synthesis of technical specifications, strategy documents, and architectural rulesets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring Friction Reduction:&lt;/strong&gt; Maintained system telemetry integrity by smoothing over data structure transformations during critical code cleanups.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Core Metric:&lt;/strong&gt; The primary return on investment was not the replacement of core architectural thinking, but the severe reduction of mechanical overhead surrounding low-level system experimentation.&lt;/p&gt;

&lt;p&gt;Because CrowdShield AI is built explicitly for deterministic stadium operations and specification-driven development, every automated code generation sequence was strictly validated against the project's rigid architectural invariants and safety parameters.&lt;/p&gt;

&lt;p&gt;In many ways, the CrowdShield ecosystem acts as a direct exploration of a foundational, meta-level thesis:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;What would an execution runtime look like if it were engineered from day zero to be natively interpreted, expanded, and sustained by AI agents?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That exact question is what continues to drive the roadmap and engineering velocity behind the entire CrowdShield AI ecosystem.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
    </item>
    <item>
      <title>I built a free AI observability tool, prove your AI is useful, not just running</title>
      <dc:creator>emmanuela Opurum </dc:creator>
      <pubDate>Sun, 24 May 2026 18:42:57 +0000</pubDate>
      <link>https://vibe.forem.com/emmao/i-built-a-free-ai-observability-tool-prove-your-ai-is-useful-not-just-running-470a</link>
      <guid>https://vibe.forem.com/emmao/i-built-a-free-ai-observability-tool-prove-your-ai-is-useful-not-just-running-470a</guid>
      <description>&lt;p&gt;Most AI monitoring tools tell you if your API is up.&lt;br&gt;
Mine tells you if it's worth running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it right now, no signup, 30 seconds
&lt;/h2&gt;

&lt;p&gt;Visit: &lt;a href="https://ai-pou-tracker.vercel.app" rel="noopener noreferrer"&gt;https://ai-pou-tracker.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Type any prompt in the Quick Test box on the left&lt;/li&gt;
&lt;li&gt;Hit ▶ Send Request&lt;/li&gt;
&lt;li&gt;Watch your request hit the live AI engine and appear on the dashboard instantly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or call the API directly:&lt;/p&gt;

&lt;p&gt;curl -X POST &lt;a href="https://ai-pou-tracker.vercel.app/api/request" rel="noopener noreferrer"&gt;https://ai-pou-tracker.vercel.app/api/request&lt;/a&gt; \&lt;br&gt;
  -H "Content-Type: application/json" \&lt;br&gt;
  -d '{"prompt": "does this AI actually work?"}'&lt;/p&gt;

&lt;h2&gt;
  
  
  What it tracks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Real AI success rate (not just 200 OK)&lt;/li&gt;
&lt;li&gt;Actual cost savings from semantic caching&lt;/li&gt;
&lt;li&gt;Latency patterns and fallback triggers&lt;/li&gt;
&lt;li&gt;A live Proof of Usefulness score that updates in real time&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;I was building AI features and had no way to prove they were&lt;br&gt;
actually useful, only that they were running. Every judge,&lt;br&gt;
investor, and team lead asks "is it working?" but nobody&lt;br&gt;
could define what "working" really means for AI.&lt;/p&gt;

&lt;p&gt;So I built a dashboard that answers that question with&lt;br&gt;
real numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 14 App Router&lt;/li&gt;
&lt;li&gt;Upstash Redis (persistent serverless storage)&lt;/li&gt;
&lt;li&gt;HuggingFace Inference API (fallback model)&lt;/li&gt;
&lt;li&gt;Recharts (live visualizations)&lt;/li&gt;
&lt;li&gt;Vercel edge deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Live stats so far
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;500+ real API requests tracked&lt;/li&gt;
&lt;li&gt;100% AI success rate&lt;/li&gt;
&lt;li&gt;Sub-500ms average latency across all routes&lt;/li&gt;
&lt;li&gt;6 production API routes all returning 200&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it and tell me what you think
&lt;/h2&gt;

&lt;p&gt;Live demo: &lt;a href="https://ai-pou-tracker.vercel.app" rel="noopener noreferrer"&gt;https://ai-pou-tracker.vercel.app&lt;/a&gt;&lt;br&gt;
⭐ GitHub: &lt;a href="https://github.com/Cloud-Architect-Emma/ai-pou-tracker" rel="noopener noreferrer"&gt;https://github.com/Cloud-Architect-Emma/ai-pou-tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What AI production metrics matter most to you?&lt;br&gt;
Drop them in the comments, I'm actively adding features.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>nextjs</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Beyond Autocomplete: Why Google Antigravity 2.0 Changes the Rules for Indie Builders</title>
      <dc:creator>Rohit</dc:creator>
      <pubDate>Sun, 24 May 2026 18:41:09 +0000</pubDate>
      <link>https://vibe.forem.com/sandman_sh/beyond-autocomplete-why-google-antigravity-20-changes-the-rules-for-indie-builders-1i4</link>
      <guid>https://vibe.forem.com/sandman_sh/beyond-autocomplete-why-google-antigravity-20-changes-the-rules-for-indie-builders-1i4</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-io-writing-2026-05-19"&gt;Google I/O Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The developer track at Google I/O 2026 made one thing undeniably clear: the era of the simple AI chat assistant is over. We have officially entered the &lt;strong&gt;Agentic Era&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;For independent developers, solo founders, and micro-SaaS builders who rely on high-velocity building—a development philosophy often called "vibe coding"—the headline launch of &lt;strong&gt;Google Antigravity 2.0&lt;/strong&gt; as a standalone desktop application represents a massive paradigm shift. It takes generative AI out of the isolated browser sidebar and morphs it into a fully contextualized, autonomous background engineering team. &lt;/p&gt;

&lt;p&gt;Instead of treating AI as a glorified autocomplete tool, Antigravity 2.0 treats AI as an infrastructure orchestrator. Here is a deep technical breakdown of how this platform works under the hood, why its structural architecture changes how we write software, and how solo builders can leverage it to scale their output exponentially.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Engine Layer: Why Gemini 3.5 Flash Changes the Economics of Agents
&lt;/h2&gt;

&lt;p&gt;Building autonomous coding loops has historically faced two major bottlenecks: &lt;strong&gt;latency&lt;/strong&gt; and &lt;strong&gt;cost&lt;/strong&gt;. When an AI agent needs to read a repository, analyze a bug, write a fix, run a compiler, read the terminal error, and attempt a second fix, it consumes an enormous amount of tokens across multiple sequential calls. If the model is slow or expensive, the entire workflow becomes impractical for daily development.&lt;/p&gt;

&lt;p&gt;Google bypassed this infrastructure bottleneck by co-optimizing Antigravity 2.0 around the newly released &lt;strong&gt;Gemini 3.5 Flash&lt;/strong&gt; model. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Throughput Metrics:&lt;/strong&gt; Clocking in at an incredible 289 output tokens per second, Gemini 3.5 Flash provides the rapid-fire inference required to sustain real-world agent loops without stalling your workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Preservation via Event Compaction:&lt;/strong&gt; Running long-horizon tasks usually risks exhausting context windows or spiking API costs. Antigravity 2.0 utilizes an engineering feature called &lt;em&gt;Event Compaction&lt;/em&gt;. Instead of blindly truncating your conversation history, the system dynamically compresses older context blocks, saving up to 38% on token overhead during long debugging sessions.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Multi-Agent Orchestration &amp;amp; Parallel Engineering Pipelines
&lt;/h2&gt;

&lt;p&gt;Traditional IDE extensions operate linearly: you prompt, you wait, you review a diff, and you click accept. If you need a backend database schema, an API route, and a matching frontend UI component, you generally have to hold the AI's hand through each step sequentially.&lt;/p&gt;

&lt;p&gt;Antigravity 2.0 completely rewrites this lifecycle by introducing &lt;strong&gt;Multi-Agent Workflows&lt;/strong&gt; and &lt;strong&gt;Dynamic Subagents&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8n6bkgglxtww6ciz5e75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8n6bkgglxtww6ciz5e75.png" alt="A conceptual diagram showing a main AI agent delegating tasks to three parallel subagents for UI, testing, and database work in a dark mode interface" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               [ Main Antigravity Agent ]
                           │
       ┌───────────────────┼───────────────────┐
       ▼                   ▼                   ▼
[Subagent A: UI]   [Subagent B: Test]   [Subagent C: DB]
(React/Tailwind)   (Vitest/Regression)  (Prisma/Migration)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you assign a macro-level objective to Antigravity, the primary agent evaluates the workspace and autonomously spawns specialized, sandboxed subagents to tackle distinct tasks in parallel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolated Execution Environments:&lt;/strong&gt; Subagents operate within persistent, secure remote Linux sandboxes. They can install dependencies, compile binaries, and execute code safely without clogging your local machine’s environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Solo Founder Advantage:&lt;/strong&gt; This architecture effectively transforms a single software engineer into a cross-functional development team. While your primary focus remains on high-level user experience, design feel, and core business logic, one background subagent can be actively writing edge-case regression tests, while another maps out a database migration pipeline.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Native Intent Control: Slash Commands for Real World Workflows
&lt;/h2&gt;

&lt;p&gt;One of the greatest friction points in AI development is maintaining alignment—ensuring the model doesn't confidently refactor a critical piece of codebase into oblivion. Antigravity 2.0 handles this through explicit, engineering-focused intent controls built directly into the command interface:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frghmp07ryku9gx24gnzy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frghmp07ryku9gx24gnzy.jpg" alt="A close-up of a dark mode futuristic code editor interface showing the use of AI slash commands like /grill-me" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/goal [task]&lt;/code&gt;&lt;/strong&gt;: This initiates an asynchronous, long-horizon loop. It instructs the agent to run an entire multi-step task to absolute completion in the background, signaling you only when the objective is achieved or if it encounters a fatal blocker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/grill-me&lt;/code&gt;&lt;/strong&gt;: To combat hallucinations and misaligned logic, this command forces the agent to pause. It requires the AI to actively interview &lt;em&gt;you&lt;/em&gt;, asking sharp architectural questions to clarify edge cases before it touches a single line of production code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/browser&lt;/code&gt;&lt;/strong&gt;: This grants the agent autonomous web-browsing permissions. If a subagent encounters an undocumented breaking change in a third-party framework library, it can independently scour updated web documentation, extract the correct syntax, and patch the codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Furthermore, context is no longer isolated to a single file or a lone directory. Antigravity 2.0 handles multi-repository "Projects," allowing background agents to retain state, track global variables, and safely manage workspace directory permissions across complex, full-stack micro-SaaS setups.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Strategic Takeaway for Micro-SaaS Founders
&lt;/h2&gt;

&lt;p&gt;For independent builders looking to launch lean, low-overhead digital products, the structural shifts unveiled at Google I/O 2026 alter the competitive landscape. With the introduction of the accessible $100 Antigravity tier and native integrations with the &lt;strong&gt;Firebase Agent Skills bundle&lt;/strong&gt;, managing underlying backend infrastructure is becoming fully automated.&lt;/p&gt;

&lt;p&gt;The competitive advantage in software development is rapidly shifting. It is no longer about who can write boilerplate code or configure server routing the fastest; it is about who can best orchestrate autonomous AI pipelines to solve hyper-niche, real-world problems. &lt;/p&gt;

&lt;p&gt;Antigravity 2.0 proves that the future of engineering isn't about writing code line-by-line—it's about directing a highly specialized, agentic system to build your vision at scale.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What are your thoughts on the Antigravity 2.0 standalone application? Are you planning to migrate your development stack to an agent-first environment, or do you prefer traditional IDE plugins? Let's discuss in the comments below!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleiochallenge</category>
    </item>
    <item>
      <title>터미널 AI 에이전트 구축 (v12)</title>
      <dc:creator>matias yoon</dc:creator>
      <pubDate>Sun, 24 May 2026 18:40:28 +0000</pubDate>
      <link>https://vibe.forem.com/matias_yoon_738a24cb1190f/teomineol-ai-eijeonteu-gucug-v12-42c9</link>
      <guid>https://vibe.forem.com/matias_yoon_738a24cb1190f/teomineol-ai-eijeonteu-gucug-v12-42c9</guid>
      <description>&lt;h1&gt;
  
  
  터미널 AI 에이전트 구축 (v12)
&lt;/h1&gt;

&lt;p&gt;터미널에서 직접 작동하는 AI 에이전트를 구축하여 개발 워크플로우를 최적화하세요. 이 가이드는 개발자들이 직접 구축하고 커스터마이징할 수 있는 실질적인 터미널 AI 에이전트를 제공합니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. CLI AI 에이전트 생태계
&lt;/h2&gt;

&lt;p&gt;현재 CLI AI 에이전트 생태계는 다음과 같은 주요 도구들로 구성되어 있습니다:&lt;/p&gt;

&lt;h3&gt;
  
  
  Aider
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 설치&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;aider

&lt;span class="c"&gt;# 기본 사용&lt;/span&gt;
aider &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Continue.dev
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# VS Code 확장으로 설치&lt;/span&gt;
&lt;span class="c"&gt;# https://marketplace.visualstudio.com/items?itemName=Continue.continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  OpenCode
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# GitHub 저장소에서 직접 설치&lt;/span&gt;
git clone https://github.com/open-code/open-code.git
&lt;span class="nb"&gt;cd &lt;/span&gt;open-code
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  커스텀 스크립트
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 간단한 래퍼 스크립트 예제&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# aider-wrapper.sh&lt;/span&gt;
aider &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--model&lt;/span&gt; gpt-4-turbo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. 로컬 LLM API 엔드포인트 설정
&lt;/h2&gt;

&lt;p&gt;로컬 LLM을 사용하여 비용을 절감하고 성능을 향상시키세요:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ollama 설치
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ubuntu/Debian&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;ollama

&lt;span class="c"&gt;# 시작&lt;/span&gt;
ollama serve

&lt;span class="c"&gt;# 모델 다운로드&lt;/span&gt;
ollama pull llama3
ollama pull codellama:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  API 엔드포인트 생성
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# llm_api.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/chat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;llama3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;11434&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. 함수 호출 기능이 있는 Python CLI 에이전트
&lt;/h2&gt;

&lt;p&gt;다음은 간단한 Python CLI 에이전트 예제입니다:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
# smart_agent.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TerminalAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;명령어 실행&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimeoutExpired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Command timed out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;함수 호출&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;function_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;function_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list_files&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ls -la&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;function_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown function: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;사용자 요청 처리&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;system_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        You are a terminal assistant. You can execute shell commands and 
        return their output. You have access to these functions:
        - run_command(command): execute shell command
        - list_files(): list all files in current directory
        - git_status(): show git status
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Execute shell command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parameters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                            &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list_files&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;List all files in directory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parameters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Show git status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parameters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;function_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# 함수 호출 처리
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;finish_reason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;function_call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;function_call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_call&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;function_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Command result: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Usage: python smart_agent.py &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;your prompt here&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TerminalAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-api-key-here&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. tmux와 통합
&lt;/h2&gt;

&lt;p&gt;터미널 멀티플렉서와의 통합을 통해 더 효율적인 워크플로우 구축:&lt;/p&gt;

&lt;h3&gt;
  
  
  tmux 스크립트 생성
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# tmux_agent.sh&lt;/span&gt;
&lt;span class="c"&gt;# 새로운 세션 생성&lt;/span&gt;
tmux new-session &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; agent-session

&lt;span class="c"&gt;# 창 분할&lt;/span&gt;
tmux split-window &lt;span class="nt"&gt;-h&lt;/span&gt;
tmux split-window &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;span class="c"&gt;# 첫 번째 창에 에이전트 실행&lt;/span&gt;
tmux send-keys &lt;span class="nt"&gt;-t&lt;/span&gt; agent-session:0.0 &lt;span class="s2"&gt;"python smart_agent.py"&lt;/span&gt; Enter

&lt;span class="c"&gt;# 두 번째 창에 코드 편집기 열기&lt;/span&gt;
tmux send-keys &lt;span class="nt"&gt;-t&lt;/span&gt; agent-session:0.0 &lt;span class="s2"&gt;"vim"&lt;/span&gt; Enter

&lt;span class="c"&gt;# 세션 연결&lt;/span&gt;
tmux attach &lt;span class="nt"&gt;-t&lt;/span&gt; agent-session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  tmux와 에이전트 통신
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tmux_integration.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TmuxAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent-session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session_name&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_to_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;특정 창에 명령어 전송&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tmux&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;send-keys&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-t&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;window_index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;새 창 생성&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tmux&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new-window&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-t&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_name&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_window_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;창의 출력 얻기&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tmux&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;capture-pane&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-t&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;window_index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;

&lt;span class="c1"&gt;# 사용 예제
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TmuxAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_to_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ls -la&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. 맞춤형 도구 개발
&lt;/h2&gt;

&lt;h3&gt;
  
  
  코드 검색 도구
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
python
# code_search.py
import os
import re
from typing import List, Dict

class CodeSearcher:
    def __init__(self, root_dir: str = "."):
        self.root_dir = root_dir

    def find_files(self, pattern: str, file_types: List[str] = None) -&amp;gt; List[str]:
        """파일 찾기"""
        matches = []
        for root, dirs, files in os.walk(self.root_dir):
            for file in files:
                if file_types and not any(file.endswith(ft) for ft in file_types):
                    continue
                if re.search(pattern, file):
                    matches.append(os.path.join(root, file))
        return matches

    def search_in_files(self, pattern: str, file_extensions: List[str] = None) -&amp;gt; Dict[str, List[str]]:
        """파일 내에서 패턴 검색"""
        results = {}
        search_files = self.find_files(r".*", file_extensions)

        for file_path in search_files:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    matches = re.finditer(pattern, content)
                    line_numbers = [m.start() for m in matches]

                    if line_numbers:
                        results[file_path] = line_numbers
            except Exception:
                continue
        return results

# 사용 예제
searcher =

---

📥 **Get the full guide on Gumroad**: https://gumroad.com/l/auto ($5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>developers</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building Instagram-Powered Apps with HikerAPI (Without Fighting Scrapers)</title>
      <dc:creator>baroque Ai</dc:creator>
      <pubDate>Sun, 24 May 2026 18:40:26 +0000</pubDate>
      <link>https://vibe.forem.com/baroque_ai_383066a9394165/building-instagram-powered-apps-with-hikerapi-without-fighting-scrapers-3nh9</link>
      <guid>https://vibe.forem.com/baroque_ai_383066a9394165/building-instagram-powered-apps-with-hikerapi-without-fighting-scrapers-3nh9</guid>
      <description>&lt;p&gt;I recently worked on a project where I needed reliable Instagram data inside a backend workflow.&lt;/p&gt;

&lt;p&gt;At first, I tried the usual approaches:&lt;/p&gt;

&lt;p&gt;Direct scraping&lt;br&gt;
Browser automation&lt;br&gt;
instagrapi&lt;br&gt;
Rotating proxies&lt;/p&gt;

&lt;p&gt;It worked… until it didn’t.&lt;/p&gt;

&lt;p&gt;Instagram constantly changes things. Sessions expire, accounts get flagged, rate limits appear randomly, and maintenance becomes a bigger problem than the actual product you're building.&lt;/p&gt;

&lt;p&gt;That’s when I started using HikerAPI — a REST Instagram API with simple authentication using an x-access-key header.&lt;/p&gt;

&lt;p&gt;The pricing starts from $0.001/request and includes 100 free requests, which made it easy to test before integrating it into production.&lt;/p&gt;

&lt;p&gt;The Problem with Traditional Instagram Scraping&lt;/p&gt;

&lt;p&gt;If you've built anything around Instagram data before, you probably know the pain points:&lt;/p&gt;

&lt;p&gt;Login challenges&lt;br&gt;
Session invalidation&lt;br&gt;
Proxy management&lt;br&gt;
CAPTCHA issues&lt;br&gt;
Random breaking changes&lt;br&gt;
Rate limiting&lt;br&gt;
Infrastructure overhead&lt;/p&gt;

&lt;p&gt;Libraries like instagrapi are useful, especially for prototypes and personal automation, but they still depend on reverse-engineered private APIs.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;p&gt;Your app can break unexpectedly&lt;br&gt;
Reliability becomes your responsibility&lt;br&gt;
Scaling gets harder over time&lt;/p&gt;

&lt;p&gt;For hobby projects, that might be acceptable.&lt;/p&gt;

&lt;p&gt;For client work or production systems, it becomes risky.&lt;/p&gt;

&lt;p&gt;Why I Tried HikerAPI&lt;/p&gt;

&lt;p&gt;I wanted something simpler:&lt;/p&gt;

&lt;p&gt;REST endpoints&lt;br&gt;
No browser automation&lt;br&gt;
No managing Instagram accounts&lt;br&gt;
No proxy rotation&lt;br&gt;
Easy backend integration&lt;/p&gt;

&lt;p&gt;The biggest advantage for me was speed of integration.&lt;/p&gt;

&lt;p&gt;Instead of spending time debugging scraping logic, I could focus on building features.&lt;/p&gt;

&lt;p&gt;Quick Start&lt;/p&gt;

&lt;p&gt;The API authentication is extremely straightforward.&lt;/p&gt;

&lt;p&gt;Here’s the exact Python example I used to test it:&lt;/p&gt;

&lt;p&gt;import requests&lt;/p&gt;

&lt;p&gt;headers = {"x-access-key": "YOUR_KEY"}&lt;/p&gt;

&lt;p&gt;r = requests.get(&lt;br&gt;
    "&lt;a href="https://api.hikerapi.com/v2/user/by/username?username=instagram" rel="noopener noreferrer"&gt;https://api.hikerapi.com/v2/user/by/username?username=instagram&lt;/a&gt;",&lt;br&gt;
    headers=headers&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;print(r.json())&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;No cookies.&lt;br&gt;
No login sessions.&lt;br&gt;
No Selenium.&lt;/p&gt;

&lt;p&gt;Real Use Case: Instagram Lead Discovery&lt;/p&gt;

&lt;p&gt;One real use case I explored was finding niche creators and business accounts for .&lt;/p&gt;

&lt;p&gt;The workflow looked something like this:&lt;/p&gt;

&lt;p&gt;Search accounts by username or keyword&lt;br&gt;
Pull profile metadata&lt;br&gt;
Store results in a database&lt;br&gt;
Run filtering logic&lt;br&gt;
Display curated profiles in a dashboard&lt;/p&gt;

&lt;p&gt;A simplified version:&lt;/p&gt;

&lt;p&gt;import requests&lt;/p&gt;

&lt;p&gt;API_KEY = "YOUR_KEY"&lt;/p&gt;

&lt;p&gt;headers = {&lt;br&gt;
    "x-access-key": API_KEY&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;usernames = [&lt;br&gt;
    "instagram",&lt;br&gt;
    "nike",&lt;br&gt;
    "natgeo"&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;for username in usernames:&lt;br&gt;
    response = requests.get(&lt;br&gt;
        f"&lt;a href="https://api.hikerapi.com/v2/user/by/username?username=%7Busername%7D" rel="noopener noreferrer"&gt;https://api.hikerapi.com/v2/user/by/username?username={username}&lt;/a&gt;",&lt;br&gt;
        headers=headers&lt;br&gt;
    )&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data = response.json()

print({
    "username": data.get("username"),
    "followers": data.get("follower_count"),
    "verified": data.get("is_verified")
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This made it easy to plug Instagram data into an existing backend pipeline.&lt;/p&gt;

&lt;p&gt;Comparing HikerAPI vs instagrapi&lt;/p&gt;

&lt;p&gt;Here’s the honest tradeoff.&lt;/p&gt;

&lt;p&gt;HikerAPI Advantages&lt;br&gt;
Much faster setup&lt;br&gt;
Cleaner REST architecture&lt;br&gt;
No session management&lt;br&gt;
No proxy maintenance&lt;br&gt;
Easier scaling&lt;br&gt;
Better for backend products and SaaS apps&lt;br&gt;
instagrapi Advantages&lt;br&gt;
More control&lt;br&gt;
Potentially cheaper at scale&lt;br&gt;
Good for experiments&lt;br&gt;
Works well for personal tooling&lt;br&gt;
The Tradeoff&lt;/p&gt;

&lt;p&gt;With HikerAPI, you’re paying for convenience and reliability.&lt;/p&gt;

&lt;p&gt;With scraping libraries, you save money but spend more engineering time maintaining infrastructure.&lt;/p&gt;

&lt;p&gt;For me, the decision depended on the project.&lt;/p&gt;

&lt;p&gt;If I’m building:&lt;/p&gt;

&lt;p&gt;an MVP,&lt;br&gt;
a client project,&lt;br&gt;
a production workflow,&lt;br&gt;
or something time-sensitive,&lt;/p&gt;

&lt;p&gt;…I’d rather use a managed API than babysit scrapers.&lt;/p&gt;

&lt;p&gt;Performance Notes&lt;/p&gt;

&lt;p&gt;The response times were fast enough for typical backend usage in my project.&lt;/p&gt;

&lt;p&gt;I also liked that I could integrate it directly into:&lt;/p&gt;

&lt;p&gt;Flask APIs&lt;br&gt;
FastAPI services&lt;br&gt;
Node.js backends&lt;br&gt;
scheduled jobs&lt;br&gt;
data pipelines&lt;/p&gt;

&lt;p&gt;without needing a separate scraping server.&lt;/p&gt;

&lt;p&gt;Things to Keep in Mind&lt;/p&gt;

&lt;p&gt;A few honest considerations:&lt;/p&gt;

&lt;p&gt;You still need to handle rate limits properly&lt;br&gt;
External APIs introduce dependency risk&lt;br&gt;
Costs can add up at very large scale&lt;br&gt;
You’re relying on a third-party service&lt;/p&gt;

&lt;p&gt;That said, the engineering time saved was worth it for my use case.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;If you're experimenting with Instagram automation, analytics, creator tools, or lead generation, there are basically two paths:&lt;/p&gt;

&lt;p&gt;Option 1: Build and maintain scrapers yourself&lt;/p&gt;

&lt;p&gt;More control, more maintenance.&lt;/p&gt;

&lt;p&gt;Option 2: Use a managed API&lt;/p&gt;

&lt;p&gt;Less infrastructure pain, faster development.&lt;/p&gt;

&lt;p&gt;For my project, HikerAPI helped me move faster and spend more time building actual product features instead of debugging Instagram internals.&lt;/p&gt;

&lt;p&gt;If you've been fighting with proxies, session cookies, or broken scraping scripts lately, it's worth trying the free requests just to compare the experience yourself.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>api</category>
      <category>react</category>
    </item>
    <item>
      <title>Checkpoints, Not Transcripts: Rethinking AI Coding Agent Memory</title>
      <dc:creator>lweiss01</dc:creator>
      <pubDate>Sun, 24 May 2026 18:40:21 +0000</pubDate>
      <link>https://vibe.forem.com/lweiss01/checkpoints-not-transcripts-rethinking-ai-coding-agent-memory-21pj</link>
      <guid>https://vibe.forem.com/lweiss01/checkpoints-not-transcripts-rethinking-ai-coding-agent-memory-21pj</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; AI coding agent memory should live in the repository, not the chat window. Bigger context windows and vector databases are solving the wrong problem. Here is the case for treating the repo itself as the durable cognitive surface.&lt;/p&gt;




&lt;p&gt;Everyone is trying to solve AI agent memory right now.&lt;/p&gt;

&lt;p&gt;Longer context windows.&lt;br&gt;
Vector databases.&lt;br&gt;
Conversation replay.&lt;br&gt;
Semantic retrieval.&lt;br&gt;
Infinite transcripts.&lt;/p&gt;

&lt;p&gt;But after spending months building workflows across Claude, Codex, Gemini, Cursor, and other coding agents, I've started to think we may be treating the wrong thing as the source of truth.&lt;/p&gt;

&lt;p&gt;The problem is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do we make the model remember everything forever?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How does a software project remain cognitively coherent across sessions, compaction, agent switches, and time?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are very different problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Context Window Is Not Durable Infrastructure
&lt;/h2&gt;

&lt;p&gt;Modern AI coding workflows are surprisingly fragile.&lt;/p&gt;

&lt;p&gt;An agent works for hours. The context window fills up. Compaction happens. Then suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;architectural reasoning disappears&lt;/li&gt;
&lt;li&gt;unresolved work gets forgotten&lt;/li&gt;
&lt;li&gt;regressions come back&lt;/li&gt;
&lt;li&gt;agents undo each other&lt;/li&gt;
&lt;li&gt;humans re-explain the same context repeatedly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The industry response so far has mostly been: store more. Bigger context windows, vector databases, hosted memory services, semantic retrieval over giant transcripts.&lt;/p&gt;

&lt;p&gt;But transcripts are not understanding.&lt;/p&gt;

&lt;p&gt;And replaying giant chat histories is not the same thing as preserving operational continuity.&lt;/p&gt;

&lt;p&gt;In practice, most coding workflows do not fail because information disappeared entirely. They fail because the important state was never extracted from the conversation in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkpoints, Not Transcripts
&lt;/h2&gt;

&lt;p&gt;The idea I have been exploring is pretty simple:&lt;/p&gt;

&lt;p&gt;Instead of preserving entire conversations forever, preserve structured checkpoints at meaningful moments.&lt;/p&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every token&lt;/li&gt;
&lt;li&gt;every thought&lt;/li&gt;
&lt;li&gt;every conversational detour&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the things that actually matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;current state&lt;/li&gt;
&lt;li&gt;architectural decisions&lt;/li&gt;
&lt;li&gt;unresolved threads&lt;/li&gt;
&lt;li&gt;regression risks&lt;/li&gt;
&lt;li&gt;next recommended actions&lt;/li&gt;
&lt;li&gt;implementation reasoning&lt;/li&gt;
&lt;li&gt;handoff context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The checkpoint becomes the durable source of truth.&lt;/p&gt;

&lt;p&gt;The live context window becomes disposable working memory.&lt;/p&gt;

&lt;p&gt;That distinction changes a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Repo Should Remember
&lt;/h2&gt;

&lt;p&gt;One realization that kept hitting me while working across multiple coding agents:&lt;/p&gt;

&lt;p&gt;The repository itself is the only thing that actually persists.&lt;/p&gt;

&lt;p&gt;Agents change.&lt;br&gt;
Models change.&lt;br&gt;
Sessions end.&lt;br&gt;
Windows compact.&lt;/p&gt;

&lt;p&gt;But the repo stays.&lt;/p&gt;

&lt;p&gt;So instead of treating continuity as something trapped inside a chat session, I started treating continuity as a repo-native concern.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;continuity artifacts live in the repo&lt;/li&gt;
&lt;li&gt;handoffs live in the repo&lt;/li&gt;
&lt;li&gt;operational state lives in the repo&lt;/li&gt;
&lt;li&gt;regression memory lives in the repo&lt;/li&gt;
&lt;li&gt;checkpoints live in the repo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repo remembers, not the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Agent Development Is Already Here
&lt;/h2&gt;

&lt;p&gt;A lot of tooling still assumes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;one human, one agent, one session.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is not how many people are actually working anymore.&lt;/p&gt;

&lt;p&gt;Real workflows increasingly look like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude for architecture&lt;/li&gt;
&lt;li&gt;Codex for implementation&lt;/li&gt;
&lt;li&gt;Cursor for iteration&lt;/li&gt;
&lt;li&gt;Gemini for exploration&lt;/li&gt;
&lt;li&gt;a human reviewing all of it&lt;/li&gt;
&lt;li&gt;another session tomorrow continuing the work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Continuity is no longer just memory. It is coordination across interchangeable execution surfaces. And once you frame it that way, the chat window stops looking like the right place to store anything important.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Agents Are Temporary. Repositories Persist.
&lt;/h2&gt;

&lt;p&gt;I think we are entering a phase where software repositories themselves become cognitive systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accumulating decisions&lt;/li&gt;
&lt;li&gt;preserving continuity&lt;/li&gt;
&lt;li&gt;coordinating work&lt;/li&gt;
&lt;li&gt;surviving agent turnover&lt;/li&gt;
&lt;li&gt;carrying operational memory forward over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not because the models became infinitely smart.&lt;/p&gt;

&lt;p&gt;But because the continuity stopped depending entirely on the model session.&lt;/p&gt;

&lt;p&gt;That is the direction I have been exploring with Holistic, an open-source CLI for repo-native continuity across agents: &lt;a href="https://github.com/lweiss01/holistic" rel="noopener noreferrer"&gt;https://github.com/lweiss01/holistic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still early. Still evolving quickly. If you are working across multiple coding agents and running into the continuity problem, I would genuinely love feedback, critiques, or just a conversation about how you are solving it.&lt;/p&gt;

&lt;p&gt;The repo remembers, not the window.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>From Side Project to Student Savior: My AI PPT &amp; Resume Tool Crossed 1.5K+ Users</title>
      <dc:creator>Chandrakant Kelgire</dc:creator>
      <pubDate>Sun, 24 May 2026 18:34:41 +0000</pubDate>
      <link>https://vibe.forem.com/chandrakant_kelgire_79721/from-side-project-to-student-savior-my-ai-ppt-resume-tool-crossed-15k-users-2c1o</link>
      <guid>https://vibe.forem.com/chandrakant_kelgire_79721/from-side-project-to-student-savior-my-ai-ppt-resume-tool-crossed-15k-users-2c1o</guid>
      <description>&lt;p&gt;Hey Dev Community!A few weeks ago I quietly dropped a small project — an AI-powered tool for students. Today, I’m back with an update that genuinely surprised me.pptmaker.co.in has now helped 10,000+ students create presentations and resumes in record time.What Changed Since the First Post?Added PDF to PPT conversion (this feature exploded in usage)&lt;br&gt;
Improved Gemini prompts → much better content quality and structure&lt;br&gt;
Launched Company Vault — pre-loaded ATS keywords for TCS, Infosys, Wipro, Accenture, etc.&lt;br&gt;
Added multiple modern themes (including Glassmorphism)&lt;br&gt;
Better mobile experience&lt;/p&gt;

&lt;p&gt;The Most Heartwarming FeedbackStudents are using it at 2 AM before seminars.&lt;br&gt;
One guy told me he submitted his final-year project presentation 10 minutes before the deadline — fully AI-generated and edited.Another fresher landed his first interview because the resume looked professional and passed ATS easily.Technical Lessons I LearnedGemini is incredibly fast but you have to be very specific with system prompts.&lt;br&gt;
Vercel + React is perfect for quick MVPs, but I’m now thinking about caching and rate limiting.&lt;br&gt;
Indian students don’t want “beautiful” tools — they want fast + practical + free.&lt;/p&gt;

&lt;p&gt;What’s Next? (Roadmap)Multi-language support (Hindi + English)&lt;br&gt;
Presentation coach (AI feedback on content &amp;amp; delivery)&lt;br&gt;
Batch PDF to PPT (upload multiple files)&lt;br&gt;
Export to Google Slides directly&lt;/p&gt;

&lt;p&gt;If you’re a student, developer, or someone who hates making slides — I’d love you to try it and roast it honestly. &lt;a href="https://www.pptmaker.co.in" rel="noopener noreferrer"&gt;https://www.pptmaker.co.in&lt;/a&gt; What should I build next? Drop your suggestions below #webdev #AI #students #indiehacker #buildinpublic #GeminiAI &lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why Story Points Don’t Work in the AI Era, And What Should Take Their Place Instead.</title>
      <dc:creator>Sriramprabhu Rajendran</dc:creator>
      <pubDate>Sun, 24 May 2026 18:34:17 +0000</pubDate>
      <link>https://vibe.forem.com/rsri/why-story-points-dont-work-in-the-ai-era-and-what-should-take-their-place-instead-5a27</link>
      <guid>https://vibe.forem.com/rsri/why-story-points-dont-work-in-the-ai-era-and-what-should-take-their-place-instead-5a27</guid>
      <description>&lt;p&gt;Imagine this scenario at your next sprint review meeting: You're looking good on your velocity graph. But half your team is struggling in their own little hell. Estimations have devolved into Russian roulette. You get a "2-point" done in 45 minutes. You have another "2-point" that takes 4 days because AI-written code introduced a nasty bug in staging that was missed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzps4ljqhrhordoocbe7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzps4ljqhrhordoocbe7.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This isn't an effort issue; it’s a problem of predictability. Story points, meant for predicting the performance of stable humans, don’t even know what to do with this situation.&lt;/p&gt;

&lt;h2&gt;
  
  
  I believe it’s time to ditch story points. Let me explain how.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Why I Think the Old System Is Defective
&lt;/h2&gt;

&lt;p&gt;"Story points are a commitment to the idea 'that task is about twice as hard as this task.'" These assumptions include the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The developer has approximately the same capacity&lt;/li&gt;
&lt;li&gt;The level of complexity correlates well to duration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AI breaks both assumptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throughput is no longer fixed.&lt;/strong&gt; The same developer, same work, same day—make one tweak to the model and next thing you know they’re getting things done in 40% less time. Or something goes wrong with the model and it takes them two days to fix an hallucinated abstraction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complexity not linked to task duration.&lt;/strong&gt; A very difficult task could become very easy if the AI does it right. But a very simple task could become very difficult if the AI gets it wrong. The variance is much larger than the mean—and story points measure only the mean.&lt;/p&gt;

&lt;p&gt;Here’s what I believe a normal team would be seeing after a couple of sprints using AI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Task Type         | Pre-AI Avg  | Post-AI Range
------------------|-------------|----------------
API endpoint      | 2 days      | 3 hours – 2.5 days
DB migration      | 3 days      | 1 day – 4 days  
UI component      | 1.5 days    | 30 min – 3 days
Legacy refactor   | 5 days      | 2 days – 8 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the range exceeds the estimation, then the estimation is noise.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hidden Cost That No One Is Estimating
&lt;/h2&gt;

&lt;p&gt;This is my hypothesis on what most teams are failing to estimate: &lt;strong&gt;verifying the AI output is the highest-cost activity for almost any task.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A real-time estimate of costs involved would be something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20% of the time: figuring out what to build&lt;/li&gt;
&lt;li&gt;15% of the time: the AI building it&lt;/li&gt;
&lt;li&gt;65% of the time: going through it, looking for problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that third one—the &lt;strong&gt;curation tax&lt;/strong&gt;—falls into an unseen category for everyone. They're estimating the time spent on construction and not on curation. That would be like planning a home renovation based only on how fast someone bangs a hammer.&lt;/p&gt;

&lt;p&gt;If teams started taking review-and-validation seriously as part of their estimate process, I'm convinced their accuracy will improve drastically.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Suggest to Replace Story Points
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Confidence-Tagged Estimates
&lt;/h3&gt;

&lt;p&gt;What I suggest: Every ticket must be provided with two things—a &lt;strong&gt;time estimate&lt;/strong&gt; and a &lt;strong&gt;confidence tag&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ticket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;INDE-002 — Migrate auth service to new SDK&lt;/span&gt;
&lt;span class="na"&gt;estimate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.5 days&lt;/span&gt;
&lt;span class="na"&gt;confidence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;low&lt;/span&gt;
&lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;New&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SDK,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;AI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hasn't&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;seen&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;our&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;auth&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;patterns&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;before"&lt;/span&gt;
&lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spike first (0.5 day), then re-estimate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The confidence tag is the important part. It tells the PM whether to trust the number or treat it as a hypothesis.&lt;/p&gt;

&lt;p&gt;Three bands:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;th&gt;How to plan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;high&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Done this before with AI, know the variance&lt;/td&gt;
&lt;td&gt;Plan on the estimate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;medium&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Familiar territory, some unknowns&lt;/td&gt;
&lt;td&gt;Buffer 2x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;low&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Novel task or AI-unfriendly domain&lt;/td&gt;
&lt;td&gt;Spike first, don't commit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My recommendation for the rule: &lt;strong&gt;always spike a ticket marked &lt;code&gt;low&lt;/code&gt; before adding it to a sprint.&lt;/strong&gt; My guess is that just one rule can prevent almost all sprint meltdowns in AI-driven teams.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. "Free Second Time" Paradigm
&lt;/h3&gt;

&lt;p&gt;It’s interesting how I notice a certain pattern: when doing any task for the first time with the help of artificial intelligence, it’s very costly. But the second time the same is done, the cost is reduced by 60%. And by the third time, it becomes pretty much negligible.&lt;/p&gt;

&lt;p&gt;How can this happen? Well, the first attempt makes one develop a specific &lt;em&gt;workflow&lt;/em&gt; – an optimal prompt structuring, a proper context window configuration, and everything that might go wrong during execution.&lt;/p&gt;

&lt;p&gt;That being said, I believe the cost estimation should be done in a different way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First instance of task type:  estimate × 2 (you're building the workflow)
Second instance:              estimate × 0.8 (refining the workflow)
Third+ instance:              estimate × 0.4 (executing the workflow)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example: consider migration of 12 microservices using a new observability SDK.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service 1: 6 hours (thinking about how to do it, writing prompts)&lt;/li&gt;
&lt;li&gt;Service 2: 2.5 hours (fine-tuning, dealing with edge cases)&lt;/li&gt;
&lt;li&gt;Services 3-12: ~45 minutes each (batches in bulk using known procedure)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Old estimate: 12 x 4 hours = 48 hours. This approach: ~16 hours. But only if you &lt;em&gt;put in the effort&lt;/em&gt; on service 1 rather than rushing through.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Review-Weighted Sizing
&lt;/h3&gt;

&lt;p&gt;I don’t believe that one should size by "how difficult will it be to develop." Instead, one must size by "how difficult will it be to verify?"&lt;/p&gt;

&lt;p&gt;The easiest pieces to create are often very difficult to review (large refactors, verbose migrations), while difficult pieces to generate are simple to review (small algorithmic fixes with explicit test cases).&lt;/p&gt;

&lt;p&gt;This sizing rubric must be inverted:&lt;br&gt;
| Old thinking | New thinking |&lt;br&gt;
|-------------|-------------|&lt;br&gt;
| "Lots of code = big ticket" | "Lots of code to &lt;em&gt;review&lt;/em&gt; = big ticket" |&lt;br&gt;
| "Complex logic = big ticket" | "Ambiguous correctness criteria = big ticket" |&lt;br&gt;
| "New framework = big ticket" | "AI-unfamiliar patterns = big ticket" |&lt;/p&gt;

&lt;p&gt;500-lines of boilerplate migration needs to be &lt;code&gt;large&lt;/code&gt; not because it’s difficult to generate, which an AI can do within minutes, but because checking for nuanced differences in 500 lines of code is truly costly.&lt;/p&gt;




&lt;h2&gt;
  
  
  How This Changes The PM Conversation
&lt;/h2&gt;

&lt;p&gt;The hardest part of any estimation paradigm shift isn’t technical. It’s explaining the change.&lt;/p&gt;

&lt;p&gt;Old conversation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This epic is 34 story points. At our velocity of 21/sprint, it’ll take ~1.6 sprints."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Where I think this discussion needs to go:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This epic has 8 tickets. 5 of which are high-confidence tickets (we’re going to meet the estimates here). 2 are medium confidence (double our estimates). 1 is low confidence (we need a spike day to be sure about that). Optimistic estimate: 1 sprint. Pessimistic: 1.5 sprints. What if the low-confidence ticket is a problem? 2.5 sprints."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bigger sentences? Yes. More useful? Absolutely. PMs have a choice to make now: "Pull out the low-confidence ticket and ship everything else on time" is now a discussion that you can have.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metrics Worth Tracking Instead
&lt;/h2&gt;

&lt;p&gt;Velocity as a metric should be scrapped in favor of more useful measures, which I would propose to track include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Curating rate&lt;/strong&gt; – proportion of review time vs creation time. Goal: below 3:1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confidence success rate&lt;/strong&gt; – proportion of 'high' tickets that make into the estimate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process reuse rate&lt;/strong&gt; – frequency of reusing a process for second similar task vs creating anew.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spike conversion rate&lt;/strong&gt; – after spike how often 'low' ticket turns into 'medium' or 'high'.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These measures will inform about the progress of the team in collaboration with the AI, as opposed to going 'fast'.&lt;/p&gt;




&lt;h3&gt;
  
  
  TL;DR – The Replacement Kit
&lt;/h3&gt;

&lt;p&gt;If you are still Fibonacci-estimating stories in 2026 and asking why sprints are akin to playing Russian roulette, here’s my suggestion:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Confidence tagging for estimates&lt;/strong&gt; — confidence level will matter more than estimate itself &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curation effort estimation vs Construction effort estimation&lt;/strong&gt; — curation will be the hard work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Novelty tracking for each task&lt;/strong&gt; — new tasks are 2-3 times costlier than recurring tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task size based on difficulty of reviewing and not generation&lt;/strong&gt; — reverse the complexity paradigm
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spike before undertaking high uncertainty tasks&lt;/strong&gt; — one simple rule to massively reduce blowups&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my mind, however, the fundamental change that needs to happen is as follows: before, estimation was focused on how much time it takes to build something. Today, it’s time to focus on how much time it takes to validate it. Change the paradigm, and suddenly, sprint planning starts reflecting reality.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Obviously just one way of seeing things—there are many brilliant minds out there who have figured out how to make story points work using AI-driven adjustments. Where do you stand: adding to the old system or building a new one from scratch?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>agile</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
