Your AI Coding Agent Needs a Dev Environment Too

I’ve been watching how AI coding agents actually work — not the demos, the real day-to-day usage — and something keeps standing out: we’ve optimized the wrong things.

Agents have great editors now. They can read files, write files, run tests, search codebases, manage git. The tooling around code manipulation is genuinely impressive.

What they can’t do — what nobody seems to talk about — is run a development loop against the services their code depends on. And if you’ve built anything nontrivial in the last decade, that’s where most real development happens.

The development loop problem

When you build a feature that talks to an external API — Stripe for payments, your team’s user service, a third-party data provider — you need that service running and responding while you develop. You write code, test it, see what the API returns, adjust, test again. Tight loop.

AI agents do this loop too. Except faster. A lot faster.

A developer might hit an API endpoint 20–30 times while building a payment integration. They know the API, they’ve read the docs, they debug by reading error messages and adjusting carefully.

An AI agent doing the same work might hit that endpoint 200–400 times. It generates code, tests it, reads the response, adjusts, generates new code, tests again. Each iteration is cheap in human terms — it costs tokens, not hours — but each iteration still needs a real response from a real service.

And that’s where everything falls apart.

What happens when agents hit real services

Three problems, in order of how much they’ll derail your afternoon:

Rate limits. Stripe’s test mode allows 25 requests per second. That sounds generous until an AI agent burns through it in a tight iteration loop, gets rate-limited, spends 400 tokens parsing the 429 response, backs off, retries, gets limited again, and eventually asks you what went wrong. Your agent just hit a wall that has nothing to do with the code it’s writing.

Cost. Not every API has a free test mode. Third-party data providers, AI inference APIs, communication platforms — many charge per request even in sandbox environments. An agent making 200 exploratory calls during a feature build adds up fast. And unlike a developer who reads docs and makes targeted calls, agents are exploratory by nature. They try things. They test edge cases. That exploration has a dollar sign attached when it hits real services.

Availability. The API you depend on is maintained by a team in another timezone. They pushed a breaking change to staging at 3 PM their time — 7 AM yours. Your agent starts working at 8 AM and immediately hits 500 errors on every call. It doesn’t know the staging environment is broken. It thinks its code is wrong. It spends 45 minutes rewriting perfectly good code to “fix” an error that isn’t its fault.

Developers recognize these situations instinctively. We check Slack, we ping the other team, we context-switch to something else. Agents don’t have that instinct. They see a broken response and conclude they need to write different code.

The gap in your local dev stack

We’ve actually solved most of the “AI agent needs local infrastructure” problem:

  • Filesystem? Agents have one.
  • Terminal? Built in.
  • Database? SQLite, Docker, your local Postgres — pick one.
  • Git? First-class support in every agent.
  • Editor/IDE integration? That’s… what they are.

What we haven’t solved: the services this application talks to over the network.

Your app makes HTTP requests to a user service. It connects to a WebSocket for real-time updates. It calls a gRPC service for search. It authenticates through an OAuth provider. Those services are remote, someone else owns them, and they may or may not be running right now.

That gap — the space between “my agent can edit code” and “my agent can develop against a real system” — is the bottleneck nobody’s addressing. Your agent has every tool it needs to write code, and no way to verify that code works against the services it’s designed to talk to.

Mock servers as agent infrastructure

I’ll be direct: this is the reason I built mockd.

Not because mock servers are a new concept. WireMock has been around since 2011. json-server since 2013. The idea of returning fake responses from a local server isn’t novel.

What’s new is the role. Mock servers aren’t just test utilities anymore. They’re development infrastructure that makes AI agents productive. The same way a local database makes a developer productive — it’s always there, it’s fast, it’s yours — a local mock server makes an agent productive against external service dependencies.

When you point an AI agent at a project that has a mock server running, the agent can:

  • Make unlimited API calls with zero rate limits
  • Get sub-millisecond responses instead of network-latency responses
  • Work against services that don’t exist yet (the other team hasn’t built them)
  • Work at 3 AM when staging is down for maintenance
  • Work in complete isolation from other developers and their agents
  • Hit intentional error responses you’ve configured, not accidental ones from broken staging

This isn’t a test-time convenience. It’s a development-time necessity.

Self-provisioning infrastructure

Here’s where it gets interesting. With MCP (Model Context Protocol), an agent doesn’t just develop against mock servers — it can manage them.

MCP is a standard protocol that lets AI agents call external tools programmatically. mockd exposes 18 MCP tools for creating endpoints, importing API specs, managing state, and inspecting request logs. Any MCP-compatible agent — Claude Code, Cursor, GitHub Copilot — can use them.

