<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nek.12</title>
    <description>The latest articles on DEV Community by Nek.12 (@nek12).</description>
    <link>https://dev.to/nek12</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1377290%2F3c1e6586-daf8-4799-9012-4fc9db5fef0f.png</url>
      <title>DEV Community: Nek.12</title>
      <link>https://dev.to/nek12</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nek12"/>
    <language>en</language>
    <item>
      <title>How to Build Agentic Graphs</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Sat, 29 Aug 2026 13:24:57 +0000</pubDate>
      <link>https://dev.to/nek12/how-to-build-agentic-graphs-302g</link>
      <guid>https://dev.to/nek12/how-to-build-agentic-graphs-302g</guid>
      <description>&lt;p&gt;Over the past 4 months of working with graphs, I've learned several major lessons about graph design the hard way. In this post, I want to share the main takeaways so you don't repeat my mistakes. &lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;my&lt;/strong&gt; definition of graphs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Agent graphs (a.k.a. workflows) are directed graphs that allow cycles and describe how work is passed between agents (nodes) operating in a loop through predefined transitions (edges). Graphs consist of branches, loops, scripts, and transitions (along with their prompts and parameters).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Parallelism is not the silver bullet
&lt;/h2&gt;

&lt;p&gt;At first, I was very enthusiastic about parallel branches in graphs. But over time, I realized that parallelism can not only increase costs but also &lt;strong&gt;slow down&lt;/strong&gt; task execution.&lt;/p&gt;

&lt;p&gt;A standard parallel group of checks may include code review, QA, and scope review. The problem begins when these stages are inside a loop.&lt;/p&gt;

&lt;p&gt;Let's take a simple example. Suppose code review, QA, and architecture run in parallel, after which the task returns to implementation if necessary.&lt;/p&gt;

&lt;p&gt;If the architecture review passes but the code review finds several minor issues, the task returns to the implementation agent. Once the fixes are made, it goes back for review - and the architecture reviewer has to examine the updated diff again, even though the previous version was completely acceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In cyclic graphs, parallel checks often lead to duplicated work, cache invalidation, and unnecessary costs with no real benefit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In theory, this problem can be solved with a smart router. Kent supports this through script nodes: the router can determine whether the agent completed the entire implementation or only addressed feedback from a specific reviewer (kent.sh is my free, open-source project for building agent graphs. I mention it because I use it myself and don't know of any similar products. You can apply this advice to any comparable orchestrator).&lt;/p&gt;

&lt;p&gt;However, this brings us back to the problem we were trying to avoid with agent graphs: the agent once again gets to decide which verification stages need to be run. This negates a significant portion of the graph's value.&lt;/p&gt;

&lt;p&gt;In practice, the solution is simpler: dependent checks should run sequentially. &lt;strong&gt;In my workflows, architecture review always comes before code review.&lt;/strong&gt; The task moves on to code review only after the architecture has been approved.&lt;/p&gt;

&lt;p&gt;That's why I've removed many parallel stages and now save tokens by avoiding checks on results that would have been rejected at another stage anyway.&lt;/p&gt;

&lt;p&gt;This approach works especially well with planning, code review, and QA. For example, code review should first filter out implementation issues, and only then should QA begin. Otherwise, both stages may independently find the same bug and produce duplicate feedback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents must be able to challenge feedback
&lt;/h2&gt;

&lt;p&gt;Initially, absolutism and dictatorship ruled my development agent graph: every reviewer comment had to be addressed, or the task could not proceed. But reviewers don't always produce the right result either.&lt;/p&gt;

&lt;p&gt;Now, every agent in my graphs can ask me a question and clarify what to do with conflicting feedback. For example, scope review may reject tests that code review had required just one step earlier because it considered task verification incomplete without them. At the same time, agents cannot be fully trusted to resolve such conflicts on their own. Even with new models like Sol, you can end up in an infinite loop of fixing made up or nitpick problems.&lt;/p&gt;

&lt;p&gt;I solve this by delegating the final decision to myself (pure choice, I like to be involved). You can also hand it off to a PM agent or set up communication between multiple agents. For example in Kent agents can get others' session IDs so they can discuss the situation and reach a compromise.&lt;/p&gt;

&lt;p&gt;Anthropic in their &lt;a href="https://www.anthropic.com/research/multiagent-systems" rel="noopener noreferrer"&gt;recent paper&lt;/a&gt; argue that this is the model's problem. I disagree - this is the harness's problem, and my system above proves that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A graph must have a mechanism for escalating conflicting or questionable feedback - otherwise, review turns into a dictatorship capable of trapping the entire workflow in a loop, or a war of stubborness.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't forget static checks
&lt;/h2&gt;

&lt;p&gt;Agent graphs sound exciting, and it's easy to want to create dozens of agents and verification stages. This can indeed reduce the primary agent's cognitive load and improve the quality of its work, but static checks should take priority.&lt;/p&gt;

&lt;p&gt;Initially, my implementation agent ran the linter, architecture tests, and unit tests itself, opened the PR, and checked incoming comments. I realized at one point that that's just cargo culting, then decided to move these actions into script nodes in the agent graph.&lt;/p&gt;

&lt;p&gt;Now, a separate stage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runs the required static checks and tests;&lt;/li&gt;
&lt;li&gt;properly manages the machine's shared resources;&lt;/li&gt;
&lt;li&gt;filters the results;&lt;/li&gt;
&lt;li&gt;returns only relevant information to the implementation agent;&lt;/li&gt;
&lt;li&gt;invokes the agent again only when its involvement is actually required.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the tests are green, the implementation agent never even learns about it: no new turn is started, which means the agent doesn't spend a single token on running tests or reading their results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't assign an LLM work that a regular script can perform more reliably and cheaply.&lt;/strong&gt; At workflow scale, this produces substantial savings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose models appropriate for tasks
&lt;/h2&gt;

&lt;p&gt;If you don't optimize your graph for token usage and cost, you can significantly overspend simply because many tasks will be overkill under the updated workflow. In the past, we used one model for everything in harnesses because we had no alternative. You no longer need to do that, and properly allocating models and resources can save you a lot of money.&lt;/p&gt;

&lt;p&gt;In standard harnesses, you can usually switch models, but doing so invalidates caches. On top of that, you either retain the cluttered context from the previous session or start a new one and steer/prompt it manually.&lt;/p&gt;

&lt;p&gt;Kent solves these problems, so don't be afraid to create different roles for agents. For example, manual QA can run on cheap models like DeepSeek or Luna, which cost almost nothing or barely affect your subscription quota. The smartest models can then be reserved for critical stages, such as planning.&lt;/p&gt;

&lt;p&gt;It has long been known that if you have a good plan, you can assign implementation to a less capable model and get almost the same result. Moreover, additional verification stages reduce the minimum level of model intelligence required to implement a task even further.&lt;/p&gt;

&lt;p&gt;Starting with version 2.6, Kent natively allows one agent to select the model, system prompt role, and reasoning level for the next agent after transitioning along a graph edge. This makes it possible to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;delegate simple tasks and bug fixes to models like Luna;&lt;/li&gt;
&lt;li&gt;run QA on cheap models with high limits;&lt;/li&gt;
&lt;li&gt;hand simple decisions off to local models;&lt;/li&gt;
&lt;li&gt;reserve the strongest models for complex planning and critical checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keep an eye on caches and time between turns
&lt;/h2&gt;

&lt;p&gt;I measured the threshold beyond which the probability of continuing a session after a cache miss - and paying several times more - becomes high enough for preemptive compaction to be worthwhile.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu44pnbhxgxju2tiumz8y.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu44pnbhxgxju2tiumz8y.webp" alt="Image" width="800" height="376"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;speculative compaction (for regular sessions) becomes worthwhile at ~88% context usage according to this slop-chart. For workflows, my statistical threshold is around 71%&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Imagine that the implementation agent spent 40 minutes addressing code review feedback. During that time, the reviewer agents' caches may have been invalidated. When they review the work a second time, Kent will compact the session in advance so the review continues with fresh context and without unnecessary costs caused by a cache miss.&lt;/p&gt;

&lt;p&gt;But this is only a heuristic. You should still consider how much time passes between consecutive calls to the same agent. If the workflow is long and a node waits a long time for the work to return, the likelihood of cache invalidation increases.&lt;/p&gt;

&lt;p&gt;In this case, there are two main options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use &lt;code&gt;compact and continue&lt;/code&gt; mode in Kent - it is similar to &lt;code&gt;speculative compact&lt;/code&gt;, but compaction is always performed;&lt;/li&gt;
&lt;li&gt;create more granular checkpoints that return work to the agent more frequently and keep caches warm.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the right setup, you can reduce costs so much that the average cost of completing a task is lower than working in a regular chat with the same Sol/Opus at standard reasoning.&lt;/p&gt;

&lt;p&gt;If you ignore this, it's easy to fall into the overkill trap and become disappointed with agentic graphs: "This is too expensive for me." But in practice, &lt;strong&gt;well-designed agent graphs can be more efficient than standard sessions.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Make nodes idempotent
&lt;/h2&gt;

&lt;p&gt;As my graph evolved, I added more and more ways to send a task backward. Different reviewers and stages gained the ability to return it to previous nodes. This gives agents the flexibility they need, for example, if the implementation agent receives a flawed plan, it should be able to return the task to the planning stage and explain exactly what needs to be fixed. As in regular software development, product issues and underspecified requirements are often discovered only during implementation.&lt;/p&gt;

&lt;p&gt;That's normal, but what's not normal is a graph that gives the agent no way to handle such a situation. Every flawed line in a plan can potentially lead to thousands of lines of incorrect code.&lt;/p&gt;

&lt;p&gt;But a non-obvious topological problem arises &lt;em&gt;after&lt;/em&gt; the task returns to an earlier stage. Subsequent nodes may receive it with fresh context and a prompt implying that the work should start from scratch. For example, the implementation agent returns an unfinished task for replanning, then receives an instruction to implement the updated plan as though no previous work existed.&lt;/p&gt;

&lt;p&gt;This can cause duplication, conflicting implementations in the same codebase, and wasted money - and not in the form of an obvious workflow failure, but through subtle issues like "weirdly many git commits on the PR". It's also a common mistake made by agents themselves when they build workflows for you, including Kent. Agents struggle to analyze topology in the context of prompting - to put themselves in the shoes of the agent doing the actual work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Re-entering a node should not automatically mean repeating all the work from scratch. The agent must account for the existing result and continue from the current state.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kent supports this natively: for implementation-related nodes, you can enable the &lt;em&gt;continue or new&lt;/em&gt; continuation mode.&lt;/p&gt;

&lt;p&gt;Prompts should also be adapted: explicitly state that receiving a task again does not mean the agent needs to start over. Kent already adds the relevant instructions to agent prompts during a workflow, but custom prompts may still implicitly assume that the work begins from scratch, and that can &lt;strong&gt;cause the model to freak out REALLY hard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Idempotent nodes, controlled returns, and proper context reuse make an agent graph resilient not only to model errors but also to the real-world nonlinearity of development.&lt;/p&gt;

</description>
      <category>agenticgraphs</category>
      <category>aiagents</category>
      <category>llm</category>
      <category>workflow</category>
    </item>
    <item>
      <title>Meet Kent 2.0 - Your Coding Accomplice</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Mon, 15 Jun 2026 13:14:09 +0000</pubDate>
      <link>https://dev.to/nek12/meet-kent-20-your-coding-accomplice-26m0</link>
      <guid>https://dev.to/nek12/meet-kent-20-your-coding-accomplice-26m0</guid>
      <description>&lt;p&gt;Introducing &lt;a href="//kent.sh"&gt;Kent&lt;/a&gt; 2.0 - a new version of my coding agent. &lt;strong&gt;The most significant change: Builder has been renamed to Kent.&lt;/strong&gt; Still for builders, but now Kent is your true coding accomplice.&lt;/p&gt;

&lt;p&gt;We also got a new domain at &lt;a href="https://kent.sh" rel="noopener noreferrer"&gt;kent.sh&lt;/a&gt;.&lt;br&gt;
&lt;strong&gt;Migration&lt;/strong&gt; will be described in detail when you update your Brew tap or run the migration script: essentially, you'll get a new command to run and a new location for all Kent data.&lt;/p&gt;
&lt;h2&gt;
  
  
  Performance Fixes
&lt;/h2&gt;

&lt;p&gt;This release includes dozens of fixes and improvements for both model behavior and quality of life. &lt;strong&gt;The main focus is performance&lt;/strong&gt;, because I currently have a session that has been running continuously for 250+ compactions. At first Kent couldn't handle that session's file size (30+GB), so I had to urgently rewrite a lot of things. Performance is now great.&lt;/p&gt;

&lt;p&gt;Also, we have a new brand color palette - brighter and more vibrant, consistent across the entire product.&lt;/p&gt;
&lt;h2&gt;
  
  
  System Prompt Customization
&lt;/h2&gt;

&lt;p&gt;You can now customize system prompts more precisely. &lt;strong&gt;The system prompt is split into sections, and you can pick only what you need.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, for most subagents, the sections about delegation or code quality - slanted toward my own philosophy - aren't necessary. Now you can use different templates in the prompt, build your own based on recommendations that I dynamically update in the repository, and add your own pieces or remove what you don't need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the point? Creating custom subagents.&lt;/strong&gt; I now have new subagents for plan review, code review, and plan drafting - with different settings and different system prompts. Works great.&lt;/p&gt;

&lt;p&gt;I also made improvements so the model correctly launches subagents on its own, communicates with them, and independently uses the roles you give it. More details in the docs. Here's an example subagent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[subagents.plan_reviewer]&lt;/span&gt;
&lt;span class="py"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gpt-5.5"&lt;/span&gt;
&lt;span class="py"&gt;thinking_level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"xhigh"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Extra-smart, costly, slow subagent for adversarial review of plans, architectures, specs, and implementation strategies. Use when the plan needs a hard quality gate or a second look before implementation. Can take a while to complete, give it time. Cannot edit files. Use --continue for re-reviews."&lt;/span&gt;
&lt;span class="py"&gt;system_prompt_file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"agents/plan_reviewer.md"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;description&lt;/code&gt; is what the model will see in its context.&lt;/strong&gt; You can specify when and how to use this subagent there - similar to skills and agents in Claude Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminal Rendering Fixes
&lt;/h2&gt;

&lt;p&gt;Dozens of fixes for terminal rendering: repeated prompts, disappearing prompts, vanishing messages, messages accumulating in the buffer, interrupted sessions, rendering that suddenly stops, duplicates, flickering of markdown - all fixed.&lt;/p&gt;

&lt;p&gt;I'll be honest: I've been really disappointed with Go for TUI, so I'm &lt;strong&gt;rewriting Kent's terminal client from scratch&lt;/strong&gt;. For now I've put in fixes - I know there are still issues and things break sometimes, but at least it's usable now. I'll try to ship the new terminal UI as soon as possible to put all these problems to rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Slash Command &lt;code&gt;/questions&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The new &lt;code&gt;/questions&lt;/code&gt; command lets you toggle the model's ability to ask you questions.&lt;/strong&gt; This doesn't invalidate the cache - the tool simply fails with an error. It's primarily useful for sessions where you want the model to temporarily leave you alone.&lt;/p&gt;

&lt;p&gt;For example, if you're going to sleep and don't want a randomly asked question to pause work for the whole night. This has happened to me more than once during long-running tasks - so I finally added this command to turn questions off at night and back on in the morning. Very handy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goal Improvements
&lt;/h2&gt;

&lt;p&gt;A lot of fixes for the goals feature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The model now receives a reminder of its goal when starting a new session after a compact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model can no longer overwrite goals with random ones&lt;/strong&gt; while continuing to work on the previous one. This was one of the causes of hallucinations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  System Sleep Prevention
&lt;/h2&gt;

&lt;p&gt;Another important thing for long-running tasks - your laptop or computer going to sleep. My Mac kept falling asleep and work would stall. &lt;strong&gt;Implemented a config option that prevents system sleep.&lt;/strong&gt; It's enabled by default, but only when an agent is actively doing something.&lt;/p&gt;

&lt;p&gt;So your Mac will smartly go to sleep only when no agents are running. More details in the docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compact Fixes
&lt;/h2&gt;

&lt;p&gt;Thanks to everyone who sent bug reports. &lt;strong&gt;Fixed issues with compact: sessions no longer just die when the context window is exceeded by a lot.&lt;/strong&gt; Such recovery invalidates the cache - yes, annoying, but there's no other way - but the session will now continue successfully after trimming the excess context that was blocking requests from being sent to the servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Models
&lt;/h2&gt;

&lt;p&gt;Finally got the new Mac, so now I can dogfood local models. Made a bunch of fixes for them to work correctly and &lt;strong&gt;simplified prompting for local models to reduce hallucinations&lt;/strong&gt;. A context window of at least 100k tokens is still recommended.&lt;/p&gt;




&lt;p&gt;Would love to hear what you think about the name Kent :) I got really lucky with the domain - kent.sh is a great short domain, the name ranks well in search, easy to top. Kent comes from Russian "jail jargon" - meaning "Bro".&lt;/p&gt;

