<?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: SeungHo Lee</title>
    <description>The latest articles on DEV Community by SeungHo Lee (@dev_sh).</description>
    <link>https://dev.to/dev_sh</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%2F4012843%2F640ada14-8eab-4af1-9d27-b4c08d90e3af.png</url>
      <title>DEV Community: SeungHo Lee</title>
      <link>https://dev.to/dev_sh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dev_sh"/>
    <language>en</language>
    <item>
      <title>I stopped babysitting my AI frontend agent — so I built it guardrails</title>
      <dc:creator>SeungHo Lee</dc:creator>
      <pubDate>Fri, 03 Jul 2026 01:10:31 +0000</pubDate>
      <link>https://dev.to/dev_sh/i-stopped-babysitting-my-ai-frontend-agent-so-i-built-it-guardrails-57of</link>
      <guid>https://dev.to/dev_sh/i-stopped-babysitting-my-ai-frontend-agent-so-i-built-it-guardrails-57of</guid>
      <description>&lt;p&gt;Claude writes frontend code faster than I can read it. That turned out to be the problem.&lt;/p&gt;

&lt;p&gt;Here is a normal afternoon from a few weeks ago. I ask for a feature, watch a dozen files show up, and then spend the next twenty minutes playing security guard. Did it just run &lt;code&gt;git add .&lt;/code&gt; and sweep my &lt;code&gt;.env&lt;/code&gt; into the commit? Is that a real component or another screen of purple gradients and drop shadows? Did it write the tests, or just tell me it did? Did it set &lt;code&gt;strict: false&lt;/code&gt; somewhere to make a red squiggle go away?&lt;/p&gt;

&lt;p&gt;Generating the code was basically free. The watching wasn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So I built fe-rail: a Claude Code plugin that runs my frontend work through a fixed spec → build → review → PR loop, blocks the moves I'm scared of, and only stops to ask me two things.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbuxdvm49hsyact4pilj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbuxdvm49hsyact4pilj.png" alt=" " width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually wanted
&lt;/h2&gt;

&lt;p&gt;Not more autonomy. Less.&lt;/p&gt;

&lt;p&gt;Most of the agent tooling I looked at was trying to give the model more room to run. I wanted the opposite. I already have a process I trust when I write frontend by hand: write a short spec, build the types first, review for the things that actually bite (types, performance, accessibility, plain code quality), then open a clean PR. I just wanted the agent to follow it every time, without me hovering over its shoulder.&lt;/p&gt;

&lt;p&gt;The other half was fear. Some mistakes are cheap to undo. A force push over a colleague's branch, or a committed &lt;code&gt;.env&lt;/code&gt;, is not. I wanted those to be impossible, not just discouraged.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a run looks like
&lt;/h2&gt;

&lt;p&gt;One command, two questions:&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;claude
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /fe-rail:fe-spec

&lt;span class="o"&gt;[&lt;/span&gt;fe-spec] Analyzing requirements... &lt;span class="o"&gt;(&lt;/span&gt;fe-analyst, fe-architect&lt;span class="o"&gt;)&lt;/span&gt;
✔ feature.md generated — 7 sections, 3 open questions resolved

Next step?
  ❯ Full auto &lt;span class="o"&gt;(&lt;/span&gt;recommended&lt;span class="o"&gt;)&lt;/span&gt; — hand off to fe-start automatically
    Build only — I&lt;span class="s1"&gt;'ll review the spec first
    Revise spec

&amp;gt; Full auto

[fe-start] Phase 2 — implementing types → hooks → components → tests
✔ 12 files created, tsc clean, lint clean, 8 tests passing

Commit and open a PR?
  ❯ Yes — split by type (feat/fix/test) and open a draft PR
    No — leave changes uncommitted

&amp;gt; Yes

✔ 2 commits created
✔ Pushed to feat/product-search-autocomplete
✔ Draft PR: https://github.com/you/your-app/pull/42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That is the whole shape of it. Two human check-ins, "Implement?" and "Commit?". Everything between them runs on its own.&lt;/p&gt;

&lt;p&gt;Here is roughly what moved off my plate:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;With fe-rail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Re-reading every diff for a stray &lt;code&gt;git add .&lt;/code&gt; or force push&lt;/td&gt;
&lt;td&gt;Blocked at the hook, before it runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Did it actually write tests?"&lt;/td&gt;
&lt;td&gt;The build ends in tests; the quality gate re-checks changed files on stop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Catching generic gradient-and-shadow UI by eye&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;design-nudge&lt;/code&gt; warns, or a &lt;code&gt;DESIGN.md&lt;/code&gt; sets the rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Splitting commits and writing the PR body myself&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fe-git-operator&lt;/code&gt; and &lt;code&gt;fe-pr-author&lt;/code&gt; do it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;