The workflow:

  1. Agent reads the OpenAPI spec for the service you depend on
  2. Agent calls mockd’s import_mocks tool to create endpoints from the spec
  3. Agent develops against those endpoints as if they’re the real service

No human writes configuration. No human runs CLI commands. The agent provisions its own development infrastructure and immediately starts building.

This is a fundamentally different relationship between developer tools and AI. The tool isn’t something the developer configures for the agent. The tool is something the agent configures for itself.

The cost math

Let’s make this concrete.

A team of 10 engineers, each with an AI agent doing 4 hours of assisted development per day. Each agent averages 150 API calls to external service dependencies during that time.

Against real services:

Cost factor Calculation Daily cost
Metered API calls 10 agents x 150 calls x $0.01/call $15
Rate-limit delays ~5 min/day/agent of wasted time 50 min lost
Staging downtime 10% availability issues = blocked agents ~2 hrs/day team-wide
Agent token waste Debugging non-code errors (429s, 500s, timeouts) Hard to quantify, but real

Against local mock servers:

Cost factor Value
API calls $0.00
Rate-limit delays 0 ms
Availability issues 0 (it’s running on your machine)
Agent token waste Near zero

The mock server isn’t a nice-to-have optimization. It’s the difference between your agent spending tokens on your feature and spending tokens fighting infrastructure it doesn’t control.

This is just the beginning

Local mock servers for AI development is a bigger topic than one post can cover. Two things I haven’t addressed yet: why “return 200 OK with some JSON” isn’t enough (your agent needs realistic service behavior to build production-ready code), and how this scales when you have multiple teams with multiple agents all developing in parallel.

If you want to try this now:

# Install mockd
curl -fsSL https://get.mockd.io | sh

# Start the server
mockd start

# Create a mock endpoint
mockd add http --method GET --path /api/users 
  --status 200 
  --body '[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]'

Your agent now has an API it can develop against. No rate limits, no costs, no downtime.

Learn more

  • Quickstart guide — install mockd and create your first mock in under 60 seconds
  • All features — MCP server, AI mock generation, 7 protocols, recording proxy, and more
  • All 7 protocols — HTTP, gRPC, GraphQL, WebSocket, MQTT, SSE, SOAP
  • Cloud tunnel — share local mocks via a public URL with one command

Links

Aave CAPO Oracle $27.78M Liquidation — Dev.to

When the Guard Dog Bites: The Aave CAPO Oracle Incident That Liquidated $27.78M
A deep dive into how Aave’s automated anti-manipulation oracle became the source of a $27.78M liquidation cascade, and what Web3 developers should learn from it.
The Incident in Brief
On March 10, 2026, Aave’s CAPO Oracle — an automated system designed to prevent oracle manipulation — pushed a faulty snapshotRatio update for wstETH. The oracle priced wstETH at ~1.19 ETH instead of the market rate of ~1.228849 ETH. A 2.85% discrepancy.
That was enough. 34 E-Mode positions were liquidated. 10,938 wstETH wiped out. Total value: approximately $27.78M.
No hacker. No exploit. The safety system itself fired on friendly forces.
Understanding CAPO Oracle
┌──────────────────────────────┐
│ Chaos Labs Edge Risk Engine │ ← Off-chain computation
└────────────┬─────────────────┘
│ Submit update
▼
┌──────────────────────────────┐
│ AgentHub (BGD Labs) │ ← On-chain execution layer
└────────────┬─────────────────┘
│ Execute (1 block)
▼
┌──────────────────────────────┐
│ Aave CAPO Oracle │ ← Price feed for protocol
└──────────────────────────────┘

Design philosophy: Speed over deliberation. No time-lock. No multisig review. If the system makes an error, it executes in the same one-block window designed for emergency response.
The Root Cause
The official post-mortem states: “a configuration mismatch between two parameters that should have moved in sync.”
contract CAPOOracle {
uint256 public snapshotRatio;
uint256 public referenceRate;

function getAssetPrice(address asset) external view returns (uint256) {
    if (asset == WSTETH) {
        uint256 basePrice = chainlinkFeed.latestAnswer();
        uint256 effectiveRatio = (snapshotRatio * referenceRate) / 1e18;
        return (basePrice * effectiveRatio) / 1e18;
    }
}

function updateParameters(
    uint256 newSnapshotRatio,
    uint256 newReferenceRate
) external onlyRole(AGENTHUB_ROLE) {
    // ⚠️ No consistency validation
    snapshotRatio = newSnapshotRatio;
    referenceRate = newReferenceRate;
}

}