</description>
      <category>kent</category>
      <category>agents</category>
      <category>llm</category>
      <category>subagents</category>
    </item>
    <item>
      <title>I gave your agent access to Firefox - meet Firefox CLI</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Thu, 11 Jun 2026 18:37:17 +0000</pubDate>
      <link>https://dev.to/nek12/i-gave-your-agent-access-to-firefox-meet-firefox-cli-1pdm</link>
      <guid>https://dev.to/nek12/i-gave-your-agent-access-to-firefox-meet-firefox-cli-1pdm</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/respawn-llc/firefox-cli" rel="noopener noreferrer"&gt;Firefox CLI&lt;/a&gt; is my new project - a CLI interface &lt;strong&gt;that lets your agent control your real Firefox session.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a full equivalent of &lt;a href="https://github.com/vercel-labs/agent-browser" rel="noopener noreferrer"&gt;Agent Browser&lt;/a&gt; with the same capabilities, but for Firefox - and with a number of improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why it's better
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;First, you install the extension once and for all.&lt;/strong&gt; The extension ships right alongside the CLI: install it, grant access, forget about it. Unlike Chrome, where you have to grant connection permissions every half hour and manage debugging sessions - here it's one button and full control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, your agents can now create their own separate windows and request your permission to connect on their own.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In everything else, Firefox CLI mirrors Agent Browser: &lt;strong&gt;token-efficient operation via short IDs&lt;/strong&gt;, running arbitrary scripts, keypresses, input emulation, form filling, and full tab and window management of your real session - where you're already logged in.&lt;/p&gt;

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

&lt;p&gt;I used the Comet browser for a long time (on my promo subscription to Perplexity), but it started to let me down. More unnecessary features and ads crept in, it got slower. But the main thing - &lt;strong&gt;using Comet as an actual browser during development is extremely inconvenient&lt;/strong&gt;: there's music you can't turn off, a broken onboarding that was never fixed after months of back-and-forth with support, and a poorly functioning CDP.&lt;/p&gt;

&lt;p&gt;I switched back to Firefox as my main browser, but losing the ability for agents to control my browser was a huge blow to my workflow. &lt;strong&gt;No automation for filling out boring freelance forms, no proper web app testing.&lt;/strong&gt; I went looking for alternatives, but nothing like Agent Browser for Firefox simply existed. And here's the result :)&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Install the CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; firefox-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Install the Firefox extension:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firefox-cli setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Install the skill for agents:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Claude Code&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin marketplace add respawn-llc/claude-plugin-marketplace
/plugin install firefox-cli@respawn-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Codex&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$skill-installer install https://github.com/respawn-llc/firefox-cli/tree/main/skills/firefox-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;General&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills@latest add respawn-llc/firefox-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;The project was built by &lt;a href="https://nek12.dev/blog/builder-open-source-coding-agent-for-engineers" rel="noopener noreferrer"&gt;Builder&lt;/a&gt; autonomously over 62 hours of continuous work.&lt;/p&gt;

</description>
      <category>firefox</category>
      <category>cli</category>
      <category>aiagents</category>
      <category>browserautomation</category>
    </item>
    <item>
      <title>MCP is Deprecated</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Mon, 25 May 2026 13:02:59 +0000</pubDate>
      <link>https://dev.to/nek12/mcp-is-deprecated-5d11</link>
      <guid>https://dev.to/nek12/mcp-is-deprecated-5d11</guid>
      <description>&lt;p&gt;I believe the MCP standard should be deprecated and its use should stop. Many people have asked me to explain why &lt;a href="https://github.com/respawn-app/builder" rel="noopener noreferrer"&gt;Builder&lt;/a&gt; doesn't support MCP and why I'm so against using them. There are several reasons - let's go through each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 1: Zero Flexibility
&lt;/h2&gt;

&lt;p&gt;The first and main reason: all the functionality MCP provides is delivered to the model exclusively as tools it can call. This was designed back in 2024, in an era when people didn't even know that agents could run with access to Bash, to a CLI, to the command line. So tools were the only way to extend agent functionality - they were supported in the API.&lt;/p&gt;

&lt;p&gt;Now, most coding agents and even general-purpose agents run on real machines with command-line access. &lt;strong&gt;So the tools MCP provides have gone from a feature to a liability.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why? Because agents are exceptionally good at working with the command line. They use commands better than any Linux power user, and they can write and execute any scripts they need. So it costs them nothing to implement whatever functionality they need using a convenient CLI interface or even hitting the API directly.&lt;/p&gt;

&lt;p&gt;For example, if you connect the GitHub MCP, your agent gets tools loaded into its context whose output it can't post-process in any way, and it can't create reusable scripts for repetitive tasks. Atlassian MCP will dump massive JSON blobs of tens of thousands of tokens into the agent's context on every tool call - with no way to pipe through &lt;code&gt;jq&lt;/code&gt; for filtering, parse it and strip garbage escape characters, write it to a file and process it in chunks, run scripts, or write a custom utility for convenient reuse. Even composing multiple calls costs you sequential round-trips to the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If the agent were calling this through the CLI, it could write a reusable script that fires all of the required commands and immediately processes their results&lt;/strong&gt; - one API request, and the agent gets exactly what it needs as output. This is the biggest problem with MCP for me - the complete lack of flexibility in how tools are used and reused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 2: Context Pollution and No Progressive Disclosure
&lt;/h2&gt;

&lt;p&gt;Second reason: the definition of all MCP functionality - all tools, all features, output format, and input arguments - is fully loaded into the model's context at the start of every session, and there's no getting rid of it.&lt;/p&gt;

&lt;p&gt;We moved out of the prompt engineering era into context engineering, and now into harness engineering, a long time ago. &lt;strong&gt;But MCP is stuck in place because of a fundamentally broken design that has no mechanism for progressive disclosure of information to the agent.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your agent needs to leave a comment on a GitHub ticket, it can't just look up the request format for that specific action. Along with the tool it needs, it gets 45 others that take up 50,000 context tokens - just because it wanted to leave a comment. And you have no way to disable the extra MCP tools. &lt;strong&gt;This is a fundamental flaw in the architecture: if you start a session and realize a particular tool isn't needed, you can't just turn it off&lt;/strong&gt; - doing so would fully invalidate caches. Result: 50,000 context tokens wasted.&lt;/p&gt;

&lt;p&gt;You might argue: who cares, we have plenty of tools anyway? The thing is, every extra tool in context doesn't just add tokens. Even if you write minimalist tool descriptions and design your MCP perfectly, you're still expanding the agent's decision surface.&lt;/p&gt;

&lt;p&gt;Imagine that, as a human, every time you want to screw the next bolt in, you have to first re-decide and find the right one among 50 tools in the garage. &lt;strong&gt;The exact same thing happens with agents.&lt;/strong&gt; If you have 50 tools in an MCP server, the model's attention gets spread across all 50, with some fraction of it going to each one. Roughly speaking, the model runs through every tool like a checklist: &lt;em&gt;do I need &lt;code&gt;create_github_comment&lt;/code&gt;? do I need &lt;code&gt;search_github_issues&lt;/code&gt;? do I need &lt;code&gt;edit_github_pull_request_description&lt;/code&gt; right now?&lt;/em&gt; - and so on through every tool in context. And this happens not just before calling the right tool, but literally on every step the agent takes, even when it's doing something completely different.&lt;/p&gt;

&lt;p&gt;Some will say: "But Anthropic made tool search - now Claude can discover tools." First, this doesn't fully solve the problem: Claude still gets a list of all tools from all MCPs in context at startup, just in a more minimal format. This only reduces context usage but doesn't eliminate the need to make decisions - Claude still sees &lt;code&gt;create_github_issue&lt;/code&gt;, it just now needs an extra &lt;code&gt;get_tool_schema&lt;/code&gt; call to get the details. &lt;strong&gt;In some ways this makes things even worse.&lt;/strong&gt; Second, it's just a band-aid fix: people complained, and Anthropic shipped the first thing that came to mind. It doesn't solve the progressive disclosure problem at the root - it treats the symptom, and now the agent needs an extra step just to learn tool descriptions.&lt;/p&gt;

&lt;p&gt;Worth mentioning separately: &lt;strong&gt;MCP, by its standard, is required to provide usage context alongside the tools&lt;/strong&gt; - what they're for, when and how to use them. And there's nowhere to put this except in the tool's &lt;code&gt;description&lt;/code&gt; or in a separate tool that burns the agent's turns. Which means you don't even know what you're actually adding when you connect an MCP. You can connect an MCP that will tell your model to never make commits, or exfiltrate your credentials to a third-party server - and the model will comply, because all of this lands in the system block and carries nearly the same authority as the system prompt. This is exactly why malicious MCPs are so widespread: the moment a user enables one, the attacker can do whatever they want with the model.&lt;/p&gt;

&lt;p&gt;Compare this to CLI: the agent only gets instructions when it calls a command's &lt;code&gt;help&lt;/code&gt;. If the agent needs to leave a comment on GitHub, it calls &lt;code&gt;gh --help&lt;/code&gt;, learns the relevant commands right before using it - and this has zero impact on the session until the GitHub CLI is actually needed. &lt;strong&gt;Instructions are revealed immediately before using the tool, exactly as they should be&lt;/strong&gt; - since the model's attention is significantly more focused on the end of the context window. The agent gradually "forgets" instructions from the beginning of the context. So with MCP you'll see constant degradation in tool usage quality, whereas with CLI the agent receives instructions later - which increases how long it actually follows them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 3: Strange Architecture and an Unstable Standard
&lt;/h2&gt;

&lt;p&gt;Let's talk about MCP's architecture. First, if an MCP runs locally, it must spin up a server that agents communicate with over STDIO. It's unclear why they couldn't make them stateless and store auth credentials elsewhere. Why STDIO transport - essentially a hack for passing data through terminal I/O - rather than any other approach? Now Claude users have 56 &lt;code&gt;node&lt;/code&gt; processes eating up gigabytes of data just because they needed to look up maven artifacts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The MCP standard started with SSE events, and in early 2025 the authors of every single MCP had to migrate to HTTP transport and rework their implementations.&lt;/strong&gt; With CLI you can count on decades of stability - terminals aren't going anywhere anytime soon - but with MCP every standard change brings another round of refactoring.&lt;/p&gt;