&lt;p&gt;fe-rail is five slash commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/fe-rail:fe-spec&lt;/code&gt; turns a rough request into a structured spec, then asks whether to keep going.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fe-rail:fe-build&lt;/code&gt; implements it in a fixed order: types, then logic, then components, then tests.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fe-rail:fe-review&lt;/code&gt; runs a four-axis review: types, performance, a11y, and quality.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fe-rail:fe-start feature.md&lt;/code&gt; chains all of that through to a PR.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fe-rail:fe-doc-sync&lt;/code&gt; reads your project and suggests updates to its &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;README&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run them one at a time when you want to drive, or let &lt;code&gt;fe-start&lt;/code&gt; take the wheel.&lt;/p&gt;

&lt;p&gt;The spec step is not limited to text. If the feature lives in a Figma file or a screenshot, &lt;code&gt;fe-vision&lt;/code&gt; reads it into concrete screens and states. If it arrives as a slide deck, &lt;code&gt;fe-deck-reader&lt;/code&gt; breaks it into screens and the flows between them. Later, at review time, &lt;code&gt;fe-vision&lt;/code&gt; can also compare a screenshot of what got built against the original reference and flag where the two drift apart. That one still surprises me when it works.&lt;/p&gt;

&lt;p&gt;There is a small detail I care about in how the two check-ins stay at two. Picking "Full auto" at the spec gate counts as the "Implement?" yes, so &lt;code&gt;fe-start&lt;/code&gt; only has to ask "Commit?" later on. I did not want the convenience of a one-command run to quietly cost me a checkpoint.&lt;/p&gt;
&lt;h2&gt;
  
  
  The part I actually care about: hooks
&lt;/h2&gt;

&lt;p&gt;Speed was never the hard part. Trust was. So the piece I spent the most time on is the hook layer, and it runs on one rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Block dangers. Warn on quality.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Blocking means the tool call never happens (the hook exits with code 2). Warning means it happens and I get a note on stderr. A few of the blockers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;guard.sh&lt;/code&gt; stops &lt;code&gt;git add .&lt;/code&gt;, force pushes, &lt;code&gt;--no-verify&lt;/code&gt;, &lt;code&gt;git reset --hard&lt;/code&gt;, &lt;code&gt;git checkout/restore .&lt;/code&gt;, and &lt;code&gt;rm -rf /&lt;/code&gt; before they run.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;write-guard.sh&lt;/code&gt; refuses to create or edit &lt;code&gt;.env&lt;/code&gt; files, private keys, and credential dumps. It still lets through the source files you would expect, like &lt;code&gt;.env.example&lt;/code&gt; or a &lt;code&gt;CredentialForm.tsx&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;config-protection.sh&lt;/code&gt; blocks edits that weaken your setup. The agent cannot flip &lt;code&gt;strict: false&lt;/code&gt;, drop in a &lt;code&gt;@ts-nocheck&lt;/code&gt;, or switch a linter's recommended rules off to make the errors disappear.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The warnings are lighter. &lt;code&gt;design-nudge.sh&lt;/code&gt; pings me when an edit reaches for the default AI look: heavy arbitrary shadows, the obligatory indigo gradient. &lt;code&gt;quality-gate.sh&lt;/code&gt; runs the linter and type check on changed files when the session ends and prints what it finds.&lt;/p&gt;

&lt;p&gt;There is an escape hatch, because there has to be one. You can turn the whole thing down with &lt;code&gt;FE_RAIL_HOOK_PROFILE=minimal&lt;/code&gt;, or switch off individual hooks by name with &lt;code&gt;FE_RAIL_DISABLED_HOOKS&lt;/code&gt;. But &lt;code&gt;minimal&lt;/code&gt; does not touch the safety blockers. If you want those gone, you have to name them one by one. Quality is negotiable. Overwriting your &lt;code&gt;.env&lt;/code&gt; is not.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multiple agents, on purpose
&lt;/h2&gt;

&lt;p&gt;Each phase hands off to a small sub-agent in its own context instead of stuffing everything into one long session. Requirements analysis goes to &lt;code&gt;fe-analyst&lt;/code&gt;. Architecture questions go to &lt;code&gt;fe-architect&lt;/code&gt;. The review fans out to &lt;code&gt;fe-reviewer&lt;/code&gt;, plus a dedicated &lt;code&gt;fe-a11y-auditor&lt;/code&gt; and &lt;code&gt;fe-perf-auditor&lt;/code&gt;. The main thread stays readable because the noisy work happens off to the side.&lt;/p&gt;