The Liquidation Cascade
2.85% oracle error → 34 accounts liquidated → 10,938 wstETH → $27.78M gone.
The Two Narratives
Omer Goldberg (Chaos Labs CEO): “A misconfiguration on Aave’s CAPO oracle resulted in wstETH E-Mode liquidations, causing 345 ETH in losses. All affected users will be fully compensated.”
Stani Kulechov (Aave founder): “Technical misconfiguration caused the liquidation of positions that were already close to liquidation threshold.”
Lessons for Web3 Developers

  1. Automated Systems Need Automated Safeguards
    modifier withinSafetyBounds(uint256 newValue, uint256 oldValue) {
    if (oldValue > 0) {
    uint256 deviation = newValue > oldValue
    ? (newValue – oldValue) * 10000 / oldValue
    : (oldValue – newValue) * 10000 / newValue;
    if (deviation > MAX_DEVIATION_BPS) {
    emit SafetyBoundExceeded(oldValue, newValue, deviation);
    _scheduleTimelockedUpdate(newValue);
    return;
    }
    }
    _;
    }

  2. Multi-Parameter Systems Need Consistency Checks
    function updateParameters(uint256 newRatio, uint256 newCache) external onlyOperator {
    uint256 oldOutput = (snapshotRatio * exchangeCache) / 1e18;
    uint256 newOutput = (newRatio * newCache) / 1e18;
    uint256 outputDelta = newOutput > oldOutput ? newOutput – oldOutput : oldOutput – newOutput;
    require(outputDelta * 10000 / oldOutput <= MAX_OUTPUT_CHANGE_BPS, “Output deviation too large”);
    snapshotRatio = newRatio;
    exchangeCache = newCache;
    }

  3. Time-Locks Are Not the Enemy of Speed
    enum UpdateType { Routine, Emergency }

function scheduleUpdate(uint256 newPrice, UpdateType updateType) external onlyOperator {
uint256 delay = updateType == UpdateType.Emergency ? 0 : 30 minutes;
if (updateType == UpdateType.Emergency) {
require(hasValidMultisigSignature(), “Emergency requires multisig”);
}
_scheduleUpdate(newPrice, delay);
}

  1. Independent Monitoring Should Be a First-Class Concern
    LTV Protocol, a third-party monitor, was the first to publicly flag the anomaly — before Aave’s official post-mortem.
  2. Track Records Create Complacency
    CAPO had pushed 1,200+ payloads with zero failures. Then one failure caused $27.78M in liquidations.
    Conclusion
    The speed that makes CAPO effective against manipulation also makes it dangerous when it misfires.
    Automate safely. Validate consistency. Embrace monitoring. Design for failure.
    References:
  3. Parameter Update TX
  4. Execution TX
  5. Aave Post-Mortem
  6. LTV Protocol Alert

Printing Department

Advent of Code 2025 Day 4

The ease of Part 1 concerns me

I’ve solved this puzzle at least a dozen times throughout Advent of Code.

There is almost certainly going to be a unique twist in Part 2.

For now, it’s time to solve this yet again.

Writing another adjacent grid cell inspector

First, I want to ‘pad’ the grid with a border of .s so I can safely check each original cell without having to account for out-of-bounds errors.

I usually do this after converting the string into a 2-dimensional array.

But this time, I will opt to create a new string by adding characters from a stringified array and replacing newline characters with dot-padded newline characters.

Here’s the single, long concatenation statement:

let lineLength = input.indexOf('n')
input = new Array(lineLength + 2).fill('.').join('') 
    + 'n.' 
    + input.replaceAll('n', '.n.') 
    + '.n' 
    + new Array(lineLength + 2).fill('.').join('')

It turns this:

..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.

Into this:

............
...@@.@@@@..
.@@@.@.@.@@.
.@@@@@.@.@@.
.@.@@@@..@..
.@@.@@@@.@@.
..@@@@@@@.@.
..@.@.@.@@@.
.@.@@@.@@@@.
..@@@@@@@@..
.@.@.@@@.@..
............

And it works like a charm for any grid.

Next, my usual dictionary of relative coordinates for each adjacent cell:

let adjacents = [
    [-1,-1], [-1,0], [-1,1],
    [0,-1], [0,1],
    [1,-1], [1,0], [1,1]
]

Next, creating the grid, initializing the counter, and shell of the nested for loop:

let answer = 0
let grid = input.split('n').map(el => el.split(''))
for (let row = 0; row < grid.length; row++) {
    for (let col = 0; col < input[0].length; col++) {

    }
}

Lastly, checking each adjacent cell and tallying up the rolls:

answer += grid[row][col] == '@' 
    && adjacents
        .map(
            cell => grid[cell[0] + row][cell[1] + col] == '.' ? false : true
        )
        .filter(el => el == true).length < 4 ? 1 : 0

It is one statement that does the following:

  • Increment answer by 1…
  • As long as the current cell contains a @…
  • And the number of adjacent cells containing a @ is less than 4
  • Otherwise increment by 0

Testing it on the example input produces the correct answer, 13.

What about on my puzzle input?

…

I got the correct answer!

Nice!

And now…for Part 2’s likely diabolical twist.

Part 2

What a delightful twist

It is essentally Part 1 repeated until no change in state is produced.

Even as large as my grid is, I feel like brute-forcing this will still generate an answer fairly quickly.

I could be wrong, but that doesn’t change my approach to solve this part.

Building on Part 1

For Part 1, I processed a single iteration.

I now need a while loop to process as many iterations as it takes until no new rolls can be removed.

And I need to keep track of both the amount and location of rolls to be removed (a.k.a. turned into empty cells).

let changers = []
do {
    for (let row = 1; row < grid.length - 1; row++) {
        for (let col = 1; col < grid[0].length - 1; col++) {
            changers.push( grid[row][col] == '@' && adjacents.map(cell => grid[cell[0] + row][cell[1] + col] == '.' ? false : true).filter(el => el == true).length < 4 ? [row,col] : null)
        }
    }
    changers = changers.filter(el => el !== null)
    console.log(changers)
} while (changers.length > 0)

The code above successfully catalogs all cell locations of rolls that should be removed.

At least…after 1 iteration.

Since it never mutates the grid or clears the array, it repeatedly adds the same set of cell locations to the same array.

Time to fix that.

let changers = []
let answer = 0
do {
    answer += changers.length
    changers.forEach(cell => {
        grid[cell[0]][cell[1]] = '.'
    })
    changers = []
    for (let row = 1; row < grid.length - 1; row++) {
        for (let col = 1; col < grid[0].length - 1; col++) {
            changers.push( grid[row][col] == '@' && adjacents.map(cell => grid[cell[0] + row][cell[1] + col] == '.' ? false : true).filter(el => el == true).length < 4 ? [row,col] : null)
        }
    }
    changers = changers.filter(el => el !== null)
} while (changers.length > 0)

It was a simple addition of the forEach() loop early in the do-while loop.

I also added the answer variable to track the sum of removed rolls.

Running it on the example input generates the correct answer.

As always, fingers crossed it runs on my puzzle input…and generates the correct answer.

…

It worked!

Finished immediately and generated the correct answer!

Woohoo!!

That was a lot easer than I expected Part 2 to be.

It makes me worried for a much tougher Day 5.

Can’t wait to find out!

I Built a Free Astrocartography Calculator with Next.js — Here’s the Full Story

Most astrocartography tools online are either locked behind a $20/month paywall, built on Flash-era technology, or so cluttered with ads you can barely find the actual calculator. As a developer who stumbled into the world of astrology charts, I kept thinking: someone should just build a clean, free one.

So I did. Here’s the honest story of how I built Astrocarto — the technical decisions, the unexpected hard parts, and what I’d do differently.

What Even Is Astrocartography?

Quick background for the non-astrology crowd (which is probably most of you):

Astrocartography is a technique that takes the positions of planets at the exact moment you were born and projects them as lines onto a world map. The idea is that certain locations carry specific planetary energies for you — your “Venus line” might run through Paris, your “Mars line” through somewhere you’d rather avoid.

Whether or not you believe any of that — the math is genuinely fascinating. We’re talking about:

  • Calculating precise planetary positions using historical ephemeris data
  • Transforming between multiple astronomical coordinate systems
  • Projecting those coordinates onto a 2D geographic map

As a developer, I was hooked on the problem before I even thought about the product.

The Tech Stack

Framework:        Next.js 14 (App Router)
Styling:          Tailwind CSS
Map Rendering:    Leaflet + React-Leaflet
Astro Calc:       astronomia (npm)
Deployment:       Vercel

I’ll explain the key choices below.

Why Next.js?

Three reasons:

1. SEO is everything for a niche tool

People actively search for “free astrocartography calculator” — I needed server-side rendering so those pages could rank. With Next.js App Router, my calculator pages are fully rendered on the server before they hit the browser.

2. Route Handlers for calculation logic

I didn’t want heavy astronomical math running on the client. Next.js Route Handlers let me keep that on the server cleanly:

// app/api/calculate/route.js
export const runtime = 'nodejs'; // critical — don't use Edge here

export async function POST(request) {
  const { birthDate, birthTime, lat, lng } = await request.json();

  const chart = calculateNatalChart({ birthDate, birthTime, lat, lng });

  return Response.json({ chart });
}