&lt;p&gt;And guess who designed the MCP standard almost single-handedly? Anthropic. &lt;strong&gt;You are dependent on one company's decisions for your agent tooling.&lt;/strong&gt; Meanwhile, CLI is a seventy-year-old standard for working with terminals.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLI as an Alternative
&lt;/h2&gt;

&lt;p&gt;In laying out all these flaws, I've already been bringing up CLI and bash tooling - and not by accident. &lt;strong&gt;For agents with filesystem access, this is the best option right now&lt;/strong&gt;, at least until something better comes along.&lt;/p&gt;

&lt;p&gt;CLI interfaces have always existed and always will. Almost all the functionality you need is already available through the console: agents with terminal access can automate tasks on a computer, write code, deploy projects, manage auth in Google Cloud, Amazon, DevOps, convert media files to any format, edit documents - all exclusively through console commands. This is vividly illustrated by OpenClaw's popularity boom: people realized that an agent with command-line access and broad privileges can do an enormous amount of useful work.&lt;/p&gt;

&lt;p&gt;Imagine if every piece of OS functionality were another MCP tool - you'd have not hundreds but thousands of tools that the agent would have to dig through, that would have to be built from scratch, and whose results would have to be shuttled through ill-fitting protocols. Let's not reinvent the wheel and instead keep building on what Linux power users have been doing for decades.&lt;/p&gt;

&lt;h2&gt;
  
  
  Counterarguments to CLI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The one thing I can't argue against&lt;/strong&gt;: if your agent doesn't have command-line access, you can't call CLI tools and will have to hit the API directly. However, solutions for virtual filesystems and virtual CLI environments are already emerging - you can use those instead of continuing to add MCPs.&lt;/p&gt;

&lt;p&gt;These are still rough, so the only real use case where you genuinely can't avoid MCP right now is agents without filesystem access and no other way to interact with an external resource.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But MCP is, at the end of the day, a lazy hack instead of a real harness.&lt;/strong&gt; We do harness engineering, and the first rule of a good agent is building a good harness. If you just plugged in an MCP with 50 tools for all possible tasks without trying to specialize the agent or configure its environment, performance on complex tasks will reflect that. Think about it: maybe you're adding MCP just to save time and avoid doing real harness design?&lt;/p&gt;

&lt;h2&gt;
  
  
  On Authorization
&lt;/h2&gt;

&lt;p&gt;Some people ask: "What about authorization in MCP?" The thing is, the MCP standard doesn't actually help much with authorization. &lt;a href="https://github.com/respawn-app/tool-filter-mcp/issues/4" rel="noopener noreferrer"&gt;STDIO servers that run locally didn't support authorization at all&lt;/a&gt; the last time I checked - so you can't make them secure. Remote MCPs support OAuth, which is a reasonable standard in principle. But what part of that doesn't CLI support?&lt;/p&gt;

&lt;p&gt;With CLI you can securely store keys in your operating system's Keychain, and the CLI accesses them when the relevant commands are called. You don't need to spin up a separate server, you don't need harness-level integration just to store keys properly - &lt;strong&gt;every CLI handles its own auth data storage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the MCP standard, Anthropic specified that the harness should handle storing and providing authorization data for MCP - a task that's completely foreign to it. I think this is a wrong separation of responsibilities. If a CLI requires authorization, the CLI should own that from A to Z, not delegate it to a harness that has no defined way of implementing that authorization. Some agents were storing all keys in a flat directory on the host machine - and you didn't even know, because it looked transparent through the harness. If a CLI has poor auth, you can isolate that CLI. But if your entire harness does - what then?&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation and Prompts in MCP
&lt;/h2&gt;

&lt;p&gt;MCP also assumes that if you don't want to pack everything directly into tool schemas, you need to attach some kind of skill on top - the standard supports custom prompts and instructions (which, in my experience, almost nobody uses). Meaning you have to rely twice on the provider or your own wrapper, process everything according to the standard, because nothing works out of the box if you want to contextually surface knowledge to the agent.&lt;/p&gt;

&lt;p&gt;This just locks you further into a walled garden. With CLI you always have &lt;code&gt;--help&lt;/code&gt;, and every subcommand has a place to add a description. As the author of &lt;a href="https://github.com/respawn-app/ksrc" rel="noopener noreferrer"&gt;both an MCP and a CLI&lt;/a&gt;, I know how hard it is to maintain documentation spread across disconnected locations, and how many security holes there are in the MCP standard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Instead of MCP?
&lt;/h2&gt;

&lt;p&gt;Let's say you agree that MCP should be deprecated, but you have 25 MCPs with no equivalent CLI. How do you work with Figma, for instance?&lt;/p&gt;

&lt;p&gt;The solution already exists. Providers are actively building CLIs for their popular tools. &lt;strong&gt;As a temporary solution, I recommend using tools that port MCPs to CLIs&lt;/strong&gt;, such as &lt;a href="https://github.com/openclaw/mcporter" rel="noopener noreferrer"&gt;mcporter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And in general - try to find an existing CLI or build a CLI for your tooling yourself. It's not as hard as it sounds.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>cli</category>
      <category>aiagents</category>
      <category>llm</category>
    </item>
    <item>
      <title>Claude-pilled: why complex agent workflows are working against you</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Fri, 01 May 2026 12:37:12 +0000</pubDate>
      <link>https://dev.to/nek12/claude-pilled-why-complex-agent-workflows-are-working-against-you-222j</link>
      <guid>https://dev.to/nek12/claude-pilled-why-complex-agent-workflows-are-working-against-you-222j</guid>
      <description>&lt;p&gt;There's a pattern I keep noticing in the community. Someone starts working heavily with LLMs - and gradually drifts off track. They build themselves a complex system of agents with roles, slash commands, swarms, orchestrators, step-by-step workflows, "plan mode" etc. Peter Steinberger nailed it with the term &lt;a href="https://x.com/steipete/status/2039551079621566812" rel="noopener noreferrer"&gt;"Claude-pilled"&lt;/a&gt; - the person gets so deep into the tooling around Claude that they start believing: the more complex the system, the better the result.&lt;/p&gt;

&lt;p&gt;I don't think that's true. And the further I go, the more convinced I am of the opposite.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a "Claude-pilled" workflow looks like
&lt;/h2&gt;

&lt;p&gt;One popular example is the "gstack" from Garry Tan, CEO of YCombinator, which has been widely &lt;a href="https://x.com/tszzl/status/2039835679853809803?s=20" rel="noopener noreferrer"&gt;mocked&lt;/a&gt; on Twitter.&lt;/p&gt;

&lt;p&gt;Tons of slash commands. Agents with roles. Prompts for those agents that were apparently auto-generated by Claude a few months ago and haven't really been revisited since. The instructions are full of noise, contradictions, and obvious statements that just get in the model's way. Tasks are decomposed down to the level of "which package to put a function in" and "which line to declare an interface on."&lt;/p&gt;

&lt;p&gt;I get where it comes from. Claude with its plugins, agents, and structured workflows practically nudges you toward this. And at some point you think: &lt;em&gt;since I've already set up the agent - might as well have it follow strict rules.&lt;/em&gt; Step by step. With clear instructions for every move.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem is that this doesn't help the model. It gets in the way.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why micromanaging agents is a step backward
&lt;/h2&gt;

&lt;p&gt;Frontier models - Opus, GPT-5.5, etc. - can make decisions on their own. They can write 8,000 lines of code in one shot, decide in the moment which package something belongs in based on the specs and project context. They don't need you to spoon-feed every step.&lt;/p&gt;

&lt;p&gt;When you create a rigid workflow with rules that apply to all tasks the same way - you're stripping the model of the flexibility that makes it useful. &lt;strong&gt;Real tasks always contain something unplanned.&lt;/strong&gt; Something that surfaces mid-process. Something the user left unsaid.&lt;br&gt;
A rigid agent system can't adapt to that - it will execute strictly to the instructions. And now you've got non-compiling code in the repo, five QA cycles after that, the task gets rolled back - and you've wasted far more time than if you'd given the model a bit more freedom from the start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agents don't need separation of responsibilities. They need unknown variables removed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I work
&lt;/h2&gt;

&lt;p&gt;My approach looks much more modest on paper, but works better in practice.&lt;/p&gt;

&lt;p&gt;I talk to Builder's agent like a senior engineer, with myself in the role of product owner. I don't spell out every step - instead, I spend 5 - 7 minutes discussing the task, write up a single text document (for the agent's convenience and to preserve context), and then the agent works for 2 - 8 hours and gets it done. &lt;/p&gt;

&lt;p&gt;No trillions of slash commands, no three JavaScript specialist agents, no "agent swarms." No decomposing down to the level of individual lines of code. No "plan mode", only planning.&lt;/p&gt;

&lt;p&gt;Sure, the first draft can be rough - the prototype might not be perfect. But 4 out of 5 times my problems come from a bad spec, not "didn't know which package to put the interface in" - meaning the problem is me. Those who work with a strict workflow get a more predictable first result, but spend 30+ minutes planning instead of 5.&lt;/p&gt;

&lt;h2&gt;
  
  
  On tokens - because it matters
&lt;/h2&gt;

&lt;p&gt;Complex workflows burn through a huge number of tokens. Claude Code with its Markdown planning, GitHub tickets, subagents, reading the same files 10 times on every run - that's all tokens, and not a small number.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://nek12.dev/blog/en/builder-open-source-coding-agent-for-engineers" rel="noopener noreferrer"&gt;Builder&lt;/a&gt; setup, context is kept in a single session. Compact is configured to carry over product-level information - decisions, intentions, task context - rather than low-level code details. That's dramatically more token-efficient.&lt;/p&gt;

&lt;p&gt;In theory, with a well-defined multi-agent workflow, simpler phases could be handled by cheaper models. But the problem is that transferring context between sessions and compressing it correctly is a problem the AI agent world still hasn't properly solved - so I don't see the point in fighting the model over it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why all of this is a relic of 2025
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;All the context management strategies currently used in harnesses like Claude Code exist for a specific reason:&lt;/strong&gt; older versions of Opus would lose around 40 IQ points simply because the context filled up. That's where compacts, tickets, and step-by-step planning came from - attempts to work around model limitations at the tooling level.&lt;br&gt;
Models from gpt-5.5 onward &lt;a href="https://developers.openai.com/api/docs/guides/prompt-guidance" rel="noopener noreferrer"&gt;don't need this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think this should be solved at the model level: proper 300K - 1M token context windows, solid native compacts, less sensitivity to context going stale. Not layer after layer of crutches in the harness.&lt;/p&gt;

&lt;p&gt;It's a shame to see people paying serious money, dealing with caching bugs, and building increasingly complex systems on top of fundamental model limitations - when the whole issue is that their approach still carries baggage from a year ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;I'm currently going to try codifying a more structured approach in Builder - attempting to take the best of both worlds: the speed and simplicity of my current approach, plus some of the predictability of a more defined workflow. Whether it works out - we'll see.&lt;/p&gt;

&lt;p&gt;The main point I want to get across: &lt;strong&gt;if you're building a complex agent system, stop and ask yourself - does the task actually need this, or have you just gotten caught up in the tooling?&lt;/strong&gt; Frontier models in 2026 are smarter than the systems people are trying to pack them into.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>agents</category>
      <category>claude</category>
      <category>prompting</category>
    </item>
    <item>
      <title>How Does My Agent Survive 37+ Compactions in a Row? A Deep Dive into Proactive Compact in Builder</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Thu, 09 Apr 2026 11:11:43 +0000</pubDate>
      <link>https://dev.to/nek12/how-does-my-agent-survive-37-compactions-in-a-row-a-deep-dive-into-proactive-compact-in-builder-5cmn</link>
      <guid>https://dev.to/nek12/how-does-my-agent-survive-37-compactions-in-a-row-a-deep-dive-into-proactive-compact-in-builder-5cmn</guid>
      <description>&lt;h2&gt;
  
  
  How Does Compaction Work in Builder?
&lt;/h2&gt;

&lt;p&gt;Today I shipped the 0.10.0 update to Builder CLI, and it landed huge improvements to compaction and cache efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Builder's Compact Is Better Than Native
&lt;/h3&gt;

&lt;p&gt;By default, when you go through onboarding, Builder recommends the local proprietary compact, which is far more detailed than the native one. Native compact can still be selected or configured in the config file if your provider supports it. I recommend local - here's why.&lt;/p&gt;

&lt;p&gt;The native compact algorithm for Claude Code and Codex was &lt;a href="https://x.com/Kangwook_Lee/status/2028955292025962534?s=20" rel="noopener noreferrer"&gt;reverse-engineered&lt;/a&gt; a long time ago, and it's literally 5 - 7 lines of instructions along the lines of: "write a description of the work, idk, make no mistakes." I reverse-engineered the native compact myself to make sure of this and to pull out the best parts.&lt;/p&gt;

&lt;p&gt;The only real advantage of native compact is preserving reasoning traces from previous conversation, but that can be achieved another way (described later). &lt;strong&gt;The local compact in Builder uses a carefully crafted prompt that I've been polishing for several months, covering all the important details that affect the agent's ability to continue working.&lt;/strong&gt; Because of this, the compact can survive literally dozens of sessions without losing the overall task context. I have large refactors that ran autonomously through more than 37 compacts in a row, all night long - without any issues.&lt;/p&gt;

&lt;p&gt;All of Builder's code is open source, so you can read the prompt. But without the harness improvements I also made for the compact, and without the ability to change this prompt in official harnesses, it won't get you very far.&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%2Fv2xtm9uofgoastfuzn5m.webp" 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%2Fv2xtm9uofgoastfuzn5m.webp" alt="Builder running through 22 compacts" width="800" height="469"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Example: Builder ran through 22 compacts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The compact in Builder is also better than the original because &lt;strong&gt;it saves the entire conversation history no matter how many compacts you've had - but the model only sees the fresh history.&lt;/strong&gt; That means you can roll back 19 compacts at any point, start a new conversation, fork it, and go off into a separate branch. On top of that, unlike some providers, Builder preserves cache, so it ends up being much cheaper. This is offset by the fact that my compact is more detailed - but as I've said, Builder's main goal is quality, not price tag or speed. In future versions I plan to optimize the compact so it contains nothing unnecessary and costs less than other providers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Proactive Compact - New in 0.10.0
&lt;/h3&gt;

&lt;p&gt;Beyond the prompt itself, the compact in Builder is proactive. Version 0.10.0 ships the first experimental tool that lets the model decide on its own when to run a compact. &lt;strong&gt;The model in Builder knows it has limited memory, knows how much space is left, knows when to make checkpoints - and gets a notification about remaining context.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enable via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tools]&lt;/span&gt;
&lt;span class="py"&gt;trigger_handoff&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this isn't implemented the way it is in Claude Code, where it freaks the model out and it refuses to work. And not the way Codex does it, where it constantly stops to "take a breather." In Builder, it's just one of the agent's autonomous processes - one that doesn't cause unnecessary pauses or degrade work quality!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proactive compact prevents losing changes, breaking builds, tests, or overwriting your decisions&lt;/strong&gt; in situations where the model runs out of context and compact gets triggered at a bad moment - causing it to forget that it has a file partially edited or a spec half-implemented. In my tests, the model knows on its own when it needs to hand off to the next agent, and prepares the workspace for that, which makes agent-to-agent collaboration across compacts much better.&lt;/p&gt;