&lt;p&gt;One choice I still go back and forth on: the model tiers are aliases, not pinned versions. High-judgment agents ask for &lt;code&gt;opus&lt;/code&gt;, cheap exploration runs on &lt;code&gt;haiku&lt;/code&gt;, and the rest use &lt;code&gt;sonnet&lt;/code&gt;. When a new tier ships, the agents pick it up on their own. Free upgrades, sure. It also means the same plugin version can behave a little differently a month from now, which is a strange thing to sign up for.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;That aliasing decision is what forced the most useful part of the project: a regression harness. &lt;code&gt;eval/run.sh&lt;/code&gt; checks the hooks, the profile toggles, and the plugin's own config without calling a live model, so I find out when a model update or a config change quietly breaks a guard. It exits non-zero on failure, so CI can gate on it.&lt;/p&gt;

&lt;p&gt;The design guard taught me something too. &lt;code&gt;design-nudge&lt;/code&gt; nagged me constantly at first, until I added a &lt;code&gt;DESIGN.md&lt;/code&gt; to the project. That is the actual design: once a project states its own rules, the generic nudging goes quiet and the reviewer enforces your rules instead of my defaults.&lt;/p&gt;

&lt;p&gt;I will be honest about the limits. It is opinionated: TypeScript in strict mode, Next.js App Router or a Vite SPA, Tailwind, shadcn/ui. Off that stack, most of the value drains away. It is young (v1.10 as I write this), and it is frontend only by design. It is really just my own frontend process, written down and made hard to skip.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you are on Claude Code and roughly that stack:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace add sh5623/fe-rail
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;fe-rail@fe-rail-market
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Two things are worth doing right after install. Give the project a &lt;code&gt;CLAUDE.md&lt;/code&gt; (run &lt;code&gt;/init&lt;/code&gt;) so the agents are not reasoning blind about your stack. And allow &lt;code&gt;Bash(git *)&lt;/code&gt; and &lt;code&gt;Bash(gh pr *)&lt;/code&gt; in your settings so PR creation does not prompt you at every step.&lt;/p&gt;

&lt;p&gt;It is open source under MIT:&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/sh5623" rel="noopener noreferrer"&gt;
        sh5623
      &lt;/a&gt; / &lt;a href="https://github.com/sh5623/fe-rail" rel="noopener noreferrer"&gt;
        fe-rail
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Claude Code plugin — spec → build → review → PR harness for Next.js, Vite SPA &amp;amp; TypeScript (Tailwind, shadcn/ui)
    &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;fe-rail&lt;/h1&gt;
&lt;/div&gt;

&lt;div&gt;
  &lt;a href="https://github.com/sh5623/fe-rail/README.ko.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/95130dfd7592caa9e30a2e7fc91b815add3b95dcf6da95559e5c656216a2d3f9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c616e672ded959ceab5adec96b42d6c69676874677265793f7374796c653d666c61742d737175617265" alt="한국어"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/sh5623/fe-rail/README.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2c10b7e89ab0f84c58cda2d5ee1b0eeab1a0f39439a9d7c7ff787cae7d6dce19/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c616e672d456e676c6973682d626c75653f7374796c653d666c61742d737175617265" alt="English"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/sh5623/fe-rail/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/422db9fd40f5831c765cf6530b6750c081b696bd18d904cf89554df98c676277/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e3f7374796c653d666c61742d737175617265" alt="MIT License"&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Frontend-focused Claude Code plugin
Automated spec → build → review → PR workflow for Next.js App Router / Vite SPA (TanStack Router · React Router 7) + TypeScript, with full Tailwind v3/v4 / shadcn/ui support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&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;claude

/plugin marketplace add sh5623/fe-rail
/plugin install fe-rail@fe-rail-market&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/sh5623/fe-rail/docs/assets/workflow.svg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsh5623%2Ffe-rail%2FHEAD%2Fdocs%2Fassets%2Fworkflow.svg" alt="fe-rail workflow: spec, build, review, PR"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;$ claude
&amp;gt; /fe-rail:fe-spec

[fe-spec] Analyzing requirements... (fe-analyst, fe-architect)
✔ feature.md generated — 7 sections, 3 open questions resolved

Next step?
  ❯ Full auto (recommended) — hand off to fe-start automatically
    Build only — I'll review the spec first
    Revise spec

&amp;gt; Full auto

[fe-start] Phase 2 — implementing types → hooks → components → tests
✔ 12 files created, tsc clean, lint clean, 8 tests passing

Commit and open a PR?
  ❯ Yes — split by type (feat/fix/test) and open a draft PR
    No — leave changes uncommitted

&amp;gt; Yes

✔ 2 commits created
✔ Pushed to feat/product-search-autocomplete
✔ Draft PR: https://github.com/you/your-app/pull/42
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Two…&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/sh5623/fe-rail" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;I would like to know where it breaks for you. What is the one thing your AI coding setup does that you wish it would ask you about first?&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>frontend</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