3. Zero-config Vercel deployment

Push to main → live in 30 seconds. For an indie project, this matters.

The Hardest Part: Astronomical Calculations

I expected the map to be the hard part. I was wrong. The calculations nearly broke me.

Using the astronomia library

astronomia is a JavaScript port of Jean Meeus’s Astronomical Algorithms — the gold standard reference for this kind of math. It handles the heavy lifting of planetary position calculations.

Here’s a simplified version of how I get a planet’s ecliptic longitude:

import { solar, julian, base } from 'astronomia';

function getPlanetPosition(birthDate, birthTime) {
  // Convert birth datetime to Julian Day Number
  const dateStr = `${birthDate}T${birthTime}:00Z`;
  const jd = julian.DateToJD(new Date(dateStr));

  // Get Sun's position (as example)
  const sunPos = solar.apparentLongitude(jd);

  // Convert from radians to degrees
  const longitude = base.pmod(sunPos * 180 / Math.PI, 360);

  return longitude;
}

The coordinate system nightmare

Where it got painful: astrocartography lines require converting between three coordinate systems:

  1. Ecliptic coordinates — where planets are relative to Earth’s orbital plane
  2. Equatorial coordinates — right ascension + declination (like lat/lng for space)
  3. Geographic coordinates — actual latitude/longitude on Earth’s surface

For each planet, I need to find every latitude on Earth where that planet was on the Ascendant, Descendant, Midheaven, or IC at the moment of birth. That means iterating through latitudes and solving for the longitude where the condition holds.

function findAscendantLine(planet, jd) {
  const points = [];

  // Step through every latitude
  for (let lat = -80; lat <= 80; lat += 0.5) {
    // Binary search for the longitude where this planet rises
    const lng = solveForAscendant(planet, jd, lat);
    if (lng !== null) {
      points.push([lat, lng]);
    }
  }

  return points;
}

Getting this right took me about two weeks of head-scratching, cross-referencing with known charts, and fixing edge cases near the poles.

Rendering the Map with Leaflet

Once I had the coordinate arrays, rendering them with Leaflet was comparatively straightforward:

import { MapContainer, TileLayer, Polyline } from 'react-leaflet';

export function AstroMap({ planetLines }) {
  return (
    <MapContainer
      center={[20, 0]}
      zoom={2}
      style={{ height: '600px', width: '100%' }}
    >
      <TileLayer
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        attribution="© OpenStreetMap contributors"
      />

      {planetLines.map(({ planet, points, color }) => (
        <Polyline
          key={planet}
          positions={points}
          pathOptions={{ color, weight: 2, opacity: 0.8 }}
        />
      ))}
    </MapContainer>
  );
}

One Leaflet gotcha with Next.js: Leaflet touches window on import, which breaks SSR. Fix:

// Use dynamic import with ssr: false
const AstroMap = dynamic(() => import('@/components/AstroMap'), {
  ssr: false,
  loading: () => <div className="map-skeleton" />
});

What Astrocarto Can Do Right Now

The tool currently supports:

  • ✅ Rising Sign Calculator — your ascendant based on exact birth time + location
  • ✅ Natal Chart — full planetary positions at birth
  • ✅ Transit Chart — current planetary positions overlaid on your natal chart
  • ✅ Relocation Chart — how your chart shifts if you move cities

All free. No account required. Just enter your birth details and go.

Try it: astrocarto.org

What I’d Do Differently

1. Validate calculations against existing tools first

I spent days debugging math that turned out to be correct — I just didn’t have a reliable reference to check against. Next time I’d set up automated comparison tests against established chart software from day one.

2. Build the map UI before perfecting the calculations

Seeing lines on a map, even approximate ones, keeps you motivated. I got too deep into calculation accuracy before I had anything visual to show for it.

3. Add caching earlier

Astronomical calculations for the same birth data always return the same result. I should have added a simple cache from the start rather than recalculating on every request.

// Simple in-memory cache I added later
const cache = new Map();

export async function POST(request) {
  const body = await request.json();
  const cacheKey = JSON.stringify(body);

  if (cache.has(cacheKey)) {
    return Response.json(cache.get(cacheKey));
  }

  const result = calculateNatalChart(body);
  cache.set(cacheKey, result);

  return Response.json(result);
}

What’s Next

  • [ ] Mobile-optimized map gestures
  • [ ] Shareable chart URLs (so you can send your chart to a friend)
  • [ ] PDF export
  • [ ] More celestial bodies — asteroids, Chiron, nodes

