<?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: Stephanie Dover</title>
    <description>The latest articles on DEV Community by Stephanie Dover (@stephanie_dover_b49c2e8d1).</description>
    <link>https://dev.to/stephanie_dover_b49c2e8d1</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%2F3973257%2Fc8e1d96f-a1aa-41c7-b0bd-88095d5b29b7.jpg</url>
      <title>DEV Community: Stephanie Dover</title>
      <link>https://dev.to/stephanie_dover_b49c2e8d1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stephanie_dover_b49c2e8d1"/>
    <language>en</language>
    <item>
      <title>A PR review agent with 2 comments beats one with 20</title>
      <dc:creator>Stephanie Dover</dc:creator>
      <pubDate>Fri, 24 Jul 2026 21:26:49 +0000</pubDate>
      <link>https://dev.to/stephanie_dover_b49c2e8d1/a-pr-review-agent-with-2-comments-beats-one-with-20-m2p</link>
      <guid>https://dev.to/stephanie_dover_b49c2e8d1/a-pr-review-agent-with-2-comments-beats-one-with-20-m2p</guid>
      <description>&lt;p&gt;A good review agent needs a bias toward silence, a validation pass against its own findings, a context budget, and a definition of "correct" that comes from the repo instead of from the industry. This walks through rules I encoded in a review agent skill, with the prompt examples, and why each one exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  The twenty-comment review
&lt;/h2&gt;

&lt;p&gt;Here is what happens when you point a competent model at a diff and ask it to review.&lt;/p&gt;

&lt;p&gt;It comes back with twenty comments. Two are real: a race in the retry path, a check that got deleted along with the code it was guarding. Four are true but irrelevant, a magic number in a test fixture, a function that could be split. Six are opinions dressed as findings, "consider extracting this to a constant." Five are about code the PR never touched. Three are wrong, because the model didn't read the caller that already validates the input.&lt;/p&gt;

&lt;p&gt;Every one of them is formatted beautifully. Severity label, file and line, a code snippet, a suggested fix.&lt;/p&gt;

&lt;p&gt;The author now has to read twenty comments to find two. Next PR, they skim. The one after that, they stop reading. The agent's precision problem has become a trust problem, and once a reviewer is untrusted, its recall doesn't matter at all. A human staff engineer reviewing that same diff leaves two comments and approves. That is not laziness. That is the job.&lt;/p&gt;

&lt;p&gt;So the interesting design question for a review agent is not "how do we find more." It's "how do we find less, and be right."&lt;/p&gt;

&lt;p&gt;Everything below comes out of building &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;klaussy-agents&lt;/a&gt;, an MIT-licensed CLI that scaffolds review skills into a repo. The prompt excerpts are the real ones, rewrapped to fit and with em-dashes normalized to commas, from &lt;a href="https://github.com/steph-dove/klaussy-agents/blob/main/src/klaussy/templates/skills/review/SKILL.md" rel="noopener noreferrer"&gt;&lt;code&gt;skills/review/SKILL.md&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/steph-dove/klaussy-agents/blob/main/src/klaussy/templates/skills/review/sub-agents.md" rel="noopener noreferrer"&gt;&lt;code&gt;sub-agents.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Make silence a valid outcome
&lt;/h2&gt;

&lt;p&gt;A model asked to review code assumes it was asked to produce findings. Zero findings reads to it like failure, so it pads. The fix is to mark this as a valid and wanted outcome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Precision over recall.** Default to *not* reporting. If no finding is one a
competent author would clearly want to fix, return an empty review and say so,
an empty review is a valid, good outcome, not a failure. Do not invent findings
or pad to look thorough.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Be concise" gets interpreted as "write shorter comments," and you get twenty terse comments instead of twenty long ones. "An empty review is a valid, good outcome" changes the target instead of the word count.&lt;/p&gt;

&lt;p&gt;Pair it with a rule that makes padding expensive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Every finding must name a concrete trigger.** State the specific input, state,
or execution path that makes it go wrong. If you cannot describe how the problem
is reached, you have not proven it, drop it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most padding cannot survive that. "Consider extracting this constant" has no input that triggers it. "This N+1 fires once per row in the results list, so a 500-row page makes 500 queries" does. Requiring the trigger is a filter the model applies to itself, and it costs you almost nothing in real findings.&lt;/p&gt;

&lt;p&gt;One thing I banned: self-assigned confidence scores.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Don't self-assign confidence scores.** A number you make up is noise; the
trigger path above is the real evidence.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Confidence: 85%" is a number the model invented. It looks like calibration and isn't. Worse, it lets a weak finding ship with a hedge attached instead of being dropped. Make the model produce evidence, not a probability.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Trace every path the change touches, then narrow again
&lt;/h2&gt;

&lt;p&gt;Focus is not the same as shallowness. The dangerous bugs in a PR are usually not visible in the diff hunk. They live one call up.&lt;/p&gt;

&lt;p&gt;Two mechanisms handle this without widening the review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read whole files, never just hunks.&lt;/strong&gt; The diff tells you what changed. It does not tell you what the change means. So the context phase reads the full text of every reviewable changed file, in one parallel batch, before any analysis happens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audit what was deleted.&lt;/strong&gt; This one catches more regressions than any other rule in the skill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Removed-behavior audit:** for every deleted or replaced line in the diff, name
the invariant, guard, or behavior it enforced, then confirm the new code
re-establishes it (or that dropping it is intentional and safe). Silently
removed checks are a top source of regressions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additions get scrutinized because they're new. Deletions get skimmed, because a red line looks like cleanup. A null check that vanished during a refactor is invisible in review and obvious in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recalibrate by creating a steelman.&lt;/strong&gt; Every finding, before it ships, goes through this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Argue the author's side, then refute it.** For each finding, write the
strongest one-line case that it is *not* a real problem (the input can't occur,
a caller already guards it, the framework handles it). Then either refute that
case with specific code evidence, or, if you can't, drop the finding as a likely
false positive. A finding you can't defend against its own counterargument
doesn't ship.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing else in the skill removes as many bad findings. Models are agreeable by default, and an agreeable reviewer confirms its own first impression. Forcing an explicit steelman of the author, then requiring code evidence to knock it down, kills most false positives at the source. The validator reads callers and callees to do it, which is the tracing work that made the finding provable or not.&lt;/p&gt;

&lt;p&gt;The two rules work as a pair. Trace wide to find it. Steelman hard to keep it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Treat context as a budget
&lt;/h2&gt;

&lt;p&gt;A large PR will not fit usefully in one window, and even when it fits, attention degrades across a long context. Three moves, in increasing order of cleverness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete the noise before the model sees it.&lt;/strong&gt; Most of a big diff is not reviewable by anyone. Lockfiles, &lt;code&gt;dist/&lt;/code&gt; and &lt;code&gt;node_modules/&lt;/code&gt;, minified bundles, generated protobuf, pure renames. A deterministic pre-pass strips them, which is a job for code, not for a model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;LOCKFILES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;frozenset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;package-lock.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yarn.lock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;poetry.lock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uv.lock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cargo.lock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;go.sum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...})&lt;/span&gt;
&lt;span class="n"&gt;NOISE_DIR_PARTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;frozenset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node_modules&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.next&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__generated__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A PR that reads as 4,000 changed lines is routinely 600 after trimming, and those 600 are the ones a human would have read.&lt;/p&gt;

&lt;p&gt;The important detail is what happens to the rest. It does not disappear. The trimmed diff is followed by an explicit &lt;strong&gt;Excluded from review&lt;/strong&gt; manifest listing every dropped file and the reason, because silent truncation reads as "I covered everything" when it didn't. If a finding in real code points at a generated file, the reviewer can go look.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Split the remaining context by lens, not by chunk.&lt;/strong&gt; Above 150 changed lines, the review fans out to parallel sub-agents, each with the same diff and one job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a senior engineer reviewing a pull request. Your ONLY focus is the lens
described below. Other concerns (correctness, architecture, security, scope,
etc.) are handled by parallel reviewers, ignore them.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correctness and concurrency. Architecture, performance, reliability, dependencies. Security, readability, tests. Scope and conventions. Two more activate conditionally: an agentic and evals lens when the diff touches AI code, and an architecture-decision lens when it contains an ADR or design doc.&lt;/p&gt;