&lt;p&gt;Plus, the main agent can pass what's called a &lt;em&gt;letter to the future&lt;/em&gt; to the next agent. The general compact prompt, especially the native one, often doesn't include specific things from the model's internal reasoning - things it knows but hasn't gotten to implement yet. Since reasoning traces aren't saved, my solution lets the model save them itself and pass them forward.&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%2F1tpmzjvc46cpctckxtoq.webp" 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%2F1tpmzjvc46cpctckxtoq.webp" alt="Builder prepared for compact" width="800" height="469"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Builder prepared for compact, passed instructions for saving important info, triggered native compact, and continued working&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-Compact Before Starting a Task
&lt;/h3&gt;

&lt;p&gt;Another UX improvement you won't find in other harnesses: pre-compact before starting a task. The harness constantly checks, whenever you send any command to the agent, whether there's enough context to complete the task - and based on heuristic analysis decides: &lt;strong&gt;should the task run now, or should it compact first and then move on to the next part of the plan?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This way your command is saved directly, doesn't go through an LLM summarization loop, doesn't waste tokens, and the agent starts fresh with clean context and no hallucinations. This lets you just talk to the model without thinking about compaction - the harness figures it all out alongside the model, freeing you from having to manually trigger slash commands or worry about hitting the "dumb zone." This is enabled by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Queues Across Compacts
&lt;/h3&gt;

&lt;p&gt;The last improvement I really love - &lt;strong&gt;queue support across compacts.&lt;/strong&gt; Your prompts, slash commands, and any processes, including background subagents, can survive compacts and transfer ownership to the next agent without any loss.&lt;/p&gt;

&lt;p&gt;For example, you can queue up: right after a task completes - a compact with a custom prompt, then a slash command, and also spin up three background subagents. When the compact finishes, the model automatically receives your prompt, the summary, the message from the previous agent, your slash command, and the output from all three subagents - and picks up from that point as if nothing happened. &lt;strong&gt;No lost information, no stops, no limits on what you can do while the agent is running.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This seriously simplifies the workflow: give the agent a task, immediately schedule a compact, and then tell it to open a pull request and fix the comments in it with background agents for planning and execution. You won't find this in any harness out of the box. Maybe some allow it through hooks, but Builder's advantage is that all of this is implemented natively - with maximum integration out of the box.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://opensource.respawn.pro/builder/quickstart/" rel="noopener noreferrer"&gt;Try Builder here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>builder</category>
      <category>compaction</category>
      <category>claude</category>
      <category>codex</category>
    </item>
    <item>
      <title>I got tired of every existing coding agent. So I built my own - Builder.</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Tue, 07 Apr 2026 15:39:29 +0000</pubDate>
      <link>https://dev.to/nek12/i-got-tired-of-every-existing-coding-agent-so-i-built-my-own-builder-4391</link>
      <guid>https://dev.to/nek12/i-got-tired-of-every-existing-coding-agent-so-i-built-my-own-builder-4391</guid>
      <description>&lt;p&gt;Today I'm excited to show my new project I've been working on for the past 2 months - &lt;a href="https://opensource.respawn.pro/builder/" rel="noopener noreferrer"&gt;Builder&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's a free and open-source coding agent built specifically for professional agentic engineers, and it works with your existing token subscription.&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%2Frehvxrgc4e6ddpsp0zqw.webp" 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%2Frehvxrgc4e6ddpsp0zqw.webp" alt="Image" width="800" height="697"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build your own when CC and Codex exist?
&lt;/h2&gt;

&lt;p&gt;I used Claude Code for over five months, then spent a long time with Codex CLI and the desktop app, Opencode, Junie, and others. And all of these major projects share three core problems for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Opacity
&lt;/h3&gt;

&lt;p&gt;Existing tools are opaque to a professional engineer. In Junie, instead of the actual commands the agent runs, you only see one-line descriptions of what's happening. In Codex, command calls are aggressively hidden and only accessible in transcript mode. And in Claude Code, instead of letting the model work freely through bash commands, it uses its own opaque custom toolset for reading, writing files, and searching.&lt;/p&gt;

&lt;p&gt;Maybe that's fine for people who don't want to look at code and don't want to understand what's going on. &lt;strong&gt;But for an engineer who understands the process and wants to work with the model collaboratively - like with a pair programmer - it's a bad fit.&lt;/strong&gt; And not a single agent wrapper I know of has ever focused on engineers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bloat
&lt;/h3&gt;

&lt;p&gt;Many agentic harnesses pack in a ton of features for vibecoders that only make sense for specific workflows. For day-to-day work, agentic engineers find them more of a hindrance than a help. I'm talking about things like planning mode, 38+ persistent notifications in ClaudeCode, orchestration modes, Swarm, plugins, Explorer agents, and so on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All of this not only gets in the model's way and pollutes the context, it also ruins the user experience&lt;/strong&gt; - and nobody has actually proven the value of these "improvements" compared to straightforward prompting and iterative work with an agent. So I wanted an agent without a planning mode (which only constrains the model), and without the flashy gimmicks that prettify responses while making the model hallucinate (looking at you, Junie).&lt;br&gt;
MCP deserves a special callout - it's been considered an anti-pattern in agentic development for a while now: it pollutes the context with useless tools, and most of them can't be used alongside bash tools.&lt;/p&gt;

&lt;p&gt;All of this gets replaced by more effective strategies for engineers who direct the model as a coding agent, rather than delegating their own thinking to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instability and closed nature
&lt;/h3&gt;

&lt;p&gt;These kinds of harnesses often have their own opinions on how things should be done - and those opinions keep changing. With every update you can expect your workflow to change in some way too. It's unclear what's going on under the hood: what the system prompt looks like, how compaction works, missing settings, no real way to switch models or providers. You can't tell what ends up in the context, when cache invalidation happens, or what changed in the latest update that might have silently broken your workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I simply got fed up with sitting down to work one fine day only to find my agent suddenly hallucinating - and it turning out to be yet another hidden bug in the harness that invalidated something under the hood and was feeding the model bad data.&lt;/strong&gt; So I decided to write my own harness.&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%2F6488idujvbzmelz0rhfn.webp" 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%2F6488idujvbzmelz0rhfn.webp" alt="Image" width="800" height="697"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Builder CLI's background shell management&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes Builder better
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Collaborative architecture work
&lt;/h4&gt;

&lt;p&gt;Many harnesses don't let models work together with the user on product and architectural design. One of the key things in my wrapper is giving the agent the ability to ask a question whenever it runs into a problem, a blocker, or finds something in the code it wants to refactor or improve.&lt;/p&gt;

&lt;p&gt;In practice, this results in dramatically better output quality: &lt;strong&gt;agents no longer try to bulldoze their way through every problem&lt;/strong&gt; and end up implementing terrible, broken solutions or trashing the architecture. Instead, they ask a question and solve the problem together with the user.&lt;/p&gt;

&lt;h4&gt;
  
  
  Instrumented workflow instead of blind trust
&lt;/h4&gt;

&lt;p&gt;The second key thing is evolving agentic harnesses beyond a simple loop where the agent spins and is just trusted to do everything right - toward a clearly instrumented workflow that prevents the agent from making dumb mistakes. Many agents start hallucinating - and that's normal for the current state of models. Even people forget things, lose context, miss small details that can actually matter. &lt;strong&gt;That's why in Builder, a separate parallel agent always watches over your agent's work, checking its output for quality and compliance with your project's rules.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Quality over token savings
&lt;/h4&gt;

&lt;p&gt;Large agent wrappers right now are optimized for quick fixes, minimal viable solutions, conserving context and tokens. Essentially, you're paying with output quality and your own time spent cleaning up after the model - all to save a few thousand tokens and run 20% faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Builder's design, I'm focused first and foremost on output quality: proper architecture, code safety, performance, first-principles solutions&lt;/strong&gt; - not the hacks that are currently the default for virtually every agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's already there
&lt;/h2&gt;

&lt;p&gt;I've already fully replaced Codex CLI with Builder and don't open it anymore. I use it for everything, including work tasks.&lt;/p&gt;

&lt;p&gt;Current features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent loop equivalent to Codex CLI&lt;/li&gt;
&lt;li&gt;Background tasks, background agents, subagents, orchestration&lt;/li&gt;
&lt;li&gt;Supervisor - a parallel agent that continuously watches the model and improves its results&lt;/li&gt;
&lt;li&gt;Auto and manual context compaction in multiple modes; native Codex compaction is supported via settings, but I've long since switched to Builder's own compaction, which is several times better in quality&lt;/li&gt;
&lt;li&gt;Two display modes: compact and detailed. In detailed mode, full information about what the model did is available - something like a transcript view, but with far more data than in Codex or Claude Code&lt;/li&gt;
&lt;li&gt;Questions from the model to the user&lt;/li&gt;
&lt;li&gt;Model steering and prompt queuing&lt;/li&gt;
&lt;li&gt;Native image and PDF viewing&lt;/li&gt;
&lt;li&gt;Native web search&lt;/li&gt;
&lt;li&gt;Prompt and session history&lt;/li&gt;
&lt;li&gt;Notifications (including system notifications) when the model finishes&lt;/li&gt;
&lt;li&gt;agents.md standard support&lt;/li&gt;
&lt;li&gt;Agent Skills support&lt;/li&gt;
&lt;li&gt;Code syntax highlighting&lt;/li&gt;
&lt;li&gt;Custom slash commands and many built-in ones&lt;/li&gt;
&lt;li&gt;Conversation editing and forking&lt;/li&gt;
&lt;li&gt;Lots of keyboard shortcuts&lt;/li&gt;
&lt;li&gt;Toggles for OpenAI: turbo mode, model verbosity, thinking mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm currently actively working on worktree management and also laying the groundwork for a native desktop app.&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%2Fw3580scbzmrj4edk49xc.webp" 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%2Fw3580scbzmrj4edk49xc.webp" alt="Image" width="800" height="621"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Finished the code review&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What won't be there
&lt;/h2&gt;

&lt;p&gt;So you can decide whether Builder is right for you, here's what conflicts with its philosophy and won't be implemented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP support&lt;/strong&gt; - pollutes the context and is incompatible with bash tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planning mode&lt;/strong&gt; - a legacy leftover from Anthropic's models that constrains the model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI bells and whistles&lt;/strong&gt; for people who aren't doing serious programming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Micro-compaction and anything that invalidates caches&lt;/strong&gt; - it costs a lot of money&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sandbox&lt;/strong&gt; - ClaudeCode, Codex, and Junie still don't have proper sandboxing, and I've been using them without it for a long time. As a professional engineer I simply don't end up in situations where the model deletes something. Safety is handled through proper prompting right now; file editing is configurable and access can be restricted. Sandboxing won't save you on any OS from destructive actions - so I decided not to put on the tinfoil hat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebFetch or equivalent&lt;/strong&gt; - there's already a CLI, Markdown standards, and skills for that. No need for an extra tool that hands the model an LLM-processed Medium article. Just use any CLI script like jina.ai if the agent needs internet access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini and Anthropic subscriptions&lt;/strong&gt; - because those are now illegal for us.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Current state
&lt;/h2&gt;

&lt;p&gt;The product is quite ready to use, but I realize I focused primarily on the features I use myself to start dogfooding it as quickly as possible. So I haven't tested the agent on Windows or Linux, haven't tested it with a bare API key, and haven't implemented support for other model providers.&lt;/p&gt;