Building niche tools is genuinely one of my favorite things to do as an indie developer. The intersection of an obscure domain and a real technical problem is where the most interesting projects live.

If you’ve built something in a weird niche — astrology, birdwatching, chess, anything — I’d love to hear about it in the comments.

And if you’re curious about your own astrocartography: astrocarto.org — takes about 60 seconds.

“I’m not a developer, but I built a web app with AI. Here’s how and why.”

*RadioLlama a free, Winamp-inspired radio player with no ads, no accounts, and thousands of stations.
*

The problem – (not really just kinda why not moment :D)
It started with gaming.

Story Behind:
I wanted to listen music while playing, but I didn’t want to tab out, open Spotify, deal with ads, or click through playlists. I just wanted one click from mine mouse and music starts.

Simple, right?

Turns out, nothing really worked the way I wanted. So I thought: I have been fiddling with AI a lot why not make player for this issue.

The first version
I’m not a developer. I cant code and I don’t possess time to learn it.
But I had found Ai and I had mine experience with it.

I opened Notepad++ and started prompting Grok AI. I built a Python script that could play radio streams. I made system not to break the code. I had system to handle new versions and I knew how to prompt grok and when grok stuck I changed to deepseek and prompted mine progress.

I had learned how to handle free AI Language models. To be honest working with free ai model is not walk in the park all the time, but with system and structure it’s doable for small python codes.

I finally got working version of mine first python winamp game ready music radio station player and here is picture of it:

Not the pretties of players, but it works 🙂

After this I wanted to build it more:

I moved it to a Raspberry Pi so I could run it without my computer on. After this I wanted to use it in my car while driving. That’s when things got interesting.

I found Lovable.ai an AI-powered web app builder. I started prompting. “Build a radio player.” “Add an EQ.” “Make it look like Winamp.” “Add skins.”

Lovable was so easy way to build what I wanted, just common sentences and instructions and there was something new every time after prompt. That thing is addictive.

And with lovable I made RadioLlama 😀

What RadioLlama is now
Today, RadioLlama is a “full-featured” web radio player:

Thousands of stations fetched from radio-browser.info, a huge open directory of global stations

Search by genre, country, artist, or song name — find what you actually want to hear

EQ because Winamp had it and I wanted it

Skins + wallpaper editor change the look to match your vibe

Car mode: big buttons, Bigger Screen, easy to see and use while driving

Song & artist display, so you know what’s playing

And the two things that matter most to me:

No ads. None. Ever.

No data collection. Optional accounts exist (nickname + password only, no email required) if you want to save favorites. If not, just play.

The Winamp connection
If the UI looks familiar, that’s intentional.

Winamp’s slogan was “It really whips the llama’s ass.” So I named mine RadioLlama. The attitude is the same: no hand-holding, no tutorials, figure it out. Just pure radio.

What I’m not
I’ll be honest with you: I’m not a developer by trade.

This project started with Python and AI prompts. The web app version was built with Lovable.ai. I don’t fully understand the tech stack under the hood. But I keep prompting, keep tweaking, and keep shipping.

And honestly? That’s kind of the point.

You don’t need to be a “real developer” to build something useful. You just need an idea, some curiosity, and the willingness to keep going.

Why I’m sharing this
I built RadioLlama for myself. But now I want to share it with people who might actually use it.

I don’t want your email. I don’t want your data. I don’t want your money.

I want feedback.

What sucks? What’s missing?
What would make this your go-to radio player?

I’m building this every day (at least now :D), and I want to build it with the people who use it.

Try it
🔗 https://radioLlama.com

Go break it. Tell me what to fix. Tell me what to add.

I’m listening.

P.S. If you’re a Winamp fan, you’ll probably recognize the vibe. If you’re a radio person, you’ll find stations from all over the world. And if you’re just someone who wants free music without ads — this is for you.

7 Mac Apps Built for Apple Silicon That Every Developer Should Try in 2026

If you’re running an M-series MacBook or Mac mini in 2026, you already know the hardware is absurd. But are you getting the most out of it software-wise?

I’ve spent the last year curating my Mac setup specifically around apps that are native to Apple Silicon — no Rosetta, no Electron bloat, just fast, lean tools that feel like they belong on the platform.

Here are 7 that I think every developer should at least try.

1. Warp — The Terminal That Actually Feels Modern

warp.dev

Warp is a Rust-based terminal that runs natively on Apple Silicon, and you can feel the difference the moment you open it. Command output renders instantly, the AI command search is built in, and the block-based interface makes it easy to scroll through and share terminal output. If you’re still using the default Terminal.app, this is a free upgrade that will genuinely change your workflow.

2. Raycast — The Launcher That Replaces 5 Other Apps