&lt;p&gt;Chunk-splitting means no agent ever sees the whole change, and cross-file bugs go missing. Lens-splitting means every agent sees the whole change and is only responsible for one kind of question, which is also how a good human review rotation works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fan out the slow part too.&lt;/strong&gt; Validation is the one phase that stays serial by default, and it's the most expensive one, because every finding means reading files and tracing paths. So above six findings, validators run in parallel batches of four to six, grouped by file so each validator reads a given file once.&lt;/p&gt;

&lt;p&gt;There's also a cost lever. Not every lens needs the same model. Scope and conventions is largely pattern matching, so it runs on a cheap fast model, while correctness, security, and architecture keep the expensive one. In a parallel fan-out this saves money more than time.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The repo's conventions outrank the industry's
&lt;/h2&gt;

&lt;p&gt;This is where general-purpose review agents consistently annoy people. The model knows what modern code looks like. Your repo is eleven years old. Those disagree, and the model is wrong to resolve it in favor of the industry.&lt;/p&gt;

&lt;p&gt;If the codebase uses callbacks throughout, a comment recommending async/await is not a review finding. It's a proposal for a refactor in a thread about something else. Same for a custom &lt;code&gt;Result&lt;/code&gt; type instead of exceptions, a hand-rolled DI container, a naming scheme from 2015. Consistency with the surrounding code is worth more than proximity to current best practice, because consistency is what lets the next person read the file.&lt;/p&gt;

&lt;p&gt;So the conventions lens gets its standards from the repo, not from the model's priors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Project Conventions
{{REPO_SPECIFIC_CHECKS}}

If no repo-specific checks are listed above, read CLAUDE.md and any matching
`.claude/rules/*.md` for the area being changed, and verify the PR adheres to
the conventions and known pitfalls listed there.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;{{REPO_SPECIFIC_CHECKS}}&lt;/code&gt; is filled in at scaffold time by parsing the repo's own &lt;code&gt;CLAUDE.md&lt;/code&gt; and path-scoped rule files into check items. The agent is checking the PR against the house style as written down, not against a general notion of good code. And convention findings have to cite the rule they're breaking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;For convention violations, reference the specific convention (file path or
section in CLAUDE.md / `.claude/rules/`).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A citation requirement is a good forcing function. If the agent can't point at where a convention is written down, what it has is a preference, and preferences don't go in review comments.&lt;/p&gt;

&lt;p&gt;An agent that wants to modernize your codebase is not reviewing your PR, it's proposing a different one.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Thorough and excessive are different axes
&lt;/h2&gt;

&lt;p&gt;Thoroughness and brevity are not a tradeoff you have to make. Thoroughness is about how many paths you check. Excessiveness is about how many of your observations you report. You want maximum on the first and heavy filtering on the second.&lt;/p&gt;

&lt;p&gt;Three filters do that work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Severity has to mean something.&lt;/strong&gt; Blocker, High, Medium, Low, Warn, Nit. If everything lands at Medium, the scale is decorative. The validation pass explicitly downgrades: a race condition that only affects a debug-only code path is a Low, not a High, and calling it High trains the author to distrust every High.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out-of-scope changes get flagged, not fixed.&lt;/strong&gt; Unrelated changes get &lt;code&gt;Warn&lt;/code&gt; severity, which says "you probably meant to do this, confirm," not "change it." That's a different move from a finding, and it's the right one for drive-by refactors bundled into a feature PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unrelated optimizations you happen to notice do not go in the review.&lt;/strong&gt; This is the hardest one to hold. The agent read three files and it found an inefficient loop in one of them. Untouched by this PR. It is a real inefficiency. It still does not belong in this review, because a review comment is a request for the author to act, and asking them to fix code they didn't write in a PR about something else is how a two-day PR becomes a two-week PR. File it, mention it in chat, open a separate issue. Do not put it in the thread.&lt;/p&gt;

&lt;p&gt;The nit question resolves the same way. A trailing-whitespace nit is not wrong. It's just not worth what it costs: it consumes the author's attention, and attention is the scarce resource in a review. Ten nits stacked above one Blocker is a formatting choice that hides a bug.&lt;/p&gt;

&lt;p&gt;Thorough means the agent traced the deletion, read the callers, and checked the error path. Excessive means it told you about all of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Make it sound like a person, because tone changes whether it's read
&lt;/h2&gt;

&lt;p&gt;You can be completely right and still get ignored. Feedback that reads as generated, or as a lint report with a scolding attached, gets skimmed.&lt;/p&gt;

&lt;p&gt;Two layers handle this, and both are needed.&lt;/p&gt;