&lt;p&gt;Please report any issues and missing features - open an issue on GitHub and I'll do my best to address everything. Any feedback is very welcome.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opensource.respawn.pro/builder/quickstart/" rel="noopener noreferrer"&gt;Getting started guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd also love some stars on &lt;a href="https://github.com/respawn-app/builder" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agenticcoding</category>
      <category>opensource</category>
      <category>codingagent</category>
      <category>builder</category>
    </item>
    <item>
      <title>I switched to a tiling window manager on macOS. Full breakdown: Aerospace, Amethyst, and Yabai</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Fri, 27 Mar 2026 11:08:25 +0000</pubDate>
      <link>https://dev.to/nek12/i-switched-to-a-tiling-window-manager-on-macos-full-breakdown-aerospace-amethyst-and-yabai-1c88</link>
      <guid>https://dev.to/nek12/i-switched-to-a-tiling-window-manager-on-macos-full-breakdown-aerospace-amethyst-and-yabai-1c88</guid>
      <description>&lt;p&gt;A week ago I ran into a problem. I started using more and more parallel workflows and more and more terminal tabs. I use Ghostty, but keeping up with that many tabs - even with its pretty solid UX - became simply impossible. I had 3 - 4 windows open with two to four tabs each running in parallel, and the entire workflow depended purely on window order and which one was currently in focus. &lt;strong&gt;For working with agents across multiple projects and multiple clients simultaneously - that approach just doesn't work anymore.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I tried Cmux - a new terminal emulator built on top of Ghostty - but it's still rough around the edges: no native tabs, ugly UI, no blur. The concept is solid, but the implementation needs more time. That said, I know plenty of people who are very happy with cmux - if you're not as picky about UI/UX as I am, you'll really like its workspace-based model: each workspace is a separate tab with a single terminal session. You could also try Codex App, but since I have my own agent, I didn't want to switch to a desktop Electron app lacking the features I need just because I couldn't manage my windows properly. The solution for me turned out to be tiling window managers, which I'd been wanting to try for a long time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a tiling window manager
&lt;/h2&gt;

&lt;p&gt;A tiling window manager is something that came from Linux. It's a non-traditional window management system: instead of manually dragging and resizing windows, the window manager does all of that for you using various algorithms when a window is opened or its boundaries change. &lt;strong&gt;You say: I have X windows, I want them to fill the entire screen in the most convenient arrangement without overlap - and the manager does it for you.&lt;/strong&gt; A tiling WM simply won't allow a single pixel of empty space on the screen: it constantly resizes windows so they stay snapped together with a sensible distribution.&lt;/p&gt;

&lt;p&gt;On Mac there are only three options, because macOS has serious limitations - you can't replace the window manager entirely like you can on Linux. Your experience will always be second-rate compared to Linux, but there are working solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main macOS bug you need to know about
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;There's a major bug in macOS that will seriously affect your workflow.&lt;/strong&gt; That's exactly why I say the experience is second-rate. All native tabs in macOS apps are treated as new windows by window managers - there's no way to tell whether it's a tab or a window. macOS always sends a signal that a new window was opened, even if what actually happened was a new tab opening in an existing window. This means your tiling WM will treat all open tabs as new windows, and empty space will keep multiplying on your screen depending on how many tabs you have open.&lt;/p&gt;

&lt;p&gt;There's no fix for this, and any app using native macOS tabs will behave badly in a tiling WM. Ghostty uses native tabs - which is exactly why I had to ditch the tab concept in my terminal entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  My current workflow
&lt;/h2&gt;

&lt;p&gt;I disabled tabs in Ghostty and even the window affordances, and moved as fully as possible to keyboard-driven control - part of a broader focus on moving away from the mouse. Right now I can't even see the close and minimize buttons in my terminal. Other apps keep them - like Android Studio, which spams modal windows and needs to be maximized to full screen, or Telegram, which I want to float on top of everything.&lt;/p&gt;

&lt;p&gt;For the terminal, I open, close, and minimize windows with hotkeys that are flexibly configured in Ghostty. &lt;strong&gt;The screen splitting logic works like this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I open the first terminal - it takes up the full screen.&lt;/li&gt;
&lt;li&gt;I open a second one via &lt;code&gt;CMD+N&lt;/code&gt; - the space immediately splits in half, each terminal takes exactly 50%.&lt;/li&gt;
&lt;li&gt;I open a third window - the half where focus was splits again. The new terminal opens in the same folder I was in at the moment of opening.&lt;/li&gt;
&lt;li&gt;Each additional window divides the space using the golden ratio principle (Binary Space Partitioning): depending on whether there's more horizontal or vertical space in that particular section of the screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This lets you spawn child windows from a single window in the same directory and launch things like Vim or multiple copies of a dev agent inside them.&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%2Fnek12.dev%2Fmedia%2Ftilingwm-1-1774604800.webp" 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%2Fnek12.dev%2Fmedia%2Ftilingwm-1-1774604800.webp" alt="Only up to 1/8th here" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At some point, as you can see in the screenshot above, I'm already close to the limit of what my brain can handle. So I separate workflows by project using standard macOS Spaces: a dedicated Space for my agent, for Respawn, for each client project - and one extra for general tasks.&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%2Fnek12.dev%2Fmedia%2Ftilingwm-2-1774605402.webp" 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%2Fnek12.dev%2Fmedia%2Ftilingwm-2-1774605402.webp" alt="Spaces" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three tiling WM options for macOS
&lt;/h2&gt;

&lt;p&gt;One thing upfront: all the configuration was done for me by my assistant OpenClaw, not by me. Tiling window managers are a pretty niche product aimed at developers - configuration is done via YAML files in various folders or through bash macros for hotkey management. I would have taken forever figuring it out on my own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aerospace
&lt;/h3&gt;

&lt;p&gt;The first option I wanted to try. It has a fairly convenient YAML configuration, though I still couldn't be bothered to dig into it. &lt;strong&gt;It's a clone of the i3 window manager from Linux&lt;/strong&gt; - those who know, know.&lt;/p&gt;

&lt;p&gt;The main problem for me: it's manual. Opening any window doesn't trigger an automatic relayout of the rest. I needed the window manager to handle all windows for me automatically, so I had to drop Aerospace.&lt;/p&gt;

&lt;p&gt;That said, it's stable, highly configurable, it has handy workspace functionality with fast switching and even some semblance of a user interface on Mac. Aerospace has window groups, each with its own layout mode. One group is split vertically into columns, another is split horizontally in half, a third isn't split at all. You manage them manually through hotkeys. The concept is really cool if you're willing to learn how to work with them and memorize all the necessary hotkeys. &lt;strong&gt;If you've already used a tiling WM on Linux - I'd recommend Aerospace first, it's the closest thing to a proper professional window manager experience on macOS.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Amethyst
&lt;/h3&gt;

&lt;p&gt;A completely different approach - more simplified and user-friendly. &lt;strong&gt;Amethyst has a full graphical interface&lt;/strong&gt; via an icon in the menu bar, where you can configure everything, though YAML configs are also supported and take priority.&lt;/p&gt;

&lt;p&gt;For me the biggest issue was that it doesn't support mouse control - everything is keyboard only. Sometimes my layout is complex enough that I just want to drag a window with the mouse instead of fumbling with arrow key combos. It also has poor support for modal windows and dialogs - they're treated as separate windows, and when a tiny "Do you want to send a voice message?" popup appears in Telegram, the entire screen gets wrecked: all windows shuffle around to make room for it. So I just stopped using it. &lt;strong&gt;But I'd recommend trying Amethyst first if you want to get into the world of tiling WMs&lt;/strong&gt; - the GUI makes the entry point much gentler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Yabai
&lt;/h3&gt;

&lt;p&gt;The last contender, and the one I settled on. As far as I know, it's the oldest tiling WM project on macOS. It supports the Fibonacci layout (Binary Space Partitioning) that I wanted, and generally has all the features of the previous two combined.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two downsides.&lt;/strong&gt; First, Yabai has no graphical interface at all - all configuration is through bash scripts and shell commands, it runs as a background service, and it's started and stopped via the terminal. On one hand, that's a barrier to entry; on the other, I just told my OpenClaw to set up the configuration I needed, and it set everything up in 10 seconds. A one-time setup overhead is much better than tolerating some other persistent bug.&lt;/p&gt;

&lt;p&gt;Second, Yabai has no built-in hotkey support - you need a separate utility that translates hotkeys into shell commands. OpenClaw recommended &lt;code&gt;skhd&lt;/code&gt;. I honestly don't even care what that is - I just asked the agent to set everything up and sent it a list of the hotkeys I needed, and it was all ready in a minute. An unexpected bonus of yabai was that it works really well with AI agents: my Gemini already knew absolutely everything about its configuration options, so setting it up from a single voice message in Telegram took 10 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yabai has a lot going for it.&lt;/strong&gt; Beyond supporting all the features I needed, it has a handy visual highlight when dragging windows with the mouse: you can immediately see exactly how the space will be split and where the window will end up. This makes mouse-based rearranging so simple and intuitive that, I'll admit, I now often skip the hotkeys and just use the mouse.&lt;/p&gt;

&lt;p&gt;Additionally, Yabai supports extended features when you partially disable macOS system security - you can remove shadows, rounded corners, and the slow transition animations between Spaces. &lt;strong&gt;The main advantage - it uses native macOS Spaces instead of its own&lt;/strong&gt;, which means support for different Spaces on different monitors. All the other options require you to disable this feature or they break. I didn't install the system extensions myself: every update requires a reboot and re-disabling security, which is more annoying than the slow animations.&lt;/p&gt;

&lt;p&gt;Yabai can also be disabled with a single shell command - handy when you want to play games or let someone else use the computer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you have an agent that can configure everything for you and you want the best option with maximum control - go with Yabai.&lt;/strong&gt; But I wouldn't install it just to try it out: by default it ships with no configuration and barely works until you create the config files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Will I keep using a tiling window manager? Absolutely - at least until the problem of managing dozens of windows while working with agents gets solved. Maybe cmux will get polished up and I'll switch to that, but every day I like tiling WM more, and I notice I'm using the mouse less and less. &lt;strong&gt;I'll never go back to native macOS features like Stage Manager&lt;/strong&gt; - objectively, that thing is unusable.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>tilingwm</category>
      <category>yabai</category>
      <category>aerospace</category>
    </item>
    <item>
      <title>Case Study: How I Sped Up Android App Start by 10x</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Thu, 29 Jan 2026 13:48:44 +0000</pubDate>
      <link>https://dev.to/nek12/case-study-how-i-sped-up-android-app-start-by-10x-1c03</link>
      <guid>https://dev.to/nek12/case-study-how-i-sped-up-android-app-start-by-10x-1c03</guid>
      <description>&lt;p&gt;At my last job, we had a problem with long load times, especially for the first launch of our Android app. ~18% of people were leaving before the app even opened. I was tasked with fixing this situation and achieving an app load time of &lt;strong&gt;under 2 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At first glance, the task seemed impossible, because the app on startup hits the backend more than four times, registers a new anonymous user, exchanges keys for push notifications, initializes three different analytics SDKs, downloads remote configuration, downloads feature flags, downloads the first page of the home screen feed, downloads several videos that play on app start during feed scrolling, initializes multiple ExoPlayers at once, sends data to Firebase, and downloads assets (sounds, images, etc.) needed for the first game. How can you fit such a huge volume of work into less than two seconds?!&lt;/p&gt;

&lt;p&gt;After two weeks of meticulous work, I finally did it! And here's a complete breakdown of how I made it happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit and Planning
&lt;/h2&gt;

&lt;p&gt;I conducted a full audit of the codebase and all logic related to app startup, profiled everything the app does on start using Android Studio tooling, ran benchmarks, wrote automated tests, and developed a complete plan for how to achieve a 2-second load time without sacrificing anything I described above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementing all of this took just one week&lt;/strong&gt; thanks to the fact that I planned everything out, and the team could parallelize the work among several developers.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  1. Switching from Custom Splash Screen to Android Splash Screen API
&lt;/h3&gt;

&lt;p&gt;We switched from a custom splash screen, which was a separate Activity, to the official Android Splash Screen API and integrated with the system splash screen. I've written many times in my posts and always say in response to questions, or when I see developers trying to drag in a custom Activity with a splash screen again, or some separate screen in navigation where they load something: &lt;strong&gt;this is an antipattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Our Splash Activity contained a huge ViewModel with thousands of lines, had become a God Object where developers just dumped all the garbage they needed to use, and forced all the rest of the app logic to wait while it loaded. &lt;strong&gt;The problem with custom Activities is that they block the lifecycle, navigation, and take time to create and destroy.&lt;/strong&gt; Plus, they look to the user like a sharp, janky transition with the system animation that Android adds when transitioning between Activities. This creates a user experience that increases not only the actual load time, but also how it's &lt;strong&gt;perceived&lt;/strong&gt; by the user.&lt;/p&gt;

&lt;p&gt;We completely removed the Splash Activity and deleted all two thousand lines of code it had. We switched to the Splash Screen API, which allowed us to integrate with the system Splash Screen that Android shows starting from version 8, add an amazing animation there, and our own custom background.&lt;/p&gt;