raycast.com

Raycast is what Spotlight should have been. It launches apps, runs scripts, manages clipboard history, controls windows, and integrates with basically every tool you use — GitHub, Linear, Notion, Slack, you name it. It’s native Swift, so it opens in milliseconds on M-series chips. The extension ecosystem is massive and growing. Once you start using it, Spotlight feels like a toy.

3. CleanShot X — Screenshots and Screen Recording Done Right

cleanshot.com

If you share screenshots in Slack, PRs, or docs (and who doesn’t), CleanShot X is a massive upgrade over the built-in screenshot tool. Annotation, scrolling capture, screen recording with GIF export, OCR text grab — it does everything. It’s a native macOS app that launches instantly and stays out of your way until you need it. One-time purchase, no subscription.

4. TokenBar — Know What Your AI Workflow Actually Costs

tokenbar.site

This one’s a tiny menu bar utility that tracks your LLM token usage in real time across providers — OpenAI, Anthropic, Google, whatever you’re hitting. If you’re using Cursor, Copilot, or running agents that burn through API calls, TokenBar gives you a live count right in your menu bar so you’re never surprised by the bill. Native Swift, uses almost no resources, and it’s $5 lifetime. The kind of tool you install once and wonder how you lived without.

5. Monk Mode — Block Feeds Without Blocking Apps

mac.monk-mode.lifestyle

Most “focus” apps block entire websites or apps. Monk Mode is smarter — it blocks the feed inside apps like Twitter, Reddit, YouTube, and LinkedIn while still letting you use search, DMs, and direct links. So you can still look up a Stack Overflow answer or respond to a DM without falling into a 45-minute scroll hole. Native macOS, runs as a menu bar app, $15 lifetime. I run it basically all day when I’m shipping code.

6. Bear — Markdown Notes That Don’t Fight You

bear.app

Bear is the notes app I keep coming back to. It’s fast, beautiful, and does markdown natively without making you think about it. The nested tagging system is brilliant for organizing dev notes, meeting notes, and quick code snippets. It syncs via iCloud, which means no account to create and no data leaving Apple’s ecosystem. The Apple Silicon build is buttery smooth — I’ve never seen it lag, even with thousands of notes.

7. Fantastical — The Calendar That Actually Understands Natural Language

flexibits.com/fantastical

Fantastical has been around forever, but the native Apple Silicon version is noticeably faster than the old Intel build. The natural language parsing is what sets it apart — type “standup every weekday at 9:30am” and it just works. The menu bar widget gives you a quick glance at your day without opening a full app. If you juggle meetings and deep work blocks (and you should), this makes calendar management almost painless.

The Common Thread

Every app on this list is native macOS, built for Apple Silicon, and either free or reasonably priced. No Electron wrappers eating 500MB of RAM. No web apps pretending to be desktop apps.

The M-series chips are incredible — might as well use software that actually takes advantage of them.

What’s in your Apple Silicon toolkit? Drop your favorites in the comments 👇

📦 I Built 3 Astro Integrations and Shipped Them to npm — Here’s What They Do

let

I’ve been building with Astro for a while now, and the same pain points kept showing up across projects. I got tired of patching around them and just built integrations instead.

Three packages. All on npm. All open source.

let's begin

🎯 What’s an Astro Integration, anyway?

If you’ve never built one — an Astro Integration is a package that hooks into Astro’s build lifecycle or dev toolbar. You return a name and a hooks object, and Astro calls your callbacks at the right moments.

export default function myIntegration(): AstroIntegration {
  return {
    name: 'my-integration',
    hooks: {
      'astro:build:done': ({ pages }) => {
        // runs after build
      }
    }
  }
}

That’s basically it. It’s how @astrojs/react, Tailwind, Partytown, and most of the ecosystem slot into your config without you wiring anything up yourself.

📦 @shiftescape/astro-bundle-budget

You build your site, ship it, and two weeks later someone notices a page is pulling in 400KB of JS. No warnings. No CI failure. Just a slow page and a mild sense of shame.

This integration hooks into astro:build:done, reads the output, and compares your JS/CSS sizes against budgets you set. Exceed them — build fails.

import bundleBudget from '@shiftescape/astro-bundle-budget';

export default defineConfig({
  integrations: [
    bundleBudget({
      budgets: [
        { type: 'js', budget: 100 },  // 100KB JS max
        { type: 'css', budget: 50 }   // 50KB CSS max
      ]
    })
  ]
});

Set it once, forget about it. The pipeline tells you when something bloats.

programmer suprised

📥 npm · source

🔍 @shiftescape/astro-env-inspector