&lt;p&gt;The prompt side asks for human prose. No em-dashes, the single biggest tell. No filler openers (&lt;code&gt;It's worth noting that&lt;/code&gt;). No chatbot scaffolding (&lt;code&gt;Hope this helps!&lt;/code&gt;). No emoji, no ALL-CAPS scolding, no LLM lexicon (&lt;code&gt;delve&lt;/code&gt;, &lt;code&gt;robust&lt;/code&gt;, &lt;code&gt;seamless&lt;/code&gt;, &lt;code&gt;leverage&lt;/code&gt;). No ranking praise, which is its own tell: &lt;code&gt;great catch&lt;/code&gt; on a review the agent itself wrote is nonsense.&lt;/p&gt;

&lt;p&gt;Collaborative is the default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Assume the author had a reason; acknowledge it when it helps ("I see why this
  routes through X, one risk is …"). Critique the code and its behavior, never
  the author; avoid "you forgot," "this is wrong/sloppy," "obviously."
- Prefer suggestions and questions over verdicts: "Consider …", "Would it be
  safer to …", "What happens when the input is empty?"
- Agreeable is not padded: warmth lives in the framing, not in filler praise.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blunt mode exists for people who want it: lead with the problem and the fix, no hedging, no softening. Still professional, still critiquing the code and not the author.&lt;/p&gt;

&lt;p&gt;Maintain substance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Tone must not dilute substance.** Every comment keeps its severity, its
`file:line` + verbatim code quote, its concrete trigger / failure scenario, and
its specific suggested fix. A note that hides a real Blocker, downgrades
severity, or drops the detail has failed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Politeness that turns a Blocker into "you might want to look at this sometime" is worse than bluntness. Tone is the wrapper, never the content.&lt;/p&gt;

&lt;p&gt;And because a prompt asks but does not enforce, there's a deterministic pass afterward that strips the hard tells with regex, preserving fenced and inline code untouched. Prompt compliance is soft, regex is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this buys you
&lt;/h2&gt;

&lt;p&gt;A review that comes back with two comments, both real, both traceable to a specific input, both citing a convention or a failure path, gets read. The author fixes both and merges.&lt;/p&gt;

&lt;p&gt;A review with twenty comments gets skimmed once and ignored afterward, and the two real findings inside it die with the other eighteen.&lt;/p&gt;

&lt;p&gt;The rules that get you the first one are mostly rules about &lt;em&gt;not&lt;/em&gt; reporting: default to silence, require a trigger, steelman the author before you ship, cite the repo instead of the industry, keep unrelated observations out of the thread. The thoroughness still happens.&lt;/p&gt;

&lt;p&gt;If you want to read the full skill, it's in &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;klaussy-agents&lt;/a&gt; under &lt;code&gt;templates/skills/review/&lt;/code&gt;, MIT licensed. &lt;code&gt;pip install klaussy-agents&lt;/code&gt; and &lt;code&gt;klaussy init&lt;/code&gt; will scaffold a version wired to your repo's own conventions.&lt;/p&gt;

&lt;p&gt;What's the worst false positive your review bot has confidently reported?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written by Stephanie Dover, Software Engineer 10+ YOE, ex GitHub, Twitch, Microsoft. Creator of Klaussy.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/stephaniedover/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://github.com/steph-dove" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.klaussy.com/" rel="noopener noreferrer"&gt;Klaussy Desktop&lt;/a&gt; · &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;Klaussy Agents&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codereview</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>Decoupling a High-Throughput Engagement Service from a Monetization System</title>
      <dc:creator>Stephanie Dover</dc:creator>
      <pubDate>Tue, 23 Jun 2026 17:17:14 +0000</pubDate>
      <link>https://dev.to/stephanie_dover_b49c2e8d1/decoupling-a-high-throughput-engagement-service-from-a-monetization-system-45hd</link>
      <guid>https://dev.to/stephanie_dover_b49c2e8d1/decoupling-a-high-throughput-engagement-service-from-a-monetization-system-45hd</guid>
      <description>&lt;p&gt;At large-scale consumer platforms, product reuse can turn into technical debt.&lt;/p&gt;

&lt;p&gt;In this case, a non-monetized &lt;em&gt;engagement points&lt;/em&gt; feature shared a backend with a wallet-based &lt;em&gt;monetization&lt;/em&gt; service.&lt;/p&gt;

&lt;p&gt;The engagement system handled &lt;strong&gt;over a million transactions per second&lt;/strong&gt;, far beyond what the transactional payments backend was built for.&lt;/p&gt;

&lt;p&gt;What began as a pragmatic shortcut led to rising latency, ballooning storage costs, and operational strain on a system optimized for financial correctness rather than lightweight interactivity.&lt;/p&gt;

&lt;p&gt;The only sustainable fix was to &lt;strong&gt;decouple them completely&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Context
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;monetization system&lt;/strong&gt; prioritized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Durable writes&lt;/li&gt;
&lt;li&gt;Idempotency&lt;/li&gt;
&lt;li&gt;Strong transactional guarantees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;engagement system&lt;/strong&gt; prioritized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Speed&lt;/li&gt;
&lt;li&gt;Throughput&lt;/li&gt;
&lt;li&gt;Low operational cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By sharing a backend, the engagement workload forced the payments infrastructure to scale inefficiently adding expensive transactional overhead to a system that didn’t need it.&lt;/p&gt;

&lt;p&gt;Scaling further wasn’t the answer. Isolation was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the New Backend
&lt;/h2&gt;

&lt;p&gt;The decoupling required two major components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;dedicated API and data model&lt;/strong&gt; built for high-TPS, low-latency operations.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;live migration pipeline&lt;/strong&gt; capable of achieving full data parity with zero downtime.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Infrastructure Overview
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go microservice&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core logic and API layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protobuf + gRPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal RPC communication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Primary datastore: high throughput, flexible schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Kinesis Streams&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time change-data capture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Lambda functions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stream processors handling event ordering and writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redis cache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Idempotency layer to prevent duplicate writes during dual-write and stream replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure-as-code provisioning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudWatch metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Observability for throughput, lag, and latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feature flag service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Safe rollout and traffic control&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The new backend used a lightweight schema aligned to engagement interactions, simpler, cheaper, and better suited for massive write volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Diagrams
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Diagram 1: System Overview&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[Engagement API] --&amp;gt; B[Old DynamoDB Table]
    B --&amp;gt; C[AWS Kinesis Stream]
    C --&amp;gt; D[AWS Lambda Functions]
    D --&amp;gt; E[New DynamoDB Table]
    D --&amp;gt; F[Redis Cache&amp;lt;br/&amp;gt;(idempotency)]
    F --&amp;gt; E
    E --&amp;gt; G[Feature-flag Dual-Write]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Diagram 2: Migration Lifecycle&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[1. Export snapshot → S3 → Import new table] --&amp;gt; B[2. Sync updates via Kinesis + Lambda]
    B --&amp;gt; C[3. Redis ensures idempotency&amp;lt;br/&amp;gt;for dual-writes &amp;amp; replayed events]
    C --&amp;gt; D[4. Feature flag directs dual-writes]
    D --&amp;gt; E[5. Cutover &amp;amp; validation]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Migration Strategy
&lt;/h2&gt;

&lt;p&gt;Building the API was straightforward.&lt;/p&gt;

&lt;p&gt;Migrating live data at 1M+ TPS without downtime was the challenge.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Snapshot Bootstrap
&lt;/h3&gt;

&lt;p&gt;AWS provides a built-in mechanism to export a DynamoDB table snapshot to S3, which can then be imported into a new table.&lt;/p&gt;

&lt;p&gt;This seeded the new database with a point-in-time baseline, no long-running scans or Glue jobs required.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Real-Time Sync via Kinesis + Lambda
&lt;/h3&gt;

&lt;p&gt;Once the snapshot was imported, &lt;strong&gt;Kinesis Streams&lt;/strong&gt; captured every subsequent change (insert, update, delete) from the source table.&lt;/p&gt;

&lt;p&gt;Each event was processed by an &lt;strong&gt;AWS Lambda&lt;/strong&gt; consumer that replayed the change into the new DynamoDB table.&lt;/p&gt;

&lt;p&gt;Maintaining &lt;strong&gt;transaction order&lt;/strong&gt; was critical, out-of-sequence events could cause corruption or lost updates.&lt;/p&gt;

&lt;p&gt;To handle retries and potential duplicate delivery, I introduced a &lt;strong&gt;Redis-based idempotency layer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each event carried a unique transaction ID. Before processing, Lambda performed a fast Redis lookup to check whether that ID had already been written.&lt;/p&gt;

&lt;p&gt;If found, the event was skipped, eliminating double writes both from Kinesis replays and from the feature-flagged dual-write traffic hitting the same endpoint.&lt;/p&gt;

&lt;p&gt;This lightweight Redis layer made the migration &lt;em&gt;safe&lt;/em&gt;, ensuring exactly-once behavior without compromising throughput.&lt;/p&gt;

&lt;p&gt;Monitoring &lt;strong&gt;IteratorAge&lt;/strong&gt; and &lt;strong&gt;Duration&lt;/strong&gt; metrics in CloudWatch remained critical.&lt;/p&gt;

&lt;p&gt;If IteratorAge rose, the stream was falling behind, meaning either smaller batches or more concurrency were needed.&lt;/p&gt;

&lt;p&gt;With tuning and caching in place, the pipeline kept pace with over a million updates per second.&lt;/p&gt;

&lt;p&gt;The full migration completed within hours, not days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cutover with Feature Flags
&lt;/h2&gt;

&lt;p&gt;After the real-time sync stabilized, I rolled out the new backend via a &lt;strong&gt;feature-flagged dual-write&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dual-write requests to both APIs.&lt;/li&gt;
&lt;li&gt;Use Redis for idempotency checks to prevent duplicate writes.&lt;/li&gt;
&lt;li&gt;Validate data parity.&lt;/li&gt;
&lt;li&gt;Monitor Kinesis lag until zero.&lt;/li&gt;
&lt;li&gt;Cut traffic to the old API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once validation passed, the engagement service ran entirely on its new infrastructure.&lt;/p&gt;

&lt;p&gt;The monetization system was finally free of the extra load, and both systems could scale independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety and Verification
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Extended Kinesis retention to &lt;strong&gt;24+ hours&lt;/strong&gt; during migration.&lt;/li&gt;
&lt;li&gt;Kept the source table intact until post-cutover validation completed.&lt;/li&gt;
&lt;li&gt;Used Redis TTLs to automatically expire processed transaction keys, keeping cache cost minimal.&lt;/li&gt;
&lt;li&gt;Continuously compared record counts and hash digests between tables during dual-write.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These guardrails ensured recovery options, consistency, and full traceability throughout the migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results and Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kinesis Streams + Lambda&lt;/strong&gt; enable live, high-TPS migrations when tuned for throughput and ordering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis caching&lt;/strong&gt; ensures idempotency and prevents double writes under dual-write conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature-flag rollouts&lt;/strong&gt; provide control and observability for safe cutovers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling mismatched systems&lt;/strong&gt; is often cheaper and safer than scaling them together.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Scaling isn’t always about adding resources, sometimes it’s about removing coupling.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the final cutover, metrics flatlined exactly where they should, and I finally took that long-delayed vacation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technologies Referenced
&lt;/h2&gt;

&lt;p&gt;AWS Lambda — Serverless compute service for running backend logic without managing servers.&lt;/p&gt;

&lt;p&gt;Amazon DynamoDB — Fully managed NoSQL database optimized for high-throughput workloads.&lt;/p&gt;

&lt;p&gt;Amazon Kinesis Data Streams — Real-time event streaming service used for data replication and ingestion.&lt;/p&gt;

&lt;p&gt;AWS CloudWatch — Metrics and observability platform for monitoring throughput, latency, and iterator age.&lt;/p&gt;

&lt;p&gt;Amazon S3 — Object storage service used for snapshot exports and imports.&lt;/p&gt;

&lt;p&gt;Redis — In-memory cache used here for idempotency checks during dual-writes and stream replay.&lt;/p&gt;

&lt;p&gt;Terraform — Infrastructure-as-code tool for provisioning and managing AWS resources.&lt;/p&gt;

&lt;p&gt;gRPC — High-performance RPC framework for service-to-service communication.&lt;/p&gt;

&lt;p&gt;Protocol Buffers (Protobuf) — Serialization format used with gRPC to define and enforce API contracts.&lt;/p&gt;

&lt;p&gt;Feature Toggles / Flags — Technique for gradual rollouts and safe cutovers.&lt;/p&gt;

&lt;p&gt;Go Concurrency: Goroutines — Lightweight thread mechanism for concurrent workloads.&lt;/p&gt;

&lt;p&gt;Go Channels — Synchronization and communication primitive used for concurrent fan-out/fan-in patterns.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written by Stephanie Dover, Software Engineer 10+ YOE, ex GitHub, Twitch, Microsoft. Creator of Klaussy.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/stephaniedover/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://github.com/steph-dove" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.klaussy.com/" rel="noopener noreferrer"&gt;Klaussy Desktop&lt;/a&gt; · &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;Klaussy Agents&lt;/a&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>architecture</category>
      <category>database</category>
    </item>
    <item>
      <title>Why I scrub AI prose with regex, not a second LLM</title>
      <dc:creator>Stephanie Dover</dc:creator>
      <pubDate>Sat, 20 Jun 2026 00:11:10 +0000</pubDate>
      <link>https://dev.to/stephanie_dover_b49c2e8d1/why-i-scrub-ai-prose-with-regex-not-a-second-llm-5gnb</link>
      <guid>https://dev.to/stephanie_dover_b49c2e8d1/why-i-scrub-ai-prose-with-regex-not-a-second-llm-5gnb</guid>
      <description>&lt;p&gt;&lt;em&gt;Written by Stephanie Dover, Software Engineer 10+ YOE, ex GitHub, Twitch, Microsoft. Creator of Klaussy.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/stephaniedover/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://github.com/steph-dove" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.klaussy.com/" rel="noopener noreferrer"&gt;Klaussy Desktop&lt;/a&gt; · &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;Klaussy Agents&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;klaussy-agents&lt;/strong&gt; is a free, MIT-licensed CLI (&lt;code&gt;pip install klaussy-agents&lt;/code&gt;) that makes the prose an AI coding agent writes, PR comments, review notes, commit messages, read like a person wrote them. It works in two layers: a humanization spec baked into the agent's skills so it writes clean prose up front, and a deterministic &lt;code&gt;klaussy humanize&lt;/code&gt; pass that scrubs the output afterward. The scrubber is rule-based regex, not an LLM, and it never touches code. There's also a part I didn't expect going in: once the AI tells are gone, what's left can read curt and run long, so the spec also handles tone (don't be rude) and length (one sentence for a reply, one to five for a review comment). Repo: github.com/steph-dove/klaussy-agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;You can spot AI-written text now. Everyone can. And the place it grates most is a code review comment or a commit message, where the prose sits next to your name in a thread your teammates read.&lt;/p&gt;

&lt;p&gt;The tells are consistent. The em-dash is the biggest one. Right behind it: filler openers like "It's worth noting that…" and "I wanted to point out that…", chatbot scaffolding like "Hope this helps!" and "Let me know if you have questions!", and stacked hedges like &lt;code&gt;could potentially&lt;/code&gt;. An agent that leaves those in your PR reads like a bot, and people notice.&lt;/p&gt;

&lt;p&gt;The obvious fix is to tell the model not to do it. Add "don't sound like AI" to the prompt and move on. That helps, inconsistently, and it regresses silently the moment you change the model or the prompt drifts. Editing every comment by hand works too, but hand-editing every comment defeats the point of having an agent write them. I wanted something I could trust without rereading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "just tell the model" wasn't enough
&lt;/h2&gt;

&lt;p&gt;The honest answer to "why not just prompt for it" is: a prompt asks, it doesn't enforce. The model tries to comply. Sometimes it complies fully, sometimes it slips an em-dash back in on a longer comment, and you don't find out until the tell is already in the thread. Prompt compliance is soft by nature: you can make it better, never guaranteed.&lt;/p&gt;

&lt;p&gt;So I stopped treating the prompt as the whole answer and treated it as the first of two layers.&lt;/p&gt;

&lt;p&gt;The first layer is prompt-side. There's a single shared humanization block, internally &lt;code&gt;HUMANIZE_BLOCK&lt;/code&gt;, that gets substituted into every prose-output skill: review, pr, commit, explain, across all five of the agents klaussy-agents ships. The rules in it are plain: no em or en dashes, no filler openers, no chatbot scaffolding, tighten hedges, no emoji, no "Certainly", vary sentence shape, and never reword code. One spec, applied everywhere, so the agent isn't given conflicting instructions in different places.&lt;/p&gt;

&lt;p&gt;The second layer is the part that actually enforces. After the agent writes, the text goes through &lt;code&gt;klaussy humanize&lt;/code&gt;, a deterministic pass that makes the high-confidence edits regardless of how well the model followed instructions. The prompt asks; the scrubber enforces. Neither layer alone is enough, which is the whole reason there are two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why deterministic, and not a second model
&lt;/h2&gt;

&lt;p&gt;The tempting design is to run the agent's prose through another LLM with a "rewrite this to sound human" instruction. That's how most "AI humanizer" tools on the market work. I deliberately didn't.&lt;/p&gt;

&lt;p&gt;A rewrite model can fix awkward phrasing a regex never will. But it can also paraphrase something into being subtly wrong, and over technical prose that's a real risk: it can rewrite an identifier, mangle a command, or "improve" an example until it no longer runs. For text that ends up in a PR comment a teammate will act on, I didn't want a process that could introduce a new mistake while removing an em-dash.&lt;/p&gt;

&lt;p&gt;So the scrubber is rule-based regex. The consequences of that choice are all upsides for this job: it's fast, it's free, it runs offline with no network call, and because it only ever does a fixed set of high-confidence substitutions, it can't introduce a new error. It will not restructure a sentence or paraphrase a paragraph. It does a small, reliable set of edits and stops.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The edits it makes
&lt;/h3&gt;

&lt;p&gt;The scrubber does a short list of conservative transforms. Each one is a tell with a known, safe fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dashes.&lt;/strong&gt; Em-dashes (&lt;code&gt;—&lt;/code&gt;) and en-dashes (&lt;code&gt;–&lt;/code&gt;) in prose become commas or hyphens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filler openers.&lt;/strong&gt; Sentence-initial filler ("It's worth noting that", "I noticed that", "Please note that", and similar) is stripped, and the next word is re-capitalized so the sentence still reads correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chatbot scaffolding.&lt;/strong&gt; Trailing lines like "Let me know if…", "Hope this helps", "Feel free to…" are dropped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verbose phrasings.&lt;/strong&gt; A few get tightened: &lt;code&gt;in order to&lt;/code&gt; becomes &lt;code&gt;to&lt;/code&gt;, &lt;code&gt;could potentially&lt;/code&gt; becomes &lt;code&gt;could&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the before and after on the exact cases the test suite covers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Leaks a connection — wrap it.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Leaks a connection, wrap it.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;range 1–5 here&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;range 1 - 5 here&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;It's worth noting that the handler swallows the error.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;The handler swallows the error.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;This races on startup.&lt;/code&gt;&lt;br&gt;&lt;code&gt;Let me know if you have questions!&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;This races on startup.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Refactor in order to avoid the N+1.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Refactor to avoid the N+1.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;This could potentially deadlock.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;This could deadlock.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every one of those is a rule a human editor would apply without thinking. None of them changes what the comment means. That's the bar for inclusion: if an edit could change meaning, it doesn't make the list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Never touching code
&lt;/h3&gt;

&lt;p&gt;The single biggest risk in running any text transform over developer prose is that it reaches into a code example and breaks it. A "humanizer" that turns a dash inside a shell command into a comma has made your example wrong.&lt;/p&gt;

&lt;p&gt;The scrubber avoids this structurally. It splits the input on fenced &lt;code&gt;blocks and on&lt;/code&gt; &lt;code&gt;inline code&lt;/code&gt;, scrubs only the prose segments, and leaves every code segment byte-for-byte untouched. The dashes in a command stay dashes. The identifiers stay identifiers.&lt;/p&gt;

&lt;p&gt;Concretely, given a string that mixes prose and code like &lt;code&gt;Use `a — b` then:&lt;/code&gt;, followed by a fenced block containing &lt;code&gt;x — y&lt;/code&gt;, the dashes inside the inline span and inside the fence are kept exactly as written. Only a dash out in the prose would be normalized. Code in, code out, unchanged.&lt;/p&gt;

&lt;h3&gt;
  
  
  The interface
&lt;/h3&gt;

&lt;p&gt;There are two ways to use it. As a library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;klaussy.humanize&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;humanize&lt;/span&gt;

&lt;span class="n"&gt;clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;humanize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;It&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s worth noting that this races, fix it.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# -&amp;gt; "This races, fix it."
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;humanize(text: str) -&amp;gt; str&lt;/code&gt;. Non-string input passes through unchanged.&lt;/p&gt;

&lt;p&gt;As a CLI, it's built to drop into a pipe or a CI gate:&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="c"&gt;# stdin to stdout&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$comment&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | klaussy humanize

&lt;span class="c"&gt;# rewrite a file in place&lt;/span&gt;
klaussy humanize NOTES.md &lt;span class="nt"&gt;--write&lt;/span&gt;

&lt;span class="c"&gt;# CI gate: exit 1 if the file would change&lt;/span&gt;
klaussy humanize NOTES.md &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;--check&lt;/code&gt; mode is the one I lean on most. It turns "did an AI tell slip into this doc" into a check that fails a pull request instead of a thing someone has to catch by eye.&lt;/p&gt;
&lt;h3&gt;
  
  
  One spec, shared across products
&lt;/h3&gt;

&lt;p&gt;The rules in both layers, the prompt-side block and the CLI, are a faithful port of one source: &lt;code&gt;humanize-comment.js&lt;/code&gt; from the klaussy desktop codebase. (That desktop app is a separate product; this is the open-source &lt;code&gt;klaussy-agents&lt;/code&gt; package. That's the only time I'll mention it.) Porting from one canonical implementation means the prompt rules and the scrubber rules don't drift apart over time, and any pipeline, CI, the desktop app, or your own scripts, can pipe through the same behavior.&lt;/p&gt;
&lt;h3&gt;
  
  
  Comment hygiene, not just prose
&lt;/h3&gt;

&lt;p&gt;The same instinct shows up one layer down, in code review. Beyond the prose tells, the generated &lt;code&gt;review&lt;/code&gt; skill flags excessive or narrating comments in code, the kind that restate what the line does or read like a changelog, and the commit guard blocks committed commented-out code via &lt;code&gt;ruff --select ERA&lt;/code&gt;. The judgment-heavy part lives in the skill where a model can weigh context; the deterministic part lives in the hook. Same division of labor as the two prose layers: ask where you need judgment, enforce where you can be certain.&lt;/p&gt;
&lt;h2&gt;
  
  
  Clean isn't the same as kind, or short
&lt;/h2&gt;

&lt;p&gt;Removing the tells exposed a second problem I didn't plan for: stripping the filler also strips the softening. Scrub "It's worth noting that this could potentially swallow the error, you may want to wrap it" down to its substance and you get "This swallows the error. Wrap it." That's clean, and on a PR thread it's also a little cold, and cold reads as curt. Review comments are the worst case, because they land on a person's work. A real one, lightly defanged: "Personally I don't find these unit tests useful, because you are mocking everything." The tells aren't the problem there; the framing is.&lt;/p&gt;

&lt;p&gt;So humanizing can't be purely subtractive. After removing what makes prose sound like a machine, the spec adds back what makes it sound like a considerate human, split the same two ways as everything else.&lt;/p&gt;

&lt;p&gt;Prompt-side, a civility floor: critique the work, never the person; prefer a question over a flat verdict; keep it a light touch, not filler praise. It's a floor, not forced warmth, so a review you asked to be blunt stays blunt, it just can't tip into insulting. A second rule covers replies: read the comment you're answering for substance but not temperature, and neutralize its rudeness before drafting, so a hostile thread doesn't prime a hostile reply. And a brevity rule with actual numbers instead of "be concise": a thread reply aims for one sentence, a single review comment for one to five, anything longer gets cut, not summarized.&lt;/p&gt;

&lt;p&gt;Deterministic-side, the scrubber's opener list grew to catch the editorializing lead-ins that prime a dismissive read, "Personally," "Honestly," "Frankly," "IMO," "In my opinion," "If you ask me," so &lt;code&gt;Personally I don't find these useful.&lt;/code&gt; becomes &lt;code&gt;I don't find these useful.&lt;/code&gt; with the same guarantee as the rest, and still never inside code.&lt;/p&gt;

&lt;p&gt;The honest caveat: tone and brevity are mostly judgment, so most of it lives in the soft prompt layer, only the openers are guaranteed.&lt;/p&gt;
&lt;h2&gt;
  
  
  A quick demo
&lt;/h2&gt;

&lt;p&gt;The clearest way to see it is to pipe a comment that has several tells stacked up:&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="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"It's worth noting that this handler swallows the error, wrap it. Hope this helps!"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 | klaussy humanize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The opener gets stripped, the next word re-capitalized, the em-dash becomes a comma, and the trailing scaffolding line is dropped. What comes out reads like a note an engineer left, because the things that made it read like a bot are gone and nothing else was touched.&lt;/p&gt;

&lt;p&gt;The whole pass is pure standard library, no network, no model. The test suite covers every transform above plus the code-preservation cases, ported from the desktop test suite; 137 tests pass overall.&lt;/p&gt;
&lt;h2&gt;
  
  
  What's next, and where the line is
&lt;/h2&gt;

&lt;p&gt;The deliberate limit is the headline tradeoff: deterministic means limited. A regex scrubber catches the reliable tells, but it will not rewrite genuinely awkward phrasing the way an LLM could. That's the trade I chose on purpose, the same property that makes it safe, fast, and incapable of introducing an error is the property that keeps it from doing deeper rewrites. If you want paraphrasing, this isn't that tool, and it's not trying to be.&lt;/p&gt;

&lt;p&gt;A couple of other honest limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The prompt layer is soft.&lt;/strong&gt; The &lt;code&gt;{{HUMANIZE}}&lt;/code&gt; block depends on the model following instructions. Two layers because neither alone is enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's opinionated.&lt;/strong&gt; Some people like em-dashes. The scrubber normalizes them, and that's a stance. It's also open code, so if you disagree, the rules are right there to edit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope is prose, not code.&lt;/strong&gt; By design it won't touch anything inside a code block. The flip side is that it won't fix a tell living inside a code comment unless you wire it to do so.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are things I'm hiding. They're the consequences of picking "safe and predictable" over "clever and risky" for text that goes in front of your team.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;klaussy-agents
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"It's worth noting that this races, fix it. Hope this helps!"&lt;/span&gt; | klaussy humanize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Repo and docs: &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;github.com/steph-dove/klaussy-agents&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/steph-dove" rel="noopener noreferrer"&gt;
        steph-dove
      &lt;/a&gt; / &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;
        klaussy-agents
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Claude Code boilerplate generator. One command to make any repo Claude Code-ready.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;klaussy&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Multi-agent repo boilerplate generator. One command to make any repo ready for
&lt;strong&gt;Claude Code, Gemini CLI, Cursor, Codex, and GitHub Copilot&lt;/strong&gt; — each gets the
same conventions and the same workflow skills in its own native format.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;pip install klaussy-agents&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Requires &lt;a href="https://pypi.org/project/klaussy-repo-conventions/" rel="nofollow noopener noreferrer"&gt;klaussy-repo-conventions&lt;/a&gt; (installed automatically).&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick Start&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; your-repo
klaussy init&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;That's it. You'll be prompted for your base branch (auto-detects &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;, etc.), then klaussy generates everything.&lt;/p&gt;
&lt;p&gt;By default klaussy bootstraps &lt;strong&gt;all&lt;/strong&gt; supported agents from the same conventions. To narrow to a subset, pass &lt;code&gt;--agents&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;klaussy init                                   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; all agents (default)&lt;/span&gt;
klaussy init --agents claude                   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Claude Code only&lt;/span&gt;
klaussy init --agents claude,gemini,cursor     &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; a subset&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;See &lt;a href="https://github.com/steph-dove/klaussy-agents#multi-agent-targets" rel="noopener noreferrer"&gt;Multi-agent targets&lt;/a&gt; for what each agent gets.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What Gets Generated&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;klaussy discovers your repo's conventions once, then writes — &lt;strong&gt;for every selected agent (all five by default)&lt;/strong&gt; — that agent's native conventions file, the workflow skills, stack-appropriate permissions…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;If you've got an AI tell that drives you up the wall and the scrubber doesn't catch it yet, open an issue with the before/after, that's exactly the kind of case I want to see.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Building repo conventions aware coding agents</title>
      <dc:creator>Stephanie Dover</dc:creator>
      <pubDate>Thu, 18 Jun 2026 01:51:18 +0000</pubDate>
      <link>https://dev.to/stephanie_dover_b49c2e8d1/building-repo-conventions-aware-coding-agents-1ep8</link>
      <guid>https://dev.to/stephanie_dover_b49c2e8d1/building-repo-conventions-aware-coding-agents-1ep8</guid>
      <description>&lt;p&gt;&lt;em&gt;Written by Stephanie Dover, Software Engineer 10+ YOE, ex GitHub, Twitch, Microsoft. Creator of Klaussy.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/stephaniedover/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://github.com/steph-dove" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.klaussy.com/" rel="noopener noreferrer"&gt;Klaussy Desktop&lt;/a&gt; · &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;Klaussy Agents&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;klaussy-agents&lt;/strong&gt;, an open-source CLI (&lt;code&gt;pip install klaussy-agents&lt;/code&gt;) that reads your repo's conventions once and scaffolds repo-aware skills for five coding agents: Claude Code, Gemini CLI, Cursor, Codex, and GitHub Copilot. The interesting part is the adaptation layer: one Claude-authored &lt;code&gt;SKILL.md&lt;/code&gt; gets rewritten into each agent's native form, including how sub-agent and plan-mode wording maps to each agent's own primitive. It's a scaffolder, not a runtime, it's at v0.3.2, and the per-agent translation has real seams. Repo: github.com/steph-dove/klaussy-agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Each AI coding agent carries repo context its own way. Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt;, Gemini CLI reads &lt;code&gt;GEMINI.md&lt;/code&gt;, Codex reads &lt;code&gt;AGENTS.md&lt;/code&gt;, Cursor reads &lt;code&gt;.cursor/rules&lt;/code&gt;, and Copilot reads &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;. On top of that, each one has its own folder of reusable "skills" or "commands." So the conventions you write for one agent, and any review or plan workflow you tune in it, don't transfer to the others. Point a second agent at the same repo and it starts from zero, because it's reading a file the first agent never wrote.&lt;/p&gt;

&lt;p&gt;You can keep all five context files and skill folders in sync by hand, but that's busywork that rots the first time one gets edited and the rest don't. The practical outcome is that whichever agent you open is the one that knows the least about your repo, unless you've redone the setup five times.&lt;/p&gt;

&lt;p&gt;What makes a real fix possible is that all five agents now read the same open &lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; &lt;code&gt;SKILL.md&lt;/code&gt; format. One folder format, genuinely portable. So a tool can discover the repo's conventions once and emit skills, adapted, into every agent's native layout. This post is about the adaptation step, because that's where it stops being a clean copy-paste.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why hand-rolling five configs didn't scale for me
&lt;/h2&gt;

&lt;p&gt;To be clear about what the alternatives actually are: hand-maintaining per-agent config works fine when you use one agent. A &lt;code&gt;CLAUDE.md&lt;/code&gt; plus a couple of &lt;code&gt;.claude/skills/&lt;/code&gt; is a perfectly good setup, and if your whole team is on one tool, you don't need any of this.&lt;/p&gt;

&lt;p&gt;Generic scaffolders (cookiecutter and friends) are great for stamping out project structure, but they don't know what a sub-agent is or how Cursor scopes a rule versus how Copilot does. The gap is specifically the multi-agent one: the same review workflow, expressed five different ways, kept in sync forever. This is a translation problem, not a templating problem. The &lt;code&gt;SKILL.md&lt;/code&gt; spec provides a shared source format; the work klaussy does is the discovery, the per-agent adaptation, and the native wiring so I'm not hand-maintaining five copies of the same skill.&lt;/p&gt;

&lt;p&gt;The honest framing is: the spec made portability possible. klaussy does the boring, error-prone part on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The approach
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;klaussy init&lt;/code&gt; does the whole thing in one pass and defaults to all five agents. Under the hood it runs a repo-conventions discovery step once, produces a project-wide &lt;code&gt;CLAUDE.md&lt;/code&gt; plus path-scoped &lt;code&gt;.claude/rules/*.md&lt;/code&gt;, then translates that into each agent's native form. Path-scoped rules map onto each agent's own scoping mechanism: Copilot's &lt;code&gt;.github/instructions/*.instructions.md&lt;/code&gt; with &lt;code&gt;applyTo:&lt;/code&gt; frontmatter, Cursor's &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt; with &lt;code&gt;globs:&lt;/code&gt; frontmatter, and so on.&lt;/p&gt;

&lt;p&gt;The same one-discovery-then-translate shape applies to skills. klaussy ships 11 namespaced workflow skills, written once in Claude Code's syntax, and writes them into each agent's dedicated skills directory. The skills are namespaced &lt;code&gt;&amp;lt;repo&amp;gt;-&amp;lt;skill&amp;gt;&lt;/code&gt; so they don't collide across repos when an agent has several checked out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;klaussy-agents
klaussy init                     &lt;span class="c"&gt;# prompts for base branch, scaffolds all five agents&lt;/span&gt;
klaussy init &lt;span class="nt"&gt;--agents&lt;/span&gt; claude,cursor   &lt;span class="c"&gt;# narrow to a subset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also run individual steps (&lt;code&gt;klaussy skills&lt;/code&gt;, &lt;code&gt;settings&lt;/code&gt;, &lt;code&gt;hooks&lt;/code&gt;, &lt;code&gt;github&lt;/code&gt;, and so on) if you only want part of it. The rest of this post zooms in on the skills step, because the adaptation it does is the part I'd actually want to read about.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  One authored skill, five target dialects
&lt;/h3&gt;

&lt;p&gt;The skills are authored in Claude Code's syntax. That means they lean on a few Claude-specific constructs: &lt;code&gt;&lt;/code&gt;`&lt;code&gt;!&lt;/code&gt; dynamic-shell blocks that run a command and inline its output, parallel sub-agents invoked through the &lt;code&gt;Agent&lt;/code&gt; tool with a &lt;code&gt;subagent_type&lt;/code&gt;, and &lt;code&gt;ExitPlanMode&lt;/code&gt; for plan mode. None of those tokens mean anything to Gemini or Codex verbatim.&lt;/p&gt;

&lt;p&gt;So klaussy doesn't copy the body across. It rewrites each body to capture the same intent in the target agent's terms. Three concrete rewrites happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic shell blocks become explicit instructions.&lt;/strong&gt; A &lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;! &lt;code&gt; block that silently runs &lt;/code&gt;git diff` and inlines the result is rewritten into a plain "run this command and use its output" instruction the other agent will actually follow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path references get retargeted.&lt;/strong&gt; A skill that points at &lt;code&gt;.claude/skills/...&lt;/code&gt; is rewritten to the target agent's own skills directory, so cross-skill references don't dangle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-agent and plan-mode orchestration gets an adaptation note.&lt;/strong&gt; This is the subtle one, and it's the next section.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These directories are the targets, exactly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/skills/     # Claude Code
.gemini/skills/     # Gemini CLI
.cursor/skills/     # Cursor
.agents/skills/     # Codex (neutral .agents/ path)
.github/skills/     # GitHub Copilot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sub-agents and plan mode
&lt;/h3&gt;

&lt;p&gt;Several of the skills orchestrate parallel sub-agents. The review skill, for instance, fans out separate lenses (correctness, architecture, security, scope) and runs them concurrently. In Claude Code that's the &lt;code&gt;Agent&lt;/code&gt; tool with a &lt;code&gt;subagent_type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Different agents handle parallel sub-agents differently. Most have their own model-invocable parallel sub-agent tool, and they name it differently; a given agent or setup might not expose one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code: &lt;code&gt;Agent&lt;/code&gt; / &lt;code&gt;subagent_type&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cursor: &lt;code&gt;Task&lt;/code&gt; (GA)&lt;/li&gt;
&lt;li&gt;Codex: &lt;code&gt;spawn_agent&lt;/code&gt; (GA)&lt;/li&gt;
&lt;li&gt;Gemini CLI: subagents (default-on, toggled under &lt;code&gt;experimental&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;GitHub Copilot: &lt;code&gt;task&lt;/code&gt; / &lt;code&gt;read_agent&lt;/code&gt; (plus an experimental &lt;code&gt;context: fork&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of hardcoding sequential, the adaptation note tells the target agent to map Claude's sub-agent wording onto its own equivalent primitive, and to fall back to sequential execution only if it genuinely has none. The note carries intent ("these lenses are independent, run them in parallel"), not Claude-specific tool names. The same goes for &lt;code&gt;ExitPlanMode&lt;/code&gt;: the note describes the plan-then-confirm intent rather than naming a tool that only Claude has.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Illustrative — exact wording is generated per skill, confirm in the emitted SKILL.md
# Adaptation note (appended when a skill orchestrates sub-agents):
# This skill runs independent lenses in parallel. Use your own
# parallel sub-agent tool (e.g. Task / spawn_agent / subagents / task).
# Only run them sequentially if you have no sub-agent tool.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the part I find genuinely teachable: portability across agents isn't "find and replace the tool name." It's separating intent from primitive, then letting each agent rebind the intent to whatever primitive it owns. The &lt;code&gt;SKILL.md&lt;/code&gt; spec gives you a shared container; it doesn't give you shared tools, so the body has to be written to degrade gracefully.&lt;/p&gt;

&lt;h3&gt;
  
  
  A real skill, so this isn't abstract
&lt;/h3&gt;

&lt;p&gt;To make "what a generated skill actually does" concrete: the review skill triages by diff size, then runs parallel lenses. Beyond the four standard lenses it adds an Agentic/Evals lens when the change touches AI code, and an Architecture-Decision/Design-Doc lens when the PR contains an ADR, RFC, or design doc. It's precision-biased: an empty review (nothing worth flagging) is a valid outcome. Every finding has to name a concrete trigger, and a final validation phase self-refutes false positives before anything is reported.&lt;/p&gt;

&lt;p&gt;That whole workflow is authored once and adapted into all five agents' skills folders. The lens fan-out is exactly the piece that needs the sub-agent translation above; the precision-bias and validation phases are plain prose and carry across unchanged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissions and hooks ride along
&lt;/h3&gt;

&lt;p&gt;The skills step is the headline, but &lt;code&gt;klaussy init&lt;/code&gt; also writes native permission allow-lists per agent (Claude &lt;code&gt;settings.json&lt;/code&gt; allow/deny, Gemini &lt;code&gt;settings.json&lt;/code&gt; &lt;code&gt;tools.allowed&lt;/code&gt;, Cursor &lt;code&gt;permissions.json&lt;/code&gt; &lt;code&gt;terminalAllowlist&lt;/code&gt;, Codex &lt;code&gt;config.toml&lt;/code&gt; approval/sandbox) and tries to keep secrets like &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt;, and &lt;code&gt;credentials*&lt;/code&gt; out of each agent's reach using that agent's own mechanism.&lt;/p&gt;

&lt;p&gt;It also wires two cross-agent hooks: a git-commit guard that runs your detected format and lint before a commit, and a read-injection guard that scans file and fetch content for prompt-injection markers. The guard scripts pull the command or path out of whatever agent's hook payload they're handed and block with &lt;code&gt;exit 2&lt;/code&gt; plus stderr, which every supported agent honors. They're pure-stdlib and hardened so any parse error falls back to allow rather than crashing. The current suite is 130 tests passing, ruff clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick demo
&lt;/h2&gt;

&lt;p&gt;Point it at a repo and run it:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It prompts for the base branch, then scaffolds conventions files, skills, permissions, and hooks for all five agents. If you only want a subset, name them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;klaussy init &lt;span class="nt"&gt;--agents&lt;/span&gt; claude,cursor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, opening any of the configured agents in the repo means it already has the conventions file it looks for and the namespaced skills in its own directory. You're still installing and running the agents yourself; klaussy generates the files they read.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next, and where the seams are
&lt;/h2&gt;

&lt;p&gt;A deep-dive owes you the honest version, so here's what doesn't fully reach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The skills are Claude-authored then adapted, not hand-tuned per agent.&lt;/strong&gt; The adaptation captures intent, and the sub-agent note tells each agent to use its own primitive, but it's a translation, not five bespoke skill sets written from scratch for each agent's quirks. If you want a skill perfectly idiomatic to Codex, you may still tweak it by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hook coverage is uneven, by design.&lt;/strong&gt; The read-injection guard is wired for Claude, Gemini, and Cursor only. Codex exposes no pre-file-read hook event, and Copilot's &lt;code&gt;preToolUse&lt;/code&gt; is fail-closed with unconfirmed read-tool args, so those two get the commit guard only. klaussy logs that rather than pretending the guard is everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret exclusion isn't universally possible.&lt;/strong&gt; Codex's sandbox governs writes and network, not reads, so there's no read-exclusion there; Copilot content-exclusion is a GitHub setting, not a committed file. klaussy says so instead of faking a &lt;code&gt;.ignore&lt;/code&gt; that wouldn't do anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It leans on the SKILL.md spec being honored.&lt;/strong&gt; Portability is real today, but the spec and the agents are young. If an agent changes how it reads skills, klaussy has to track it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those are agents being "equal." They're not. The whole point of the adaptation layer is that the agents differ and the skill set has to bend to each one. klaussy is a scaffolder: it generates files the agents read, it does not run the agents or change their model quality.&lt;/p&gt;

&lt;p&gt;(One disambiguation: there's a separate, paid klaussy desktop app. Different product, same developer. This post is only about the open-source &lt;code&gt;klaussy-agents&lt;/code&gt; CLI.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/steph-dove/klaussy-agents" rel="noopener noreferrer"&gt;github.com/steph-dove/klaussy-agents&lt;/a&gt;&lt;br&gt;
Install: &lt;code&gt;pip install klaussy-agents&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% github steph-dove/klaussy-agents %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run more than one coding agent in the same repo, I'd like to know which skill survived the translation cleanly and which one came out awkward in your agent of choice. Open an issue or drop a comment with what broke.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>How I made one desktop app drive four AI coding agent CLIs</title>
      <dc:creator>Stephanie Dover</dc:creator>
      <pubDate>Mon, 08 Jun 2026 02:45:26 +0000</pubDate>
      <link>https://dev.to/stephanie_dover_b49c2e8d1/how-i-made-one-desktop-app-drive-four-ai-coding-agent-clis-2gi</link>
      <guid>https://dev.to/stephanie_dover_b49c2e8d1/how-i-made-one-desktop-app-drive-four-ai-coding-agent-clis-2gi</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;Klaussy&lt;/strong&gt;, a desktop app that runs AI coding-agent CLIs in parallel across git worktrees and pairs them with a GitHub PR review surface. The v0.3.0 release (out June 5) replaced its hard dependency on Claude Code with a provider registry, so it now drives &lt;strong&gt;Claude Code, OpenAI Codex, Google Gemini, or GitHub Copilot&lt;/strong&gt; — your pick per task. This post covers how the registry works, the side-by-side terminal model, and where the deeper AI features are still uneven across agents. It's closed-source and in beta. Site: &lt;a href="https://www.klaussy.com/" rel="noopener noreferrer"&gt;klaussy.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Until a few weeks ago, Klaussy had one ugly constraint baked into its core: it only worked if you used Claude Code. The early-access docs said it outright — if you didn't run Claude Code, there was nothing in the app for you.&lt;/p&gt;

&lt;p&gt;That was fine for the first users, who mostly did run Claude Code. But it ruled out a large chunk of the people the app is actually for: engineers who already use &lt;em&gt;an&lt;/em&gt; agent CLI daily, have two or three tasks in flight at once, and want a structured way to run them without juggling branches in a single clone. A lot of those people had standardized on Codex, or Gemini, or Copilot — often for cost, procurement, or data-handling reasons that had nothing to do with the tool's quality. For them, Klaussy was "the Claude thing," and that was the end of the conversation.&lt;/p&gt;

&lt;p&gt;The pain Klaussy targets isn't agent-specific. It's the workflow around the agent: starting task B while the agent grinds on task A, hopping between the terminal (where the agent lives) and the browser (where PR review lives), and triaging a CI failure across GitHub, the terminal, and the editor. None of that cares which agent you run. So hard-wiring one agent into the foundation was a self-inflicted limit. v0.3.0 is the release that removed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a single-agent design was the wrong foundation
&lt;/h2&gt;

&lt;p&gt;The original architecture made the easy assumption: there's one agent CLI, so call it directly. Spawn &lt;code&gt;claude&lt;/code&gt;, parse its output, wire its session resume into the terminal manager. Every AI surface in the app — the interactive terminal, the PR-review actions, the CI-failure debugger — reached for Claude by name.&lt;/p&gt;

&lt;p&gt;That works right up until you want a second agent. Then every one of those call sites is a place that knows too much. The four agent CLIs differ in obvious ways (&lt;code&gt;@anthropic-ai/claude-code&lt;/code&gt; vs &lt;code&gt;@openai/codex&lt;/code&gt; vs &lt;code&gt;@google/gemini-cli&lt;/code&gt; vs &lt;code&gt;@github/copilot&lt;/code&gt;) and in annoying ones: different model-selection flags, different session-resume mechanics, different output streams to parse, different auth quirks. You can't paper over that with a single &lt;code&gt;if agent == "codex"&lt;/code&gt; branch sprinkled everywhere — you end up with the same conditional copied across a dozen files, and adding a fifth agent later means finding all of them again.&lt;/p&gt;

&lt;p&gt;The terminal multiplexers people would otherwise reach for (tmux, zellij) don't help here either. They'll give you N shells in one window, but they don't know what a git worktree is, what an agent session is, or what state a PR review is in. Klaussy ties each terminal to a specific agent instance, a branch, and a worktree — so the abstraction it needed was a clean seam between "which agent" and "what we're asking the agent to do."&lt;/p&gt;

&lt;h2&gt;
  
  
  The approach
&lt;/h2&gt;

&lt;p&gt;The fix was a provider registry: one module that owns everything agent-specific, and a rule that nothing outside it hard-codes an agent. Each provider declares its npm package, how to launch it, which models it exposes, and how its output should be parsed. The rest of the app asks the registry for "the current agent" and works against that interface.&lt;/p&gt;

&lt;p&gt;On top of the registry sits a small amount of UI: a global default agent you set once, plus a per-action picker so you can override it for a single task. Set Gemini as your default and every agent action follows it, persisting across restarts; reach for Codex on one specific worktree without changing the global setting. The orchestration layer above — parallel worktrees, one task per terminal, the PR review surface — didn't change. It just stopped caring which agent was underneath.&lt;/p&gt;

&lt;p&gt;The honest version of this story is that the registry isn't uniformly deep yet. Phase 1 nailed the parts every agent shares — launching, switching, resuming, running two side by side. The parts that require parsing each agent's particular output stream are mature on Claude and still being verified on the other three. More on that below, because it's the most important caveat in the release.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The provider registry
&lt;/h3&gt;

&lt;p&gt;Every AI surface in the app now routes through the registry instead of calling an agent by name. A provider entry knows its npm package, its launch command, and its model list. Adding agent number five means adding one entry, not editing a dozen call sites.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Illustrative — confirm exact API in docs.&lt;/span&gt;
&lt;span class="c1"&gt;// Conceptual shape of the provider registry (main/state/ai-providers.js).&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PROVIDERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;claude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@anthropic-ai/claude-code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sonnet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;haiku&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;codex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@openai/codex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-5.5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-5.4-mini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;gemini&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/gemini-cli&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.5-flash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3-pro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;copilot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@github/copilot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Model selection is verified for three of the four. Claude takes &lt;code&gt;--model&lt;/code&gt; aliases (opus/sonnet/haiku), Codex exposes gpt-5.5 and gpt-5.4-mini, and Gemini offers its 2.5/3 flash and pro tiers. Copilot is Default-model-only in v0.3.0 — its model slugs aren't verified yet, so the picker doesn't pretend to offer a choice it can't honor. That's a deliberate "show what's real" call rather than a missing feature dressed up as one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel worktrees, one window
&lt;/h3&gt;

&lt;p&gt;Each task runs in its own git worktree, with its own pseudo-terminal via &lt;code&gt;node-pty&lt;/code&gt;, surfaced in the same window as columns, a grid, or a single pane. This is the part that replaces the &lt;code&gt;git worktree&lt;/code&gt; + tmux + a handful of &lt;code&gt;gh&lt;/code&gt; aliases that an engineer would otherwise script themselves. The agent for each terminal is whatever the registry hands back, so a column running Claude and a column running Gemini coexist without either knowing about the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the same work in two agents at once
&lt;/h3&gt;

&lt;p&gt;Because the registry decouples agent from task, the worktree Actions dropdown can spawn a sibling task in the &lt;em&gt;same&lt;/em&gt; worktree on a &lt;em&gt;different&lt;/em&gt; agent. You can hand the same change to two agents side by side and compare what they do — useful when you're still forming an opinion about which agent is better at a given kind of work.&lt;/p&gt;

&lt;p&gt;One sharp edge worth naming: running two Codex sessions concurrently can invalidate each other's rotating OAuth tokens. Klaussy warns you before it starts a second concurrent Codex session rather than letting it silently break. Codex's auth model, not a Klaussy choice — but the kind of thing the orchestration layer has to know about, which is exactly why the registry exists.&lt;/p&gt;

&lt;h3&gt;
  
  
  The PR review surface
&lt;/h3&gt;

&lt;p&gt;Separately from the terminals, Klaussy renders a GitHub PR without a local checkout: Files, Conversation, Checks, and AI Review tabs. The inline review composer batches comments and submits them in one round trip, and per-finding state (Ignore / Add to PR / Implement / Investigate / Ask) persists across sessions. One click materializes a PR into a worktree plus a task when you do want it locally. There's a built-in Monaco editor with LSP diagnostics so you can edit and commit straight from the diff.&lt;/p&gt;

&lt;p&gt;This is where the maturity gap matters most, so I'll be plain about it: the review actions, the CI-failure auto-debug, Implement, and Ask are most battle-tested on Claude. The non-Claude output parsers for those headless surfaces are documented but still being verified in the shipped code. The interactive terminal, agent switching, resume, and side-by-side work across all four agents today. The deep AI surfaces on Codex, Gemini, and Copilot are the path I trust least, and I'd rather you know that going in than discover it on a real PR.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional on-device autocomplete
&lt;/h3&gt;

&lt;p&gt;There's also inline tab-autocomplete that runs entirely on your machine via local Ollama, using &lt;code&gt;qwen2.5-coder:1.5b&lt;/code&gt; at roughly 100ms latency. Nothing leaves the laptop per keystroke. It's opt-in and costs about a 2 GB download (the Ollama runtime plus the model weights); without it you get a free word-based completer. This is the one piece of the app that does its own inference instead of delegating to your agent CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick demo
&lt;/h2&gt;

&lt;p&gt;The setup before first launch is real and worth stating plainly. You need Node.js 18+, the GitHub CLI authenticated, and at least one of the four supported agent CLIs installed and authenticated:&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="c"&gt;# Illustrative — Klaussy surfaces install commands in a setup dialog;&lt;/span&gt;
&lt;span class="c"&gt;# it can detect missing CLIs but cannot bootstrap your auth.&lt;/span&gt;
npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @anthropic-ai/claude-code   &lt;span class="c"&gt;# or @openai/codex,&lt;/span&gt;
                                     &lt;span class="c"&gt;# @google/gemini-cli, @github/copilot&lt;/span&gt;
gh auth login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a worktree is open, picking an agent and spawning a sibling task on a second agent both happen from the worktree's Actions dropdown — no config files, no per-project agent setup. The agent you choose becomes the global default and sticks across restarts until you change it.&lt;/p&gt;

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

&lt;p&gt;A few things are honestly incomplete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-agent maturity is uneven.&lt;/strong&gt; Running, switching, resuming, and side-by-side work on all four agents. The deeper AI surfaces (PR review, Implement, CI-debug, Ask) are most proven on Claude and still being verified on Codex, Gemini, and Copilot. Treat Claude as the battle-tested path today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The built-in flow prompts are still Claude-flavored.&lt;/strong&gt; The Plan/Debug/Review slash-command bodies were written for Claude. They run on the other agents but aren't tuned to them yet. Per-agent tuning is a follow-up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It doesn't replace your agent.&lt;/strong&gt; Klaussy does not bundle and mark up agent access, you use your account that you already have with one of the supported agents. Klaussy is an developer productivity app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no Klaussy server in any of this. Data flows from your own agent CLI to that agent's provider, from your own &lt;code&gt;gh&lt;/code&gt; to GitHub, and optionally to local Ollama. Pricing is a one-time $39 founder license (rising later), or $349 / $599 for 5 / 10 seats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Site and downloads: &lt;a href="https://www.klaussy.com/" rel="noopener noreferrer"&gt;klaussy.com&lt;/a&gt;&lt;br&gt;
Early-access discussion: &lt;a href="https://discord.gg/ZxNhsuMyYu" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've tried wiring multiple agent CLIs into one workflow yourself, I'd genuinely like to know which agent you trust for which job — drop a comment.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