&lt;p&gt;Thanks to this, because we were no longer blocking data loading for the main screen with this custom Activity, we got a significant boost in actual performance from this change. &lt;strong&gt;But the biggest win was that people stopped perceiving the app loading as actual loading.&lt;/strong&gt; They just saw a beautiful splash animation and thought that their launcher was organizing the app start so nicely for them. And even if they thought the app was taking a long time to load, they were more likely to think it was because of the system or because of the load on their phone (and most often - that's exactly what it is), and not because the app is lagging, because the system Splash Screen looks like a part of the OS, not of the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Developing a Startup Background Task System
&lt;/h3&gt;

&lt;p&gt;In order to get rid of this huge Splash Activity, I needed to develop a custom system of startup jobs that executed when the app launches. In pretty much any app there are a lot of things that need to be done on startup: asynchronous remote config updates, reading something, initializing SDKs, feature flags, sending device or session analytics data, loading services, checking background task status, checking push notifications, syncing data with the backend, authorization.&lt;/p&gt;

&lt;p&gt;For this, I made an integration with DI, where &lt;strong&gt;a smart Scheduler collects all jobs from all DI modules in the app and efficiently executes them with batching, retry, and error handling, sending analytics, and measuring the performance of all this.&lt;/strong&gt; We monitored which jobs took a lot of time in the background afterwards or which ones failed often, diagnosed and fixed issues.&lt;/p&gt;

&lt;p&gt;Another architectural advantage of the system I developed is that developers no longer had to dump everything in one pile in the Splash Activity ViewModel. They got access to registering background jobs from anywhere in the app, from any feature module, for example. &lt;strong&gt;I believe that problems with app behavior aren't a question of developer skill, it's a question of the system&lt;/strong&gt;. This way, I helped the business by creating an efficient system for executing work on startup that's fully asynchronous and scales to hundreds of tasks, many years into the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Switching to Reactive Data Loading Model
&lt;/h3&gt;

&lt;p&gt;We historically used old patterns of imperative programming and one-time data loading. This was probably the most difficult part of the refactoring. But fortunately, we didn't have that much tied to imperative data loading specifically on app startup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;I migrated to &lt;strong&gt;asynchronous data loading using Jetpack DataStore.&lt;/strong&gt; They have a nice asynchronous API with coroutine support, that is non-blocking, and this significantly sped up config loading, user data loading, and auth tokens.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, I migrated to a reactive user management system. This was the hardest part at this stage. Our user object was being read from preferences on the main thread, and if it didn't exist, every screen had to access the Splash Screen to block all processes until a user account was created or retrieved from the backend and the tokens were updated.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;I redesigned this system to an asynchronous stream of updates for the user account, which automatically starts loading them on first access as early as possible on app startup.&lt;/strong&gt; And changed all the logic from blocking function calls that get the user to observing this stream.&lt;/p&gt;

&lt;p&gt;Thus, also thanks to the fact that we use &lt;a href="https://github.com/respawn-app/FlowMVI" rel="noopener noreferrer"&gt;FlowMVI&lt;/a&gt; - a reactive architecture, &lt;strong&gt;we got the ability to delegate loading status display to individual elements on the screen.&lt;/strong&gt; For example, the user avatar &amp;amp; sync status on the main screen loaded independently while the main content was loading asynchronously, and didn't block the main content from showing. And also, for example, push registration could wait in the background for the User ID to arrive from the backend before sending the token, instead of blocking the entire loading process.&lt;/p&gt;

&lt;p&gt;In the background, we were also downloading game assets: various images and sounds, but they were hidden behind the Splash screen because they were required for the first game launch. But we didn't know how many videos a person would scroll through before they decided to play the first game, so we might have plenty of time to download these assets asynchronously and block game launch, not app launch. Thus, the total asset load time could often be decreased down to 0 just by cleverly shifting not loading, but &lt;strong&gt;waiting&lt;/strong&gt;. I redesigned the asset loading architecture to use the newly developed background job system, and the game loading logic itself to asynchronously wait for these assets to finish downloading, using coroutines.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Working with the Backend
&lt;/h3&gt;

&lt;p&gt;Based on my profiling results, we had very slow backend calls, specifically when loading the video feed on the main screen.&lt;br&gt;
I checked the analytics and saw that most of our users were using the app with unstable internet connections. This is a social network, and people often watched videos or played games, for example, on the bus, when they had a minute of free time.&lt;/p&gt;

&lt;p&gt;I determined from benchmark results that our main bottleneck wasn't in the backend response time, but in how long data transfer took.&lt;/p&gt;

&lt;p&gt;I worked with the backend team, developed a plan for them and helped with its execution. We switched to HTTP/3, TLS 1.3, added deflate compression, and implemented a new schema for the main page request, which reduced the amount of data transferred by over 80%, halved TCP connection time, and increased the data loading speed by ~2.3x.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Other Optimizations
&lt;/h3&gt;

&lt;p&gt;I also optimized all other aspects, such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Code precompilation: configured Baseline Profiles, Startup Profiles, Dex Layout Optimizations. Net ~300ms win, but only on slow devices and first start;&lt;/li&gt;
&lt;li&gt;Switched to lighter layouts in Compose to reduce UI thread burst load;&lt;/li&gt;
&lt;li&gt;Made a smart ExoPlayer caching system that creates them asynchronously on demand and stores them in a common pool;&lt;/li&gt;
&lt;li&gt;Implemented a local cache for paginated data, which allowed us to instantly show content, with smart replacement of still-unviewed items with fresh ones from the backend response. Huge win for UX.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also, on another project, in addition to this, I managed to move analytics library loading, especially Firebase, to a background thread, which cut another ~150 milliseconds there, but more on that in future posts I will send to the newsletter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Thus, I was able to reduce the app's cold start time by more than 10 times.&lt;/strong&gt; The cold first app start went from 17 seconds to ~1.7.&lt;/p&gt;

&lt;p&gt;After that, I tracked the impact of this change on the business, and the results were obvious. &lt;strong&gt;Instead of losing 18% of our users before onboarding started, we started losing less than 1.5%.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Optimizing app startup time is quite delicate work and highly personalized to specific business needs and existing bottlenecks. Doing all this from scratch can take teams a lot of time and lead to unexpected regressions in production, so I now help teams optimize app startup in the format of a short-term audit. After analysis (2-5 days), the business gets a clear point-by-point plan that can be immediately given to developers/agencies + all the pitfalls to pay attention to. I can also implement the proposed changes as needed.&lt;/p&gt;

&lt;p&gt;If you want to achieve similar results, send your app name to &lt;a href="mailto:me@nek12.dev"&gt;me@nek12.dev&lt;/a&gt;, and I'll respond with three personalized startup optimization opportunities for your case.&lt;/p&gt;

</description>
      <category>android</category>
      <category>performance</category>
      <category>optimization</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>AGP 9.0 is Out, and Its a Disaster. Heres Full Migration Guide so you dont have to suffer</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Tue, 20 Jan 2026 13:20:25 +0000</pubDate>
      <link>https://dev.to/nek12/agp-90-is-out-and-its-a-disaster-heres-full-migration-guide-so-you-dont-have-to-suffer-p4f</link>
      <guid>https://dev.to/nek12/agp-90-is-out-and-its-a-disaster-heres-full-migration-guide-so-you-dont-have-to-suffer-p4f</guid>
      <description>&lt;p&gt;Yesterday I migrated a big 150,000-line project from AGP 8 to AGP 9. This was painful. &lt;strong&gt;This is probably the biggest migration effort that I had to undergo this year.&lt;/strong&gt; So, to save you from the pain and dozens of wasted hours that I had to spend, I decided to write a full migration guide for you. &lt;/p&gt;

&lt;p&gt;Be prepared, this migration will take some time, so you better start early. With AGP 9.0 already being in release, Google somehow expects you to already start using it yesterday. And they explicitly state that &lt;strong&gt;many of the existing APIs and workarounds that you can employ right now to delay the migration will stop working in summer 2026.&lt;/strong&gt; So for big apps, you don't have much time left.&lt;/p&gt;

&lt;p&gt;Before we start, please keep in mind that despite AGP somehow being in production release, &lt;strong&gt;a lot of official plugins, such as the Hilt plugin and KSP, do not support AGP 9.0.&lt;/strong&gt; If you use Hilt or KSP in your project, you will not be able to migrate without severe workarounds for now. If you're reading this later than January 2025, just make sure to double-check if Hilt and KSP already have shipped AGP 9.0 support.&lt;/p&gt;

&lt;p&gt;If you're not blocked and still here, here is what you need to do to migrate your KMP project to AGP 9.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  The biggest migration point: Dropped support for build types
&lt;/h2&gt;

&lt;p&gt;Previously, we didn't have build types on other platforms in KMP, but Android still had them. And in my opinion, they are one of the best features for security and performance that we had, but now they will not be supported and there is no replacement for them. You literally have to remove all build type-based code.&lt;/p&gt;

&lt;p&gt;At first glance, this seems like a small problem, because teams usually don't split a lot of code between source sets. It usually revolves around some debug performance and security checks. But there is a hidden caveat. &lt;strong&gt;BuildConfig values will stop working completely, because they are using the build types under the hood.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I had in my codebase dozens and dozens of places where I had a static top-level variable &lt;code&gt;isDebuggable&lt;/code&gt;, delegating to &lt;code&gt;BuildConfig.DEBUG&lt;/code&gt;, which I was checking and using a lot to add some extra rendering code, debugging, logging code, and to disable many of the security checks that the app had, which were only applicable on release.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I was using it as a static variable instead of something like &lt;code&gt;context.isDebuggable&lt;/code&gt; is because the R8, when optimizing the release build of the app, would be able to remove all of that extra debug code&lt;/strong&gt; without the need to create extra source sets, etc. This works well for KMP, where release and debug split wasn't fully supported in the IDE for a long time. &lt;/p&gt;

&lt;p&gt;But now this is completely impossible. This is a huge drawback for me personally, because &lt;strong&gt;I had to execute a humongous migration to replace all of those static global variable usages with a DI-injected interface,&lt;/strong&gt; which was implemented using still build configuration, but in the application module, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in common domain KMP module&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;AppConfiguration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;debuggable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;backendUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;deeplinkDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;deeplinkSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;deeplinkPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// in android app module&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;AndroidAppConfiguration&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AppConfiguration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;debuggable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BuildConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEBUG&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;backendUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BuildConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BACKEND_URL&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;deeplinkDomain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BuildConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEEPLINK_DOMAIN&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;deeplinkSchema&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BuildConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEEPLINK_SCHEMA&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;deeplinkPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BuildConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEEPLINK_PATH&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may result in a significant refactor, because I personally used the static &lt;code&gt;isDebuggable&lt;/code&gt; flag in places where context/DI isn't available. So, I had to sprinkle some terrible hacks with a global DI singleton object retrieval just to make the app work and then refactor the code.&lt;/p&gt;

&lt;p&gt;When you're done with this step, you must have &lt;strong&gt;0 usages of BuildConfig, build types, or manifest placeholders in library modules&lt;/strong&gt;. Note that codegen for build-time constants is still fine, just not per-build-type / Android one. You can create a custom Gradle task if you want that will generate a Kotlin file for you in ~20 lines.&lt;/p&gt;

&lt;p&gt;I know that devs love &lt;code&gt;BuildConfig.DEBUG&lt;/code&gt; a lot, and I also used it to manage deep link domains, backend URL substitution for debug and release builds, and all of that had to be refactored, which is why I urge you to stop using such code pattern with these static &lt;code&gt;isDebuggable&lt;/code&gt; flags right now. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Also avoid using &lt;code&gt;Context.isDebuggable&lt;/code&gt; boolean property, because that's a runtime check which can be overridden by fraudulent actors, so it isn't reliable.&lt;/strong&gt; Don't use it for security reasons. Remember - debug code should only be included in debug &lt;strong&gt;builds&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove all NDK and JNI code from library modules
&lt;/h2&gt;

&lt;p&gt;The next step you have to take is remove all the NDK and JNI code that you have in library modules. I have a couple of places where I need to run some C++ code in my app, and those were previously located in the Android source set of the KMP library module where they were needed, because Apple source set didn't need that native code, but Android did. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An &lt;a href="https://issuetracker.google.com/issues/439746703#comment6" rel="noopener noreferrer"&gt;official statement&lt;/a&gt; from Google is that NDK execution in library modules and C++ code execution and JNI will not be supported at all since AGP 9.0.&lt;/strong&gt; So now, the only way you can preserve that code is if you move it to the application module. Again, that is something that is a huge drawback for me, but because Google didn't give us any opportunities and didn't want to listen, you have to comply if you do not want to get stuck on a deprecated AGP forever. &lt;/p&gt;

&lt;p&gt;So before you even try to migrate to AGP 9.0, &lt;strong&gt;make sure you create an interface abstraction in your library module that will act as a proxy for all your NDK-enabled code.&lt;/strong&gt; Then the implementation of that interface can live in the application module along with all the C++ code and inject the implementation into the DI graph so that your library module in the KMP code can just use that interface. At least this is what I did. This is the simplest solution to the problem, but if you know a better one, let me know.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual migration: Remove the old Kotlin Android plugin
&lt;/h2&gt;

&lt;p&gt;Now we are finally finishing up with all the refactorings and approaching the actual migration. Start by removing the old Kotlin Android plugin. I had convention plugins set up, so it was reasonably easy for me to do, and migrate it to the new plugin. Read this &lt;a href="https://developer.android.com/build/migrate-to-built-in-kotlin" rel="noopener noreferrer"&gt;docs page&lt;/a&gt; for what exactly to do. &lt;/p&gt;

&lt;p&gt;When you remove it, also add the new plugin for Android Kotlin Multiplatform compatibility: &lt;code&gt;com.android.kotlin.multiplatform.library&lt;/code&gt;. This is because your build will stop working and we need to migrate to the new DSL, which is only provided with this new plugin.&lt;/p&gt;

&lt;p&gt;To fix gradle sync, do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Update from the deprecated Android top level DSL &lt;code&gt;android { }&lt;/code&gt; AND the deprecated &lt;code&gt;kotlin.androidLibrary {}&lt;/code&gt; DSL to the new unified &lt;code&gt;kotlin.android { }&lt;/code&gt; DSL.&lt;/strong&gt; You should be able to copy-paste all of your previous configuration, like compile SDK, minimum SDK, and all of the other Android setup options which you previously had in the top-level Android block, and merge it with the code that you previously had in the &lt;code&gt;kotlin.androidLibrary&lt;/code&gt; KMP setup. So now it's just a single place. Note that library modules no longer support target SDK, which will only be governed by the app module.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;     id("sharedBuild")
     id("detektConvention")
     kotlin("multiplatform")
&lt;span class="gd"&gt;-    id("com.android.library")
&lt;/span&gt;&lt;span class="gi"&gt;+    id("com.android.kotlin.multiplatform.library")
&lt;/span&gt; }
&lt;span class="err"&gt;
&lt;/span&gt; kotlin {
     configureMultiplatform(this)
 }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-android {
-    configureAndroidLibrary(this) 
-}
-
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See how I had an extension function from my convention plugin, &lt;code&gt;configureAndroidLibrary&lt;/code&gt;, and removed it? We can now completely ditch it. Everything will be inside the &lt;code&gt;kotlin&lt;/code&gt; block. (&lt;code&gt;configureMultiplatform&lt;/code&gt; in example above).&lt;/p&gt;