You’ve got env vars spread across .env, .env.local, .env.production. You’re debugging something weird and you genuinely don’t know which value Astro is actually reading — or if the variable is even loaded at all.

This adds a panel to the Astro Dev Toolbar. All your env vars, grouped, masked by default, searchable. Dev only — nothing touches your production build.

import envInspector from '@shiftescape/astro-env-inspector';

export default defineConfig({
  integrations: [envInspector()]
});

Beats staring at console.log(import.meta.env) output and trying to remember which file overrides which.

inspector

📥 npm · source

🗺️ @shiftescape/astro-toolbar-routes

Once a project gets past a dozen pages, you’re either memorizing routes or digging through src/pages/ every time. Neither is fun.

This puts a route map in the dev toolbar — every page in your project, grouped, searchable, clickable. It reads your file-based routes at build time and lists them out.

import toolbarRoutes from '@shiftescape/astro-toolbar-routes';

export default defineConfig({
  integrations: [toolbarRoutes()]
});

Click a route, go there. That’s the whole thing.

explorer

📥 npm · source

Using all three together

npm install @shiftescape/astro-bundle-budget @shiftescape/astro-env-inspector @shiftescape/astro-toolbar-routes
// astro.config.mjs
import { defineConfig } from 'astro/config';
import bundleBudget from '@shiftescape/astro-bundle-budget';
import envInspector from '@shiftescape/astro-env-inspector';
import toolbarRoutes from '@shiftescape/astro-toolbar-routes';

export default defineConfig({
  integrations: [
    bundleBudget({ budgets: [{ type: 'js', budget: 100 }] }),
    envInspector(),
    toolbarRoutes()
  ]
});

The toolbar panels are dev-only. The budget check runs at build time and exits non-zero on failure, so CI picks it up without any extra config.

That’s it for now

All three are at v0.1.x — functional and tested, but still early. There’s a lot more I want to do with each of them

If any of these save you some time, a ⭐ on GitHub goes a long way!

bye-son

Built with the Astro Integration API and listed in the Astro Integrations Library.

Video Ads for Elementor, Managing Pre-Roll Campaigns at Scale

I built this plugin to solve a very specific WordPress problem: adding pre-roll ads to Elementor videos without bolting on a second player, extra shortcodes, or theme-level hacks.

A lot of sites already use Elementor’s core Video widget for lessons, demos, and landing pages. The usual ad workaround is messy. Teams stack embeds, hide scripts in templates, or wire up custom JS that becomes fragile the next time someone edits the page. I wanted the ad workflow to live where editors already work.

So the plugin extends the Elementor Video widget itself. Inside the same widget panel, I add a Video Ads section where you can enable a pre-roll clip, define skip timing, and optionally attach a visit URL. That keeps the playback flow feeling like one component instead of two unrelated video systems.

Under the hood, the plugin separates configuration into two layers. The first is per-widget control inside Elementor, which is useful when a single page needs custom timing or a different ad. The second is a global admin layer in WordPress, where I can apply a shared ad across multiple pages and exclude content when needed. That avoids repetitive setup for larger sites.

I also added a statistics screen that records plugin-level activity such as impressions, skips, and interactions. It is not meant to replace a full analytics stack, but it gives editors and site owners a direct way to verify that campaigns are actually running and compare local widget settings against global rules.

The main technical goal was reducing editor confusion. If the ad settings live outside Elementor, teams have to remember which shortcode, script, or template owns the behavior. By attaching everything to the existing Video widget, the ownership is obvious.

If you want to see the product itself, I published it here: https://nexuwp.com/product/elementor-video-ads-dynamic-video-advertising-for-wordpress/

In practice, the plugin is less about ad tech buzzwords and more about keeping WordPress video monetization maintainable. One widget, one playback journey, and a cleaner way to manage pre-roll campaigns across Elementor pages.

How I built my portfolio with React + TypeScript (Lighthouse 97/100)

How I built my portfolio with React + TypeScript (Lighthouse 97/100)

I’m an 18 y/o self-taught frontend dev and just finished my personal portfolio site.

🛠️ Stack

  • React 18 + TypeScript — core framework
  • Vite over CRA — faster builds, better DX
  • Tailwind CSS — no time wasted on CSS
  • Framer Motion — smooth animations without headaches
  • i18next — multilingual (RU/EN/DE)
  • EmailJS — contact form without a backend

⚡ Performance

Metric Score
Performance 97
Accessibility 94
Best Practices 100
SEO 100

😤 Hardest part

Getting Lighthouse performance above 95 — ended up optimizing images to WebP and fixing a few render-blocking issues.

🔗 Links

Feedback welcome! 🔥