&lt;p&gt;Next up, let's update the said "configure multiplatform" function. This is based on &lt;a href="https://developer.android.com/kotlin/multiplatform/plugin#migrate" rel="noopener noreferrer"&gt;this official doc page&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-    if (android) androidTarget {
-        publishLibraryVariants("release")
&lt;/span&gt;&lt;span class="gi"&gt;+    if (android) android {
+        namespace = this@configureMultiplatform.namespaceByPath()
+        compileSdk = Config.compileSdk
+        minSdk = Config.minSdk
+        androidResources.enable = false
+        lint.warning.add("AutoboxingStateCreation")
+        packaging.resources.excludes.addAll(
+            listOf(
+                "/META-INF/{AL2.0,LGPL2.1}",
+                "DebugProbesKt.bin",
+                "META-INF/versions/9/previous-compilation-data.bin",
+            ),
+        )
+        withHostTest { isIncludeAndroidResources = true }
&lt;/span&gt;         compilerOptions {
             jvmTarget.set(Config.jvmTarget)
             freeCompilerArgs.addAll(Config.jvmCompilerArgs)
         }
&lt;span class="gi"&gt;+        optimization.consumerKeepRules.apply {
+            publish = true
+            file(Config.consumerProguardFile)
+        }
&lt;/span&gt;     }
     // ... 
     sourceSets {
         commonTest.dependencies {
             implementation(libs.requireBundle("unittest"))
         }
&lt;span class="gd"&gt;-        if (android) androidUnitTest {
-            dependencies {
&lt;/span&gt;&lt;span class="gi"&gt;+        if (android) {
+            val androidHostTest = findByName("androidHostTest")
+            androidHostTest?.dependencies {
&lt;/span&gt;                 implementation(libs.requireLib("kotest-junit"))
             }
         }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In summary, what has changed here is that we had an &lt;code&gt;androidTarget&lt;/code&gt; block which contained a small portion of our library module setup. That was replaced by the &lt;code&gt;android&lt;/code&gt; block (not top-level, I know, confusing). And now we just put everything from our previous Android top-level block in here, and we removed the target SDK configuration, which was previously available here. Some syntax changed a bit, but this is only because I'm using convention plugins, so they don't have all the same nice DSLs that you would have if you just configured this manually in your target module. &lt;/p&gt;

&lt;p&gt;As you see, I put the new packaging excludes workarounds that have been there for ages into this new place. I moved the Lint warning configuration (that was used by Compose). &lt;strong&gt;Don't forget to disable Android resources explicitly in this block because most of your KMP modules will not actually need Android resources,&lt;/strong&gt; so I highly recommend you enable them on-demand in your feature modules where you actually need them. This will speed up the build times. &lt;/p&gt;

&lt;p&gt;You can also see that instead of &lt;code&gt;androidUnitTest&lt;/code&gt; configuration that we had, we just have &lt;code&gt;androidHostTest&lt;/code&gt;, which is basically the same Android unit tests you're used to. Host means that they run on the host machine, which is your PC. This is just a small syntax change, annoying but bearable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't forget to apply the consumer keep rules here,&lt;/strong&gt; because a widely used best practice is to keep the consumer rules that are used by a particular library module together in the same place instead of dumping all of that into the application module. I was personally not happy about moving all of my consumer rules to the ProGuard rules file of the application module, so I just enabled consumer keep rules for every library module I have. This is especially useful for stuff like network modules, database modules, where I still have custom keep rules, and for modules which are supposed to use NDK and C++ code. &lt;strong&gt;If you don't do this, the new plugin will no longer recognize and use your consumer keep rules,&lt;/strong&gt; even if you place them there, so this is pretty important, as it will only surface on a release build, in runtime (possibly even in prod).&lt;/p&gt;

&lt;p&gt;Now, as you might have probably guessed, the top-level &lt;code&gt;android&lt;/code&gt; block will no longer be available for you. There will be no build variants, build flavors in those KMP library modules. So before, if you were following my instructions and already refactored all of those usages to move them to the application module and inject the necessary flags and variables via DI, you will hopefully not have a lot of trouble with this. But if you still do use some BuildConfig values, there is now no place to declare them. Same can be said for res values, manifest placeholders, etc. All of that is now not supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important note for Compose Multiplatform resources
&lt;/h2&gt;

&lt;p&gt;Previously, you saw that we disabled Android resources. But &lt;strong&gt;if you don't enable Android resource processing, even for KMP modules with CMP resources, now in your feature modules and UI modules, your app will crash at runtime.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;androidLibrary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;androidResources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&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;Add this block to every module that you have that uses Compose Multiplatform resources. I had a convention plugin for feature modules, which made this super easy for me. More details are under the &lt;a href="https://youtrack.jetbrains.com/issue/CMP-9547" rel="noopener noreferrer"&gt;bug ticket on YouTrack&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replace Android Unit Test with Android Host Test
&lt;/h2&gt;

&lt;p&gt;The next step is to replace Android Unit Test dependency declarations with Android Host Test declarations. You can do this via an IDE search and replace using a simple regex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-    androidUnitTestImplementation(libs.bundles.unittest)
&lt;/span&gt;&lt;span class="gi"&gt;+    androidHostTestImplementation(libs.bundles.unittest)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll have to do this for every single module that has Android unit test dependencies. I unfortunately didn't think of a convention plugin, so I had to run this on literally every single build.gradle file.&lt;/p&gt;

&lt;p&gt;I also had to refactor Gradle files a little bit because I used top-level &lt;code&gt;implementation&lt;/code&gt; and &lt;code&gt;api&lt;/code&gt; dependency declaration DSL functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// wrong&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wasn't correct anyway, and it was incredibly confusing because this "implementation" just meant Android implementation, not KMP implementation, so that was a good change.&lt;/p&gt;

&lt;p&gt;I'm also using &lt;a href="https://github.com/respawn-app/FlowMVI" rel="noopener noreferrer"&gt;FlowMVI&lt;/a&gt; in my project, and unfortunately, the FlowMVI debugger relies on Ktor, serialization and some other relatively heavy dependencies that were previously only included in the Android debug source set, but I had to ditch that and just install the FlowMVI debugger using a runtime-gated flag from DI that I mentioned above. This doesn't make me happy, but in the future I will improve this maybe by moving the installation of the plugin to the Android app module, since FlowMVI makes extending business logic super easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add build script dependency on Kotlin
&lt;/h2&gt;

&lt;p&gt;Finally, I recommend adding a new build script dependency on Kotlin, just to keep your build Kotlin version and runtime Kotlin versions aligned. I wanted that because I have a single version catalog definition. You do it in the &lt;strong&gt;top-level build.gradle.kts&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;buildscript&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;classpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gradle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// org.jetbrains.kotlin:kotlin-gradle-plugin&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;h2&gt;
  
  
  Small quick optional wins at the end
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ditch &lt;code&gt;android.lint.useK2Uast=true&lt;/code&gt; that is deprecated now if you had it.&lt;/li&gt;
&lt;li&gt;An optional step is to use the new R8 optimizations described in the document I linked above. We have had manual ProGuard rules for removing the Kotlin null checks, and now this is shipped with AGP, so I just migrated to the new syntax (&lt;code&gt;-processkotlinnullchecks remove&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Honestly, this migration was a huge pain to me. I'm not gonna claim that I have the perfect code, but my Gradle setup was decent. &lt;strong&gt;If you're a developer and this all sounds incredibly overwhelming and like a huge effort, you're right.&lt;/strong&gt; Because I already did it, I can help your team migrate your project to the new AGP much faster and save you the effort. I recently started taking projects as a consultant and KMP migration advisor, so consider giving your boss a shout-out to &lt;a href="https://nek12.dev" rel="noopener noreferrer"&gt;nek12.dev&lt;/a&gt; if you liked this write-up and want me to help you.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>agp</category>
      <category>gradle</category>
    </item>
    <item>
      <title>What are AI agent skills and how to use them - complete breakdown with examples</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Mon, 12 Jan 2026 19:09:46 +0000</pubDate>
      <link>https://dev.to/nek12/what-are-ai-agent-skills-and-how-to-use-them-complete-breakdown-with-examples-3e0e</link>
      <guid>https://dev.to/nek12/what-are-ai-agent-skills-and-how-to-use-them-complete-breakdown-with-examples-3e0e</guid>
      <description>&lt;h2&gt;
  
  
  What are agent skills and why do you need them?
&lt;/h2&gt;

&lt;p&gt;A relatively new thing in the world of AI agents is the so-called Skills system.&lt;/p&gt;

&lt;p&gt;Recently I started seriously developing skills. I even created a &lt;a href="https://github.com/respawn-app/claude-plugin-marketplace" rel="noopener noreferrer"&gt;marketplace&lt;/a&gt; of Claude plugins for Respawn, where I keep a skill for &lt;a href="https://github.com/respawn-app/ksrc" rel="noopener noreferrer"&gt;ksrc&lt;/a&gt; and a skill for &lt;a href="https://opensource.respawn.pro/FlowMVI/" rel="noopener noreferrer"&gt;FlowMVI&lt;/a&gt;. I'm increasingly using and creating different skills, and many of you are asking: "What even is this?". And I also see articles on the internet that incorrectly explain and incorrectly recommend creating and using skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So, initially skills were invented by Anthropic as part of their SDK for Claude Code.&lt;/strong&gt; Essentially, agent skills don't bring anything revolutionary - they're still just folders with markdown files. &lt;strong&gt;The most important thing is how they work - through so-called progressive disclosure of your agent's context.&lt;/strong&gt; I've already said that the most important thing when working with agents is context engineering, and this is another way to use context more effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do skills work?
&lt;/h2&gt;

&lt;p&gt;Skills are defined by one main file (&lt;code&gt;SKILL.md&lt;/code&gt;), and it has a specific frontmatter structure. In this frontmatter there's the skill name (what it teaches) and a description - a (very!) short description that ideally explains when to use this skill and what it can teach the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When your agent wrapper notices that you have such a file in your skills folder, it parses its header and includes it immediately in the agent's context&lt;/strong&gt; (literally as part of agents.md or claude.md). This way you get a hook for the LLM: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use this skill when you're writing code with FlowMVI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Models have already become so smart that they can understand in what development context they need to read and use this skill, by one line of text.&lt;/strong&gt; And the skills system takes advantage of this. For me it's like casting a fishing line - the model sees the bobber on the surface, and then it can pull up a whole huge pile of documentation if needed and search through it for anything it needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skill structure
&lt;/h3&gt;

&lt;p&gt;The header is written in the &lt;code&gt;SKILL.md&lt;/code&gt; file. In this file you describe your skill's structure: what folders exist, what files exist, and the main points about usage.&lt;/p&gt;

&lt;p&gt;For example, in my skill for &lt;a href="https://github.com/respawn-app/FlowMVI" rel="noopener noreferrer"&gt;FlowMVI&lt;/a&gt; the model is given the ability to view the documentation index, where all the nuances and details of using a specific feature are laid out (state management or creating plugins). But in the &lt;code&gt;skill.md&lt;/code&gt; file itself, which the model reads fully if it decides to use the skill, basic things are written: "FlowMVI is an architectural framework, here's how to quickly make a contract, here's how to write features, here's what DSL exists, and here's where you can look at function signatures".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So context disclosure happens in stages:&lt;/strong&gt; the model first sees a super-brief two-line header, then reads the main &lt;code&gt;skill.md&lt;/code&gt; file (a few hundred lines), and then can decide: "Aha, I understand, now I need to read the next file, for example, on creating custom plugins". The model goes to the appropriate folder next to the &lt;code&gt;skill.md&lt;/code&gt; file or makes internet requests, as in my case, to get fresh documentation.&lt;/p&gt;

&lt;p&gt;This way we achieve minimal context spending on knowledge for the model, unlike, for example, MCP or AGENTS.md, which are just thrown into the model's context as one huge chunk of text, regardless of whether they're needed or not.&lt;/p&gt;

&lt;p&gt;Why does your model need to know how to deploy your backend to production if it's currently doing minor fixes after review? That's the whole point of skills: &lt;strong&gt;don't give the model everything at once to avoid cluttering the context, but gradually reveal only the needed information&lt;/strong&gt; and let the model use its incredibly cool search capabilities and work with the command line to find specifically what it needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do you need to create skills?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You need skills to transfer specialized or fresh knowledge to the model in progressive form - knowledge that's not yet included in the model's training data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can create skills for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proprietary SDKs and how to work with them (store them in your repository then)&lt;/li&gt;
&lt;li&gt;New APIs that came out only a few months ago, and the model still can't handle working with them&lt;/li&gt;
&lt;li&gt;Niche frameworks that aren't yet in training data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What NOT to include in skills
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The most important thing is what you shouldn't create skills for: things the model likely already knows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You don't need to create a skill for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How to compile Kotlin code"&lt;/li&gt;
&lt;li&gt;"How to write SwiftUI"&lt;/li&gt;
&lt;li&gt;"How to work with OpenAI API"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Models already know this perfectly well based on millions of lines of code and all the documentation that exists on the internet. I've seen skills with absolutely useless content. And if the model reads such a skill, it will only work worse, because its context will be clogged with irrelevant or repetitive information that doesn't help the model but distracts it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before creating a skill, think: can the model already know what I'm trying to tell it? And completely cut out everything the model already knows from your skill.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, with &lt;a href="https://github.com/respawn-app/ksrc" rel="noopener noreferrer"&gt;ksrc&lt;/a&gt; the model doesn't need to know that ksrc is written in Go, how to use escape sequences, how to use sed syntax, how to use ripgrep, and how to make bash command chains. The model does this perfectly, and it doesn't need to be repeated. &lt;strong&gt;But what the model doesn't know is how and why to work with ksrc.&lt;/strong&gt; So that's exactly what I included in the &lt;code&gt;skill.md&lt;/code&gt; file for ksrc, and nothing more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you need to include something the model already knows, you can just reference it in one or two words.&lt;/strong&gt; For example, instead of describing in detail how grep search syntax works and what arguments are supported, just write: "Ripgrep arguments are fully supported" or "Add --rg-args at the end to filter results". That will be enough.&lt;/p&gt;

&lt;p&gt;The skill for FlowMVI works the same way. In general, models have long known what MVI frameworks are, but they specifically don't yet know ideally the syntax of FlowMVI specifically as a framework and might not know what changed in new versions. So my skill contains not a single word about what intents and side effects are, and what should go in the MVI state. Because this is general knowledge that the internet has been covered with for years, decades, and the model knows this perfectly. Instead, the skill consists of function signatures and available configuration parameters in different DSL functions and some common mistakes that the model makes in my experience working with the library. That is, &lt;strong&gt;it specifically covers the model's weak spots, spending a minimum number of tokens.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you create skills?
&lt;/h2&gt;

&lt;p&gt;Usually this is needed when your &lt;code&gt;agents.md&lt;/code&gt; files are growing, or when you're releasing some framework that's expected to be used by models too.&lt;/p&gt;

&lt;p&gt;For example, ksrc is intended for use only by models, developers don't need to use it. FlowMVI is used by both developers and models, so the skill is more of a nice bonus. &lt;strong&gt;But in any case, if a model will use this utility, it would be great if you shipped a skill that can be installed right away.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why? The model will either not use the utility at all (simply because it doesn't know about its existence), or won't use it correctly (because without reading documentation it won't know the syntax). &lt;strong&gt;This is a good way to reduce &lt;code&gt;agents.md&lt;/code&gt; and reduce tokens spent on manual documentation search by models.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you see that the model is stumbling on something - for example, writing code that doesn't compile, or incorrectly using the new Compose API, or can't make a Glance widget correctly - you can gather the documentation, pack it into a skill, and if you do it right, this can solve your problems with model performance when working with a specific technology.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create skills
&lt;/h2&gt;

&lt;p&gt;Skills are repackaged documentation for some framework. So start by creating a good header and &lt;code&gt;skill.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Most often in your code (for example, in Codex) there's already a built-in flow for creating skills. You just use a skill for creating skills (so meta 😅). And Codex, for example, will create the whole skill with everything you need. You just tell it where to get the framework documentation, and then it will work to pack it into a skill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One thing: after creating a skill you still need to go through all the files it created and clean them up.&lt;/strong&gt; Or initially prompt the model to specifically describe the moments that, as you already know from practice, are pain points / difficult and complex features to use. Because by default the model will just rewrite the documentation into &lt;code&gt;skill.md&lt;/code&gt; and might also miss many points.&lt;/p&gt;

&lt;p&gt;For example, in FlowMVI I had to redo a lot because I wanted the model to pull the most updated documentation itself via curl, and in &lt;code&gt;skill.md&lt;/code&gt; and in the skill folder there would be only function signatures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with the template that the model will create for you in your wrapper, and then refine it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;My opinion is that skills are a very cool way to save context. &lt;strong&gt;And this feature hit the LLM development curve really well, because models have now become so easy to prompt that one line is enough for perfect execution of instructions by the model.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So you just create a skill, write one line in it "Use ksrc to search sources", and you can count on the model already being so smart that it will understand on its own when to use this skill. &lt;strong&gt;This is very easy for you and saves context significantly and increases model performance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I often hear people complaining: "My model doesn't write compiling code" or "Can't work with some niche libraries", and the answer was always on the surface - as usual it's just markdown files.&lt;/p&gt;

</description>
      <category>aiagents</category>
      <category>llm</category>
      <category>skills</category>
      <category>contextengineering</category>
    </item>
    <item>
      <title>Agents and Gradle Dont Get Along - I Fixed It in Two Commands</title>
      <dc:creator>Nek.12</dc:creator>
      <pubDate>Tue, 06 Jan 2026 17:37:58 +0000</pubDate>
      <link>https://dev.to/nek12/agents-and-gradle-dont-get-along-i-fixed-it-in-two-commands-b2e</link>
      <guid>https://dev.to/nek12/agents-and-gradle-dont-get-along-i-fixed-it-in-two-commands-b2e</guid>
      <description>&lt;p&gt;Folks, today I'm excited to introduce my new project!&lt;/p&gt;

&lt;p&gt;First, I should say that I primarily write in Kotlin. &lt;strong&gt;In Kotlin, we have a problem with viewing and exploring the source code of third-party libraries.&lt;/strong&gt; I've used TypeScript, Go, and Kotlin, and I can say that I envy those who code in TypeScript, because agents, when working with it, can simply dive into &lt;code&gt;node_modules&lt;/code&gt;, ripgrep that directory and find the needed code right away, literally instantly, in the downloaded caches.&lt;/p&gt;

&lt;p&gt;Compared to this, Kotlin, especially multiplatform, is torture. &lt;strong&gt;Agents previously couldn't view source code at all, they just hallucinated code.&lt;/strong&gt; Now they've gotten smarter and try to solve problems themselves when they don't know the API of some library or need to find the right function overload, through filesystem search. But even with all permissions, caches, and assuming all dependencies are already downloaded, this is very difficult for them. Finding a single dependency can take up to 10-15k context tokens, so...&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing &lt;a href="https://github.com/respawn-app/ksrc" rel="noopener noreferrer"&gt;ksrc&lt;/a&gt;!
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This is a CLI utility that allows agents to view the source code of any Kotlin libraries in a single line&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With ksrc, your agent will check source code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ksrc search &lt;span class="s2"&gt;"pro.respawn.apiresult:core*"&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"recover"&lt;/span&gt;
pro.respawn.apiresult:core:2.1.0!/commonMain/pro/respawn/apiresult/ApiResult.kt:506:42:public inline infix fun &amp;lt;T&amp;gt; ApiResult&amp;lt;T&amp;gt;.recover&lt;span class="o"&gt;(&lt;/span&gt;
...

&lt;span class="nv"&gt;$ &lt;/span&gt;ksrc &lt;span class="nb"&gt;cat &lt;/span&gt;pro.respawn.apiresult:core:2.1.0!/commonMain/pro/respawn/apiresult/ApiResult.kt &lt;span class="nt"&gt;--lines&lt;/span&gt; 480,515
...
@JvmName&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"recoverTyped"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
public inline infix fun &amp;lt;reified T : Exception, R&amp;gt; ApiResult&amp;lt;R&amp;gt;.recover&lt;span class="o"&gt;(&lt;/span&gt;
    another: &lt;span class="o"&gt;(&lt;/span&gt;e: T&lt;span class="o"&gt;)&lt;/span&gt; -&amp;gt; ApiResult&amp;lt;R&amp;gt;
&lt;span class="o"&gt;)&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2 commands -&amp;gt; source found, with filtering by version and dependency, and automatic downloading and unpacking.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What did it look like without ksrc?
&lt;/h2&gt;

&lt;p&gt;Without ksrc, in practice the search looked like this for my agents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="nt"&gt;--files&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="s2"&gt;"ApiResult.kt"&lt;/span&gt; /Users/nek/.gradle/caches

&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="s2"&gt;"ApiResult&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.recover|recover&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;("&lt;/span&gt; /Users/nek/Developer/Respawn/Backend

&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="nt"&gt;--files&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="s2"&gt;"*apiresult*"&lt;/span&gt; /Users/nek/.gradle/caches

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /Users/nek/.gradle/caches
9.2.1       CACHEDIR.TAG    journal-1
build-cache-1   jars-9      modules-2

&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="nt"&gt;--files&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="s2"&gt;"*apiresult*"&lt;/span&gt; /Users/nek/.gradle/caches/modules-2/files-2.1

&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="nt"&gt;--files&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="s2"&gt;"*apiresult*"&lt;/span&gt; /Users/nek/.gradle/caches/jars-9

&lt;span class="nv"&gt;$ &lt;/span&gt;fd &lt;span class="nt"&gt;-i&lt;/span&gt; apiresult /Users/nek/.gradle/caches/modules-2
/Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/
/Users/nek/.gradle/caches/modules-2/metadata-2.107/descriptors/pro.respawn.apiresult/
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult
core            core-iosarm64       core-jvm
core-android        core-iossimulatorarm64  core-wasm-js
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm
2.1.0
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0
193901bf1e2ecee192d92363d99b2e056467be28
938d7fb2b3cbd2806baac501f75182b9734ee5e1
ac2afbf602985d4257dcae7a6b90713585291627
b8101c9a149083295b708f4010e7c501840c5d8d
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0/193901bf1e2ecee192d92363d99b2e056467be28
core-jvm-2.1.0-sources.jar
&lt;span class="nv"&gt;$ &lt;/span&gt;jar tf /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0/193901bf1e2ecee192d92363d99b2e056467be28/core-jvm-2.1.0-sources.jar | rg &lt;span class="s2"&gt;"ApiResult"&lt;/span&gt;
commonMain/pro/respawn/apiresult/ApiResult.kt
&lt;span class="nv"&gt;$ &lt;/span&gt;unzip &lt;span class="nt"&gt;-p&lt;/span&gt; /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0/193901bf1e2ecee192d92363d99b2e056467be28/core-jvm-2.1.0-sources.jar commonMain/pro/respawn/apiresult/ApiResult.kt | rg &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"recover"&lt;/span&gt;
...
&lt;span class="nv"&gt;$ &lt;/span&gt;unzip &lt;span class="nt"&gt;-p&lt;/span&gt; /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0/193901bf1e2ecee192d92363d99b2e056467be28/core-jvm-2.1.0-sources.jar commonMain/pro/respawn/apiresult/ApiResult.kt | &lt;span class="nb"&gt;nl&lt;/span&gt; &lt;span class="nt"&gt;-ba&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'490,510p'&lt;/span&gt;
...
public inline infix fun &amp;lt;reified T : Exception, R&amp;gt; ApiResult&amp;lt;R&amp;gt;.recover&lt;span class="o"&gt;(&lt;/span&gt;
   another: &lt;span class="o"&gt;(&lt;/span&gt;e: T&lt;span class="o"&gt;)&lt;/span&gt; -&amp;gt; ApiResult&amp;lt;R&amp;gt;
&lt;span class="o"&gt;)&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;15 (!) steps, tons of thinking tokens, tons of garbage in context, and random unarchived junk files in your system - just to see a single method!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All because of Gradle's "brilliant" cache organization system: agents need to dig through hashed folders that Gradle creates, thousands of directories in modules-2.1 and so on. The process looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find the needed dependency, knowing only the package name (the artifact often differs in its location)&lt;/li&gt;
&lt;li&gt;Navigate to that folder, find the downloaded version&lt;/li&gt;
&lt;li&gt;Select the version that's specifically used in the project (to do this, you need to check which dependencies already exist in the project)&lt;/li&gt;
&lt;li&gt;Find the ZIP archive with sources, if it exists (if it doesn't exist, you need to write your own Gradle task to download them, anew for each project)&lt;/li&gt;
&lt;li&gt;Unarchive the downloaded archive to a temporary directory&lt;/li&gt;
&lt;li&gt;Only after all that, grep through it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if there are no sources, then you generally need to use something like &lt;code&gt;javap&lt;/code&gt; to decompile the sources, just to see what a single function looks like in some library from Google.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My utility packs all the steps described above into two commands: &lt;code&gt;ksrc search&lt;/code&gt; and &lt;code&gt;ksrc cat&lt;/code&gt;&lt;/strong&gt; - and outputs a beautifully formatted result that an agent can combine with other commands and enhance with scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration with AI Agents
&lt;/h2&gt;

&lt;p&gt;I've also prepared a Claude plugin with a skill for your agents, so they can immediately use it when needed, on their own, without your participation or prompting, and also a skill for Codex.&lt;/p&gt;

&lt;p&gt;Codex wrote this utility itself for itself and completely independently in Go - a language in which I understand absolutely nothing, have never written or read a single line in my life. And it packaged it into a single file that you just need to download using the &lt;a href="https://github.com/respawn-app/ksrc" rel="noopener noreferrer"&gt;script on GitHub&lt;/a&gt;, and configured the integration with agents for you.&lt;/p&gt;

&lt;p&gt;In the near future, I'll work on publishing through Homebrew and some option for Linux. I'd be happy to hear your feedback on social media. For those who develop in Kotlin, I hope this will be as useful as it is for me.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>ai</category>
      <category>cli</category>
      <category>aiagents</category>
    </item>
  </channel>
</rss>
