<?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: AgentKit Drawer</title>
    <description>The latest articles on DEV Community by AgentKit Drawer (@eli_agentkit).</description>
    <link>https://dev.to/eli_agentkit</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%2F4197150%2F7b667bc9-5daf-4d5b-8a95-095bda904904.png</url>
      <title>DEV Community: AgentKit Drawer</title>
      <link>https://dev.to/eli_agentkit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eli_agentkit"/>
    <language>en</language>
    <item>
      <title>Stop AI agents creating Supabase tables without RLS</title>
      <dc:creator>AgentKit Drawer</dc:creator>
      <pubDate>Mon, 26 Oct 2026 14:29:40 +0000</pubDate>
      <link>https://dev.to/eli_agentkit/stop-ai-agents-creating-supabase-tables-without-rls-5f3</link>
      <guid>https://dev.to/eli_agentkit/stop-ai-agents-creating-supabase-tables-without-rls-5f3</guid>
      <description>&lt;p&gt;AI coding agents write a clean &lt;code&gt;create table&lt;/code&gt;, add a foreign key, and move on. They skip row-level security because the query works from the SQL editor and the type generator is happy. That table is then reachable through the Data API by any role that has a grant. The &lt;a href="https://supabase.com/docs/guides/api/securing-your-api" rel="noopener noreferrer"&gt;Supabase API security guide&lt;/a&gt; is blunt: tables exposed without RLS can be accessed by any role with matching grants.&lt;/p&gt;

&lt;p&gt;This post is the RLS rule itself: enable it in the same migration, write policies, and never turn it off to “make it work”. For the rest of a Next.js + Supabase Cursor setup (auth helpers, env, Stripe), use &lt;a href="https://agentkitdrawer.com/cursor-rules-nextjs-supabase/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44" rel="noopener noreferrer"&gt;Cursor rules for Next.js + Supabase&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents leave RLS off
&lt;/h2&gt;

&lt;p&gt;Tables created in the Supabase Dashboard have RLS enabled by default. Tables created in a migration, the SQL Editor, or an ORM dump do not. Agents almost always use SQL. Postgres also starts a new table with RLS disabled, so “it works” is the default until someone adds &lt;code&gt;enable row level security&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The happy path hides it.&lt;/strong&gt; The agent uses the service-role key in a seed script. That key bypasses RLS, so every query succeeds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The error looks like a bug.&lt;/strong&gt; Once RLS is on and there is no policy, the Data API returns no rows. Agents “fix” that by disabling RLS instead of writing a policy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grants are leftover.&lt;/strong&gt; &lt;code&gt;anon&lt;/code&gt; and &lt;code&gt;authenticated&lt;/code&gt; often still have &lt;code&gt;select&lt;/code&gt; (or worse). RLS off plus a grant is a public table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Official reminder: enable RLS on every table and view the Data API exposes, then write policies. See &lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noopener noreferrer"&gt;Row Level Security&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule: same migration, or the table does not ship
&lt;/h2&gt;

&lt;p&gt;Put this in &lt;code&gt;AGENTS.md&lt;/code&gt; and in a path-scoped Cursor rule on &lt;code&gt;supabase/**&lt;/code&gt;. The agent should refuse to commit a &lt;code&gt;create table&lt;/code&gt; that does not also enable RLS and define policies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Migrations&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;row-level&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;security"&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;supabase/**&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;For each newly created table, enable row level security and define its policies in the same migration.&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Policies use (select auth.uid()) for performance; separate policies per command (select/insert/update/delete).&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Add indexes on every column used in a policy or foreign key.&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Never disable RLS to 'make it work'. If a query fails, fix the policy and explain it.&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Regenerated types after schema changes go in the project's existing types file.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same bullets live as a free file on GitHub: &lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/nextjs-supabase-rls.mdc" rel="noopener noreferrer"&gt;nextjs-supabase-rls.mdc&lt;/a&gt;. For Windsurf, drop the front matter into &lt;code&gt;.windsurf/rules/&lt;/code&gt;; see &lt;a href="https://agentkitdrawer.com/windsurf-rules/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44" rel="noopener noreferrer"&gt;Windsurf rules setup&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the migration should look like
&lt;/h2&gt;

&lt;p&gt;Enable RLS, tighten grants, then add one policy per command. Separate policies keep &lt;code&gt;using&lt;/code&gt; (read/filter) and &lt;code&gt;with check&lt;/code&gt; (write) obvious, and they stay readable when an agent edits only insert later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;owner_id&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;amount_cents&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;index&lt;/span&gt; &lt;span class="n"&gt;invoices_owner_id_idx&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;revoke&lt;/span&gt; &lt;span class="k"&gt;all&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"invoices_select_own"&lt;/span&gt;
  &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;
  &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"invoices_insert_own"&lt;/span&gt;
  &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;
  &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"invoices_update_own"&lt;/span&gt;
  &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;
  &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"invoices_delete_own"&lt;/span&gt;
  &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt;
  &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrapping &lt;code&gt;auth.uid()&lt;/code&gt; in &lt;code&gt;select&lt;/code&gt; is the &lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security#call-functions-with-select" rel="noopener noreferrer"&gt;documented initPlan pattern&lt;/a&gt;: Postgres evaluates the function once per statement instead of once per row. Only use it when the result does not depend on the current row. Index the columns the policy compares, here &lt;code&gt;owner_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With RLS on and no policy, publishable-key requests see nothing. That empty result is safer than an open table. If the agent’s query “fails”, the policy is missing or wrong. Do not &lt;code&gt;alter table … disable row level security&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to tell the agent about clients
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browser and user-scoped server code&lt;/strong&gt; use the publishable (anon) key plus a user JWT. RLS is the access control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The service-role key bypasses RLS.&lt;/strong&gt; It belongs on the server only. Never prefix it with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt;. If a job must write across users, keep that path tiny and audited.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not use RLS as the only filter.&lt;/strong&gt; Still add &lt;code&gt;.eq('owner_id', userId)&lt;/code&gt; in the query. Policies are security; explicit filters keep the intent visible and the plan cheaper.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Views need the same treatment&lt;/strong&gt; if they are exposed through the Data API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prove it with a database test
&lt;/h2&gt;

&lt;p&gt;A rule without a test is a comment. Supabase can run pgTAP files with &lt;code&gt;supabase test db&lt;/code&gt;. Ask the agent for a case that inserts as user A and fails to read as user B.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- user A can read their row; user B cannot.&lt;/span&gt;
&lt;span class="c1"&gt;-- Use supabase test helpers / auth.uid() stubs your project already has.&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'suite loads; replace with two auth.uid() cases'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'other-user select returns 0 rows under authenticated'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the stubs with your project’s auth helpers. The point of the rule is: no new table without an other-user case. If you do not want to teach the agent pgTAP, require a failing-then-passing integration test that uses two sessions instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  AGENTS.md lines that actually get followed
&lt;/h2&gt;

&lt;p&gt;Keep the always-on file short. The migration glob rule above does the path-scoped work. These four lines belong in the shared &lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Database&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Every new table enables RLS and defines per-command policies in the same migration.
&lt;span class="p"&gt;-&lt;/span&gt; Policies use (select auth.uid()). Never disable RLS to make a query work.
&lt;span class="p"&gt;-&lt;/span&gt; Service-role key stays on the server. Never NEXT_PUBLIC_ it.
&lt;span class="p"&gt;-&lt;/span&gt; After schema changes, regenerate types into the existing types file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put the file at the repo root so Cursor, Codex and Windsurf all see it. Claude Code picks it up with &lt;code&gt;@AGENTS.md&lt;/code&gt; in &lt;code&gt;CLAUDE.md&lt;/code&gt;. Which file each tool reads: &lt;a href="https://agentkitdrawer.com/agents-md-vs-claude-md-vs-cursor-rules/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44" rel="noopener noreferrer"&gt;AGENTS.md vs CLAUDE.md vs Cursor rules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If the same agent is about to grant paid access from a Stripe redirect, that is a different hole. Pair this post with &lt;a href="https://agentkitdrawer.com/stripe-webhooks-ai-agents/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44" rel="noopener noreferrer"&gt;Stripe webhooks for AI agents&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;Do new Supabase tables have RLS enabled?&lt;/p&gt;

&lt;p&gt;Tables created in the Supabase Dashboard have RLS enabled by default. Tables created in the SQL Editor, migrations, or another tool do not. Enable RLS explicitly in the same migration that creates the table.&lt;/p&gt;

&lt;p&gt;What happens if RLS is on but there are no policies?&lt;/p&gt;

&lt;p&gt;Requests that use a publishable key see no rows. That is safer than leaving RLS off. Add per-command policies for the roles that should have access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why wrap auth.uid() in a select?
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;(select auth.uid())&lt;/code&gt; lets Postgres cache the value once per statement (an initPlan) instead of calling the function on every row. Only do this when the result does not depend on the current row.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the service role bypass RLS?
&lt;/h3&gt;

&lt;p&gt;Yes. The service-role key bypasses row-level security. Keep it on the server only. Never prefix it with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; or ship it to the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I disable RLS to debug a failing query?
&lt;/h3&gt;

&lt;p&gt;No. If a query fails, fix the policy and explain it. Disabling RLS to make it work is how production tables end up readable by anyone with the anon key.&lt;/p&gt;




&lt;p&gt;The free &lt;a href="https://dl.agentkitdrawer.com/starter/free?src=devto-rls&amp;amp;utm_source=devto" rel="noopener noreferrer"&gt;Starter AGENTS.md&lt;/a&gt; is a one-click download, no email needed. The full setup (migration rule, a Claude Code hook that blocks a table without RLS, a migration-reviewer subagent) is the &lt;a href="https://agentkitdrawer.com/supabase-nextjs-agent-kit/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44" rel="noopener noreferrer"&gt;Supabase + Next.js Agent Kit&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>postgres</category>
      <category>security</category>
      <category>ai</category>
    </item>
    <item>
      <title>3 AI coding agent footguns, and the one command that catches each</title>
      <dc:creator>AgentKit Drawer</dc:creator>
      <pubDate>Fri, 23 Oct 2026 15:21:08 +0000</pubDate>
      <link>https://dev.to/eli_agentkit/3-ai-coding-agent-footguns-and-the-one-command-that-catches-each-1864</link>
      <guid>https://dev.to/eli_agentkit/3-ai-coding-agent-footguns-and-the-one-command-that-catches-each-1864</guid>
      <description>&lt;p&gt;AI coding agents are fast, and sometimes they take shortcuts you won't notice until it hurts. Here are three I made short videos about this week. Each one comes with a command you can run today.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Slopsquatting: your agent installs a package that doesn't exist
&lt;/h2&gt;

&lt;p&gt;LLMs sometimes suggest package names that were never published. Anyone can register those names on npm or PyPI and put malicious code inside. The attack is called &lt;strong&gt;slopsquatting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The numbers come from &lt;em&gt;"We Have a Package for You!"&lt;/em&gt; (Spracklen et al., USENIX Security 2025). The authors generated 576,000 code samples with 16 LLMs. &lt;strong&gt;19.7%&lt;/strong&gt; of the suggested packages didn't exist. When they re-ran prompts that had produced a fake name, &lt;strong&gt;43%&lt;/strong&gt; of those fake names came back in all 10 reruns. Names that predictable are easy to squat.&lt;/p&gt;

&lt;p&gt;"Just try installing it" is the wrong move. &lt;code&gt;npm install&lt;/code&gt; runs a package's preinstall, install and postinstall scripts, so its code runs the moment it lands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 5-second check:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm view PACKAGE-NAME time.created repository.url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If you get &lt;code&gt;404 Not Found&lt;/code&gt;, the name is made up. Stop there.&lt;/li&gt;
&lt;li&gt;If it exists, look closer. A brand-new package with no repo and only a handful of weekly downloads is a red flag. Existing doesn't mean safe.&lt;/li&gt;
&lt;li&gt;For Python, &lt;code&gt;https://pypi.org/project/PACKAGE-NAME/&lt;/code&gt; returns a 404 for names that were never published.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then add this rule to AGENTS.md: &lt;em&gt;"Never invent file paths, API fields, package names, URLs, versions or quotes. If unsure, say so and say how to find out."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Claude Code reads your .env without asking
&lt;/h2&gt;

&lt;p&gt;According to Claude Code's permissions docs, file reads inside your working directory need no approval. Read-only shell commands like &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt;, &lt;code&gt;tail&lt;/code&gt; and &lt;code&gt;grep&lt;/code&gt; also run without a prompt. If the agent decides your Stripe bug "might be config", your live keys end up in the conversation. The docs also say a &lt;code&gt;.claudeignore&lt;/code&gt; file has no effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix: deny rules in &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(./.env)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(./.env.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(./secrets/**)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Deny beats allow, so no allow rule can carve an exception out of it.&lt;/li&gt;
&lt;li&gt;Read deny rules also cover file commands Claude recognizes in Bash, such as &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt;, &lt;code&gt;tail&lt;/code&gt; and &lt;code&gt;sed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Commit the file and your whole team is covered.&lt;/li&gt;
&lt;li&gt;Caveat: a Python or Node script that opens the file itself isn't covered. For OS-level blocking, turn on the sandbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Your agent made CI green by deleting the failing test
&lt;/h2&gt;

&lt;p&gt;Ask an agent to "make CI pass" and sometimes it takes the shortcut. It deletes the failing test, or marks it &lt;code&gt;.skip&lt;/code&gt;, and reports green. Anthropic's own Claude prompting guide warns that Claude "can sometimes focus too heavily on making tests pass at the expense of more general solutions."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catch it with git:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff main &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="nt"&gt;--diff-filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;D &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;'*.test.*'&lt;/span&gt;
git diff main | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^\+.*\.(skip|only)\('&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--diff-filter=D&lt;/code&gt; keeps only deleted files, and &lt;code&gt;--name-only&lt;/code&gt; prints just their paths. In a git pathspec, &lt;code&gt;*&lt;/code&gt; matches across folders, so &lt;code&gt;src/cart/cart.test.ts&lt;/code&gt; is caught too.&lt;/li&gt;
&lt;li&gt;The grep finds added lines that call &lt;code&gt;.skip(&lt;/code&gt; or &lt;code&gt;.only(&lt;/code&gt;, such as &lt;code&gt;it.skip&lt;/code&gt; or &lt;code&gt;test.only&lt;/code&gt; in Jest and Vitest.&lt;/li&gt;
&lt;li&gt;In CI, diff against the base branch (for example &lt;code&gt;origin/main...HEAD&lt;/code&gt;) and fail the job if either command prints anything.&lt;/li&gt;
&lt;li&gt;What this misses: an agent that rewrites the assertion so it passes. You still need to read the test diff.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Put the rules where your agent reads them
&lt;/h2&gt;

&lt;p&gt;Each of these checks has a matching line for AGENTS.md: no invented packages, never read &lt;code&gt;.env*&lt;/code&gt;, and "green by cheating is red." They're all in the hard rules of our packs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DRW-06: Supabase + Next.js Agent Kit ($19).&lt;/strong&gt; AGENTS.md, CLAUDE.md, 8 scoped Cursor rules, 10 prompts and a Claude Code hook that blocks tables without RLS: &lt;a href="https://agentkitdrawer.com/supabase-nextjs-agent-kit/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44&amp;amp;utm_content=agent-footguns" rel="noopener noreferrer"&gt;https://agentkitdrawer.com/supabase-nextjs-agent-kit/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44&amp;amp;utm_content=agent-footguns&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DRW-00: One-File Agent Rules ($9).&lt;/strong&gt; One 160-line AGENTS.md for Cursor, Claude Code and Codex: &lt;a href="https://agentkitdrawer.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44&amp;amp;utm_content=agent-footguns#one-file" rel="noopener noreferrer"&gt;https://agentkitdrawer.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=2026-44&amp;amp;utm_content=agent-footguns#one-file&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Has your agent pulled any of these on you? Tell me in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>claudecode</category>
      <category>git</category>
    </item>
    <item>
      <title>A Codex AGENTS.md template for OpenAI Codex CLI</title>
      <dc:creator>AgentKit Drawer</dc:creator>
      <pubDate>Wed, 21 Oct 2026 02:03:31 +0000</pubDate>
      <link>https://dev.to/eli_agentkit/a-codex-agentsmd-template-for-openai-codex-cli-167e</link>
      <guid>https://dev.to/eli_agentkit/a-codex-agentsmd-template-for-openai-codex-cli-167e</guid>
      <description>&lt;p&gt;OpenAI Codex reads &lt;code&gt;AGENTS.md&lt;/code&gt; before it does any work. That file is the Codex CLI agent instructions for the repo: stack, commands, hard never-dos, and what “done” means. A short, specific file gets followed. A 600-line essay gets skimmed.&lt;/p&gt;

&lt;p&gt;This guide is a Codex &lt;code&gt;AGENTS.md&lt;/code&gt; template you can paste today, plus how Codex actually discovers those files, and how that differs from &lt;code&gt;CLAUDE.md&lt;/code&gt; and Cursor rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Codex reads
&lt;/h2&gt;

&lt;p&gt;Codex builds an instruction chain once per run (in the TUI, once per session):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global:&lt;/strong&gt; &lt;code&gt;~/.codex/AGENTS.override.md&lt;/code&gt; if it exists, otherwise &lt;code&gt;~/.codex/AGENTS.md&lt;/code&gt;. Personal defaults, not repo rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project:&lt;/strong&gt; from the repo root (usually the Git root) down to the current working directory. In each directory it prefers &lt;code&gt;AGENTS.override.md&lt;/code&gt;, then &lt;code&gt;AGENTS.md&lt;/code&gt;. At most one file per directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge:&lt;/strong&gt; files concatenate root → cwd. Closer files appear later, so they override broader guidance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Empty files are skipped. Combined project instructions cap at about 32&amp;nbsp;KiB by default (&lt;code&gt;project_doc_max_bytes&lt;/code&gt;). Keep the root file short; put specialist rules in nested directories instead of one giant README. Official discovery notes: &lt;a href="https://developers.openai.com/codex/guides/agents-md" rel="noopener noreferrer"&gt;Custom instructions with AGENTS.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Codex does &lt;em&gt;not&lt;/em&gt; read &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt;. If those are your only instruction files, Codex guesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimal copy-paste template
&lt;/h2&gt;

&lt;p&gt;Drop this at the repo root as &lt;code&gt;AGENTS.md&lt;/code&gt;. Fill every &lt;code&gt;&amp;lt;&amp;lt;FILL&amp;gt;&amp;gt;&lt;/code&gt;. Codex’s &lt;code&gt;/init&lt;/code&gt; can scaffold a starter; edit that result the same way — exact commands beat vague vibes.&lt;/p&gt;

&lt;p&gt;AGENTS.mdcopy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# AGENTS.md&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- OpenAI Codex CLI agent instructions. Fill every &amp;lt;&amp;lt;FILL&amp;gt;&lt;/span&gt;&amp;gt;. --&amp;gt;

&lt;span class="gu"&gt;## Project&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; App: &lt;span class="c"&gt;&amp;lt;&amp;lt;FILL: name and one-line purpose&amp;gt;&lt;/span&gt;&amp;gt;
&lt;span class="p"&gt;-&lt;/span&gt; Stack: &lt;span class="c"&gt;&amp;lt;&amp;lt;FILL: e.g. Next.js 15 App Router, Supabase, Stripe, pnpm&amp;gt;&lt;/span&gt;&amp;gt;
&lt;span class="p"&gt;-&lt;/span&gt; Layout: &lt;span class="c"&gt;&amp;lt;&amp;lt;FILL: where app/, lib/, tests, and generated types live&amp;gt;&lt;/span&gt;&amp;gt;

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Dev: &lt;span class="c"&gt;&amp;lt;&amp;lt;FILL: e.g. pnpm dev&amp;gt;&lt;/span&gt;&amp;gt;
&lt;span class="p"&gt;-&lt;/span&gt; Test: &lt;span class="c"&gt;&amp;lt;&amp;lt;FILL&amp;gt;&lt;/span&gt;&amp;gt;
&lt;span class="p"&gt;-&lt;/span&gt; Lint: &lt;span class="c"&gt;&amp;lt;&amp;lt;FILL&amp;gt;&lt;/span&gt;&amp;gt;
&lt;span class="p"&gt;-&lt;/span&gt; Typecheck: &lt;span class="c"&gt;&amp;lt;&amp;lt;FILL&amp;gt;&lt;/span&gt;&amp;gt;

&lt;span class="gu"&gt;## Hard rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never print, log, or commit secrets. Never prefix secrets with NEXT_PUBLIC_.
&lt;span class="p"&gt;-&lt;/span&gt; Never disable tests, typecheck, RLS, or other safety checks to make something pass.
&lt;span class="p"&gt;-&lt;/span&gt; Keep diffs small. Never edit generated files by hand.
&lt;span class="p"&gt;-&lt;/span&gt; For auth, payments, or migrations: write a short plan and wait for go.

&lt;span class="gu"&gt;## Workflow&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Restate the goal in one line and list the files you will touch.
&lt;span class="p"&gt;2.&lt;/span&gt; Make one small change, then run the tests / typecheck you listed.
&lt;span class="p"&gt;3.&lt;/span&gt; Report what you verified, and what you could not verify.

&lt;span class="gu"&gt;## Done means&lt;/span&gt;
The commands above pass, the change is described in plain words, and you did not expand the scope.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste a safety section from the free &lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/agent-guardrails.md" rel="noopener noreferrer"&gt;agent-guardrails.md&lt;/a&gt; (MIT) when the agent will call tools or read untrusted content. For Next.js + Supabase, the free &lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/nextjs-supabase-rls.mdc" rel="noopener noreferrer"&gt;nextjs-supabase-rls.mdc&lt;/a&gt; is a Cursor-shaped rule; copy the bullets into &lt;code&gt;AGENTS.md&lt;/code&gt; (or a nested &lt;code&gt;supabase/AGENTS.md&lt;/code&gt;) so Codex sees them too.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this differs from CLAUDE.md and Cursor rules
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Who reads it&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Use it for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AGENTS.md&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenAI Codex CLI, Cursor, and other AGENTS.md-aware tools&lt;/td&gt;
&lt;td&gt;Plain Markdown; nested files concatenate root → cwd&lt;/td&gt;
&lt;td&gt;Shared source of truth: stack, commands, never-dos, done criteria&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Markdown; can import files with &lt;code&gt;@path&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A thin file that starts with &lt;code&gt;@AGENTS.md&lt;/code&gt;, then Claude-only notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.cursor/rules/*.mdc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;Markdown + YAML frontmatter (&lt;code&gt;globs&lt;/code&gt;, &lt;code&gt;alwaysApply&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Path-scoped rules that load only when matching files are in context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Recommended layout: write &lt;code&gt;AGENTS.md&lt;/code&gt; first so Codex has instructions. Add a one-line &lt;code&gt;CLAUDE.md&lt;/code&gt; that imports it. Add scoped Cursor &lt;code&gt;.mdc&lt;/code&gt; files for auth, RLS, or Stripe — do not paste the whole essay into every format. Full comparison: &lt;a href="https://agentkitdrawer.com/agents-md-vs-claude-md-vs-cursor-rules/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;AGENTS.md vs CLAUDE.md vs Cursor rules&lt;/a&gt;. Claude-specific template: &lt;a href="https://agentkitdrawer.com/claude-code-claude-md-template/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;CLAUDE.md template&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested files and overrides
&lt;/h2&gt;

&lt;p&gt;When a subdirectory has different commands or never-dos, put a second &lt;code&gt;AGENTS.md&lt;/code&gt; there. Codex running from that directory loads the root file, then the nested one. If you need to replace the directory’s file without deleting it, use &lt;code&gt;AGENTS.override.md&lt;/code&gt; in that folder — Codex will take the override and ignore the sibling &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;layout sketchcopy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;repo/&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;AGENTS.md&lt;/span&gt;&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;truth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(Codex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;others)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;CLAUDE.md&lt;/span&gt;&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@AGENTS.md&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Claude-only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;notes&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;.cursor/rules/&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Cursor-only,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;path-scoped&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;services/payments/&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;AGENTS.md&lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;payments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;commands&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;never-dos&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Free files (MIT)
&lt;/h2&gt;

&lt;p&gt;All of these live in the public &lt;a href="https://github.com/eli-alvarado/agentkit-drawer" rel="noopener noreferrer"&gt;eli-alvarado/agentkit-drawer&lt;/a&gt; repo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/agent-guardrails.md" rel="noopener noreferrer"&gt;agent-guardrails.md&lt;/a&gt; — injection defense, ask-vs-act, uncertainty&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/nextjs-supabase-rls.mdc" rel="noopener noreferrer"&gt;nextjs-supabase-rls.mdc&lt;/a&gt; — RLS bullets you can mirror into AGENTS.md&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/godot-jam-timeline.md" rel="noopener noreferrer"&gt;godot-jam-timeline.md&lt;/a&gt; and &lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/agents-md-game-modding.md" rel="noopener noreferrer"&gt;agents-md-game-modding.md&lt;/a&gt; — game-shaped AGENTS.md starters&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;What file does OpenAI Codex read?&lt;/p&gt;

&lt;p&gt;Codex CLI reads &lt;code&gt;AGENTS.md&lt;/code&gt; (and &lt;code&gt;AGENTS.override.md&lt;/code&gt; when present) before it starts work. Personal defaults can live in &lt;code&gt;~/.codex/AGENTS.md&lt;/code&gt;. It does not read &lt;code&gt;CLAUDE.md&lt;/code&gt; or Cursor &lt;code&gt;.mdc&lt;/code&gt; rules.&lt;/p&gt;

&lt;p&gt;Does Codex read CLAUDE.md or .cursor/rules?&lt;/p&gt;

&lt;p&gt;No. Keep shared rules in &lt;code&gt;AGENTS.md&lt;/code&gt;. Point Claude Code at that file with &lt;code&gt;@AGENTS.md&lt;/code&gt;. Keep Cursor path rules in &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Where should the Codex AGENTS.md template live?&lt;/p&gt;

&lt;p&gt;Repo root, next to your lockfile. Nested &lt;code&gt;AGENTS.md&lt;/code&gt; files refine the root file for a subdirectory. Codex concatenates from the Git root down to the working directory.&lt;/p&gt;

&lt;p&gt;What is AGENTS.override.md?&lt;/p&gt;

&lt;p&gt;In a given directory, it wins over &lt;code&gt;AGENTS.md&lt;/code&gt;. Use it for a local or temporary override without deleting the shared file. Codex still takes only one instruction file per directory.&lt;/p&gt;

&lt;p&gt;How is this different from Cursor rules?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is unscoped plain Markdown. Cursor &lt;code&gt;.mdc&lt;/code&gt; files add frontmatter so a rule loads only for matching paths. Shared facts belong in &lt;code&gt;AGENTS.md&lt;/code&gt;; path-scoped extras belong in Cursor rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want the whole drawer?
&lt;/h2&gt;

&lt;p&gt;The Vibe Starter Rules Pack ($19) ships a ready &lt;code&gt;AGENTS.md&lt;/code&gt; + &lt;code&gt;CLAUDE.md&lt;/code&gt; pair, 8 scoped Cursor rules, 12 slash-command prompts, and launch checklists for Next.js + Supabase + Stripe — the file Codex actually reads, plus the tool-specific extras. The Agent Builder Prompt Pack ($29) is the system-prompt and eval kit for Codex-class agents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buy.stripe.com/cNi4gy7kEfjJgWN7Zk7kc00?client_reference_id=drw-01_devto-codex-agents-md-template&amp;amp;utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43&amp;amp;utm_content=drw-01" rel="noopener noreferrer"&gt;Buy Vibe Starter · $19&lt;/a&gt;&lt;br&gt;
&lt;a href="https://buy.stripe.com/6oU3cueN68VleOFdjE7kc02?client_reference_id=drw-03_devto-codex-agents-md-template&amp;amp;utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43&amp;amp;utm_content=drw-03" rel="noopener noreferrer"&gt;Buy Agent Builder · $29&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not ready for a full pack? Start with &lt;a href="https://agentkitdrawer.com/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43#one-file" rel="noopener noreferrer"&gt;One-File Agent Rules ($9)&lt;/a&gt;, one universal AGENTS.md for any stack, or &lt;a href="https://agentkitdrawer.com/codex-agents-md-template/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43#news-h" rel="noopener noreferrer"&gt;get the free Starter AGENTS.md&lt;/a&gt; by email.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentkitdrawer.com/codex-agents-md-template/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;agentkitdrawer.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>codex</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Claude Code subagents, hooks and slash commands: a working setup</title>
      <dc:creator>AgentKit Drawer</dc:creator>
      <pubDate>Wed, 21 Oct 2026 01:56:55 +0000</pubDate>
      <link>https://dev.to/eli_agentkit/claude-code-subagents-hooks-and-slash-commands-a-working-setup-34b5</link>
      <guid>https://dev.to/eli_agentkit/claude-code-subagents-hooks-and-slash-commands-a-working-setup-34b5</guid>
      <description>&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; tells Claude Code what to do. Subagents, hooks and slash commands are how you make it actually happen: focused reviewers with limited tools, shell scripts that &lt;em&gt;enforce&lt;/em&gt; your rules instead of hoping the model remembers them, and one-word commands for your workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subagents: &lt;code&gt;.claude/agents/*.md&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A subagent is a Markdown file with YAML frontmatter: &lt;code&gt;name&lt;/code&gt;, a &lt;code&gt;description&lt;/code&gt; that tells Claude &lt;em&gt;when&lt;/em&gt; to delegate (write “Use PROACTIVELY after…” to get it used automatically), and an optional &lt;code&gt;tools&lt;/code&gt; list. Each subagent runs in its own fresh context, so it does not clutter the main conversation, and it does not see that conversation either: pass it the exact files and goal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege:&lt;/strong&gt; a reviewer or security auditor gets &lt;code&gt;Read, Grep, Glob&lt;/code&gt; only. If it cannot edit, it cannot “fix” things you did not ask for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One job each:&lt;/strong&gt; planner, code-reviewer, test-writer, debugger, security-auditor, docs-writer covers most teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output contracts:&lt;/strong&gt; “Report MUST / SHOULD / NIT with path:line” beats “review the code”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run &lt;code&gt;/agents&lt;/code&gt; in Claude Code to list, create and edit them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks: rules the model cannot skip
&lt;/h2&gt;

&lt;p&gt;Hooks live in &lt;code&gt;.claude/settings.json&lt;/code&gt; under &lt;code&gt;"hooks"&lt;/code&gt;. Each event (&lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, &lt;code&gt;Stop&lt;/code&gt;, and others) has a &lt;code&gt;matcher&lt;/code&gt; on the tool name (&lt;code&gt;Bash&lt;/code&gt;, &lt;code&gt;Edit|Write&lt;/code&gt;) and a command. The hook gets the tool call as JSON on stdin. &lt;strong&gt;Exit code 2 blocks the action&lt;/strong&gt; and sends your stderr message back to Claude; exit 0 allows it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PreToolUse on Bash:&lt;/strong&gt; block &lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;git push --force&lt;/code&gt;, &lt;code&gt;git reset --hard&lt;/code&gt;, &lt;code&gt;curl | sh&lt;/code&gt;, pushes to main.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PreToolUse on Read|Edit|Write:&lt;/strong&gt; refuse &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt; and lockfiles, while still allowing &lt;code&gt;.env.example&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostToolUse on Edit|Write:&lt;/strong&gt; run your formatter on the edited file. Never block here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stop:&lt;/strong&gt; run tests when code changed; exit 2 keeps Claude working. Check &lt;code&gt;stop_hook_active&lt;/code&gt; to avoid loops.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hooks run with your user's permissions. Read every script before enabling it and keep them in version control. Pair them with &lt;code&gt;permissions.deny&lt;/code&gt; entries in the same file for a second layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slash commands: &lt;code&gt;.claude/commands/*.md&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Each file becomes &lt;code&gt;/filename&lt;/code&gt;. Use &lt;code&gt;$ARGUMENTS&lt;/code&gt; for what you type after the command, and frontmatter such as &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;argument-hint&lt;/code&gt; and &lt;code&gt;allowed-tools&lt;/code&gt;. Good commands chain your subagents: &lt;code&gt;/feature&lt;/code&gt; = plan → implement → test-writer → tests → code-reviewer → summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free snippet: copy it
&lt;/h2&gt;

&lt;p&gt;.claude/hooks/block-dangerous-bash.shcopy&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;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# PreToolUse (matcher: Bash). Exit 2 = block; stderr is shown to Claude.&lt;/span&gt;
&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.command // ""'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;deny&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s1"&gt;'rm -rf /'&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf ~'&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf \*'&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf \.'&lt;/span&gt; &lt;span class="s1"&gt;'git push --force'&lt;/span&gt; &lt;span class="s1"&gt;'git push -f'&lt;/span&gt; &lt;span class="s1"&gt;'git reset --hard'&lt;/span&gt;
  &lt;span class="s1"&gt;'git clean -fd'&lt;/span&gt; &lt;span class="s1"&gt;'DROP DATABASE'&lt;/span&gt; &lt;span class="s1"&gt;'DROP TABLE'&lt;/span&gt; &lt;span class="s1"&gt;'TRUNCATE '&lt;/span&gt; &lt;span class="s1"&gt;'supabase db reset'&lt;/span&gt; &lt;span class="s1"&gt;'prisma migrate reset'&lt;/span&gt;
  &lt;span class="s1"&gt;'alembic downgrade base'&lt;/span&gt; &lt;span class="s1"&gt;'chmod -R 777'&lt;/span&gt; &lt;span class="s1"&gt;'mkfs'&lt;/span&gt; &lt;span class="s1"&gt;'dd if='&lt;/span&gt; &lt;span class="s1"&gt;':(){'&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;p &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if &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;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qiF&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="p"&gt;//\\/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blocked by .claude/hooks/block-dangerous-bash.sh: '&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;' is destructive. Explain why it's needed and ask the human to run it."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;2
  &lt;span class="k"&gt;fi
done
if &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;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'(curl|wget)[^|]*\|\s*(ba|z)?sh'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blocked: piping a download into a shell. Download, inspect, then ask."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi
if &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;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'git push[^&amp;amp;;|]*\b(main|master)\b'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blocked: pushing to main/master. Push a branch and open a PR."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;Where do Claude Code subagents live?&lt;/p&gt;

&lt;p&gt;Project subagents go in &lt;code&gt;.claude/agents/&lt;/code&gt; as Markdown files with YAML frontmatter (name, description, optional tools). Personal ones go in &lt;code&gt;~/.claude/agents/&lt;/code&gt;. Use &lt;code&gt;/agents&lt;/code&gt; to manage them.&lt;/p&gt;

&lt;p&gt;How do I block dangerous commands in Claude Code?&lt;/p&gt;

&lt;p&gt;Add a PreToolUse hook with matcher &lt;code&gt;Bash&lt;/code&gt; in &lt;code&gt;.claude/settings.json&lt;/code&gt;. The script reads the tool call JSON from stdin and exits with code 2 to block it; its stderr is shown to Claude. Add matching &lt;code&gt;permissions.deny&lt;/code&gt; rules as a second layer.&lt;/p&gt;

&lt;p&gt;What does exit code 2 mean in a Claude Code hook?&lt;/p&gt;

&lt;p&gt;Exit 2 blocks the action (for PreToolUse) or tells Claude to keep working (for Stop), and stderr is fed back to Claude. Exit 0 allows. Other codes are non-blocking errors shown to you.&lt;/p&gt;

&lt;p&gt;How do I make Claude Code run tests before it finishes?&lt;/p&gt;

&lt;p&gt;Use a Stop hook that runs your test command when code changed and exits 2 on failure. Check the &lt;code&gt;stop_hook_active&lt;/code&gt; field in the input and exit 0 when it is true, so the hook cannot loop forever.&lt;/p&gt;

&lt;p&gt;Do subagents see the main conversation?&lt;/p&gt;

&lt;p&gt;No. Each subagent starts with a fresh context window and only gets the task you hand it, so include file paths and the goal in the delegation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want the whole drawer?
&lt;/h2&gt;

&lt;p&gt;The Claude Code Kit ($24) ships a fill-in &lt;code&gt;CLAUDE.md&lt;/code&gt;, 6 least-privilege subagents, 10 slash commands, a &lt;code&gt;settings.json&lt;/code&gt; with permissions and hook wiring, and 4 tested hooks (dangerous Bash, secret files, lockfiles, format, tests-before-stop).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buy.stripe.com/5kQ3cuawQ6Ndayp0wS7kc04?client_reference_id=drw-04_devto-claude-code-subagents-hooks-guide&amp;amp;utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43&amp;amp;utm_content=drw-04" rel="noopener noreferrer"&gt;Buy Claude Code Kit · $24&lt;/a&gt;&lt;br&gt;
&lt;a href="https://buy.stripe.com/28EdR88oI0oPeOF6Vg7kc05?client_reference_id=drw-05_devto-claude-code-subagents-hooks-guide&amp;amp;utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43&amp;amp;utm_content=drw-05" rel="noopener noreferrer"&gt;Buy FastAPI Kit · $19&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not ready for a full pack? Start with &lt;a href="https://agentkitdrawer.com/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43#one-file" rel="noopener noreferrer"&gt;One-File Agent Rules ($9)&lt;/a&gt;, one universal AGENTS.md for any stack, or &lt;a href="https://agentkitdrawer.com/claude-code-subagents-hooks-guide/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43#news-h" rel="noopener noreferrer"&gt;get the free Starter AGENTS.md&lt;/a&gt; by email.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentkitdrawer.com/claude-code-subagents-hooks-guide/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;agentkitdrawer.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>devtools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AGENTS.md vs CLAUDE.md vs .cursor/rules: which file does your AI agent read?</title>
      <dc:creator>AgentKit Drawer</dc:creator>
      <pubDate>Wed, 21 Oct 2026 01:56:14 +0000</pubDate>
      <link>https://dev.to/eli_agentkit/agentsmd-vs-claudemd-vs-cursorrules-which-file-does-your-ai-agent-read-5cil</link>
      <guid>https://dev.to/eli_agentkit/agentsmd-vs-claudemd-vs-cursorrules-which-file-does-your-ai-agent-read-5cil</guid>
      <description>&lt;p&gt;Different AI coding tools look for different instruction files. If you only keep rules in one tool's format, the others guess. If you copy the same wall of text into every format, the copies drift. This guide maps what each file is for, and a setup that stays maintainable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/strong&gt; is the cross-tool open convention. OpenAI Codex, Cursor, and many other agents read it as plain Markdown project instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/strong&gt; is Claude Code's project memory file. It can pull in other files with &lt;code&gt;@path&lt;/code&gt; syntax (for example &lt;code&gt;@AGENTS.md&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor project rules&lt;/strong&gt; live in &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt; with YAML frontmatter (&lt;code&gt;description&lt;/code&gt;, &lt;code&gt;globs&lt;/code&gt;, &lt;code&gt;alwaysApply&lt;/code&gt;). The legacy single-file &lt;code&gt;.cursorrules&lt;/code&gt; format is deprecated; prefer &lt;code&gt;.mdc&lt;/code&gt; rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windsurf&lt;/strong&gt; stores Cascade rules as Markdown under &lt;code&gt;.windsurf/rules/&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Who reads it&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AGENTS.md&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Codex, Cursor, and other AGENTS.md-aware tools&lt;/td&gt;
&lt;td&gt;Plain Markdown at the repo root (and optionally nested)&lt;/td&gt;
&lt;td&gt;Shared source of truth: stack, commands, hard rules, definition of done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Markdown; can import other files with &lt;code&gt;@path&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Claude-specific notes on top of shared AGENTS.md&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.cursor/rules/*.mdc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;Markdown + frontmatter (&lt;code&gt;description&lt;/code&gt;, &lt;code&gt;globs&lt;/code&gt;, &lt;code&gt;alwaysApply&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Path-scoped rules that load only when matching files are in context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;.cursorrules&lt;/strong&gt; (legacy)&lt;/td&gt;
&lt;td&gt;Older Cursor setups&lt;/td&gt;
&lt;td&gt;Single root file, no frontmatter&lt;/td&gt;
&lt;td&gt;Migrate away; use &lt;code&gt;.mdc&lt;/code&gt; rules or AGENTS.md&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.windsurf/rules/&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Windsurf Cascade&lt;/td&gt;
&lt;td&gt;Markdown files with activation modes (always / glob / manual)&lt;/td&gt;
&lt;td&gt;Same ideas as Cursor rules, in Windsurf's layout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What each file is for
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AGENTS.md
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is a simple open format: a README for agents. Put durable project guidance here: how to build and test, folder layout, conventions, and the never-do list. Keep it short enough that tools will follow it. Codex discovers project &lt;code&gt;AGENTS.md&lt;/code&gt; files along the path from the repo root to your working directory; closer files can refine broader ones. Copy-paste starter: &lt;a href="https://agentkitdrawer.com/codex-agents-md-template/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;Codex AGENTS.md template&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLAUDE.md
&lt;/h3&gt;

&lt;p&gt;Claude Code loads &lt;code&gt;CLAUDE.md&lt;/code&gt; at the start of a session. Use &lt;code&gt;@path/to/file&lt;/code&gt; anywhere in that file to import more Markdown. Relative paths resolve from the file that contains the import. A common pattern is a one-line import of shared rules, then a few Claude-only notes:&lt;/p&gt;

&lt;p&gt;CLAUDE.mdcopy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;@AGENTS.md

&lt;span class="gh"&gt;# Claude Code notes&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Prefer the project package scripts over ad-hoc commands.
&lt;span class="p"&gt;-&lt;/span&gt; Before touching auth, payments, or migrations: write a short plan and wait for go.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a fuller template, see the free &lt;a href="https://agentkitdrawer.com/claude-code-claude-md-template/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;CLAUDE.md guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Each rule is an &lt;code&gt;.mdc&lt;/code&gt; file under &lt;code&gt;.cursor/rules/&lt;/code&gt;. Frontmatter controls when it loads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;alwaysApply: true&lt;/code&gt; — included in every chat.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;globs&lt;/code&gt; with &lt;code&gt;alwaysApply: false&lt;/code&gt; — attached when matching files are in context.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt; without globs — Cursor can pull the rule in when it looks relevant.&lt;/li&gt;
&lt;li&gt;Neither description nor globs — manual via &lt;code&gt;@rule-name&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plain &lt;code&gt;.md&lt;/code&gt; files in that folder are ignored by the rules system (no frontmatter). Prefer small scoped rules over one giant file. Example RLS rule (also free on GitHub as &lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/nextjs-supabase-rls.mdc" rel="noopener noreferrer"&gt;nextjs-supabase-rls.mdc&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;.cursor/rules/30-database-rls.mdccopy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Migrations and row-level security&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;supabase/**&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;For each new table&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;enable RLS + policies in the same migration.&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Policies use (select auth.uid()); separate policies per command.&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Never disable RLS to "make it work". Fix the policy instead.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Next.js + Supabase detail: &lt;a href="https://agentkitdrawer.com/cursor-rules-nextjs-supabase/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;Cursor rules for Next.js + Supabase&lt;/a&gt;. For a Python backend skeleton (venv, pytest, type hints), see &lt;a href="https://agentkitdrawer.com/cursor-rules-for-python/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;Cursor rules for Python&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windsurf &lt;code&gt;.windsurf/rules/&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Cascade reads Markdown rules from &lt;code&gt;.windsurf/rules/&lt;/code&gt;, with activation similar in spirit to Cursor (always on, glob, or manual). Keep those files thin and mirror the ideas already in &lt;code&gt;AGENTS.md&lt;/code&gt;. See &lt;a href="https://agentkitdrawer.com/windsurf-rules/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;Windsurf rules&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended setup: one source of truth
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write &lt;code&gt;AGENTS.md&lt;/code&gt; first.&lt;/strong&gt; Stack, commands, hard rules, workflow, definition of done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a thin &lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/strong&gt; that starts with &lt;code&gt;@AGENTS.md&lt;/code&gt;, then only Claude-specific notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add scoped Cursor rules&lt;/strong&gt; for paths that need extra precision (auth, RLS, Stripe, env). Do not paste the entire AGENTS.md into every &lt;code&gt;.mdc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you use Windsurf,&lt;/strong&gt; port the same scoped ideas into &lt;code&gt;.windsurf/rules/&lt;/code&gt; without inventing a third full copy of the essay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete or migrate &lt;code&gt;.cursorrules&lt;/code&gt;&lt;/strong&gt; so you are not maintaining a legacy file Agent mode may ignore.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When a rule misbehaves, fix it in the source of truth (&lt;code&gt;AGENTS.md&lt;/code&gt; or the one scoped rule), not in three places.&lt;/p&gt;

&lt;p&gt;layout sketchcopy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;repo/&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;AGENTS.md&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;truth&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;CLAUDE.md&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@AGENTS.md&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Claude-only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;notes&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;.cursor/rules/&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;-core.mdc&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;alwaysApply:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(short)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="err"&gt;-database-rls.mdc&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;globs:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;supabase/**&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="err"&gt;-stripe.mdc&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;globs:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;app/api/stripe/**&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;.windsurf/rules/&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;optional&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;mirror&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Cascade&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;database-rls.md&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Free files you can drop in today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/nextjs-supabase-rls.mdc" rel="noopener noreferrer"&gt;nextjs-supabase-rls.mdc&lt;/a&gt; — Cursor rule for Supabase RLS&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/agent-guardrails.md" rel="noopener noreferrer"&gt;agent-guardrails.md&lt;/a&gt; — guardrail snippets for AGENTS.md or system prompts&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/agents-md-game-modding.md" rel="noopener noreferrer"&gt;agents-md-game-modding.md&lt;/a&gt; — AGENTS.md template for mods and small game tools&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/eli-alvarado/agentkit-drawer/blob/main/free/godot-jam-timeline.md" rel="noopener noreferrer"&gt;godot-jam-timeline.md&lt;/a&gt; — 48-hour jam timeline for agents&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;Do I need AGENTS.md, CLAUDE.md, and .cursor/rules all at once?&lt;/p&gt;

&lt;p&gt;Only if you use more than one tool. Shared rules live in &lt;code&gt;AGENTS.md&lt;/code&gt;. Claude Code gets a thin &lt;code&gt;CLAUDE.md&lt;/code&gt; with &lt;code&gt;@AGENTS.md&lt;/code&gt;. Cursor gets scoped &lt;code&gt;.mdc&lt;/code&gt; files when you need path-based loading. Do not duplicate the essay.&lt;/p&gt;

&lt;p&gt;Is .cursorrules still supported?&lt;/p&gt;

&lt;p&gt;It is legacy. Prefer &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt; with frontmatter, and migrate remaining content into those files or into &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Can Claude Code read AGENTS.md?&lt;/p&gt;

&lt;p&gt;Its native file is &lt;code&gt;CLAUDE.md&lt;/code&gt;. Import shared rules with &lt;code&gt;@AGENTS.md&lt;/code&gt; so you maintain one copy. Imports are expanded when the session starts.&lt;/p&gt;

&lt;p&gt;What goes in AGENTS.md vs a Cursor rule?&lt;/p&gt;

&lt;p&gt;AGENTS.md: stack, commands, never-dos, done criteria. Cursor &lt;code&gt;.mdc&lt;/code&gt;: path-scoped guidance via &lt;code&gt;globs&lt;/code&gt; so auth, migrations, or webhooks only load when those files are in context.&lt;/p&gt;

&lt;p&gt;Where do Windsurf rules live?&lt;/p&gt;

&lt;p&gt;Under &lt;code&gt;.windsurf/rules/&lt;/code&gt; as Markdown. Keep them aligned with &lt;code&gt;AGENTS.md&lt;/code&gt; rather than inventing a separate doctrine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want the whole drawer?
&lt;/h2&gt;

&lt;p&gt;Skip the setup. The Claude Code Kit ($24) ships a fill-in &lt;code&gt;CLAUDE.md&lt;/code&gt;, 6 least-privilege subagents, 10 slash commands, &lt;code&gt;settings.json&lt;/code&gt; permissions and 4 tested hooks. The Vibe Starter Rules Pack ($19) ships a ready &lt;code&gt;AGENTS.md&lt;/code&gt; + &lt;code&gt;CLAUDE.md&lt;/code&gt; pair, 8 scoped &lt;code&gt;.cursor/rules&lt;/code&gt;, 12 slash-command prompts and launch checklists for Next.js + Supabase + Stripe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buy.stripe.com/5kQ3cuawQ6Ndayp0wS7kc04?client_reference_id=drw-04_devto-agents-md-vs-claude-md-vs-cursor-rules&amp;amp;utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43&amp;amp;utm_content=drw-04" rel="noopener noreferrer"&gt;Buy Claude Code Kit · $24&lt;/a&gt;&lt;br&gt;
&lt;a href="https://buy.stripe.com/cNi4gy7kEfjJgWN7Zk7kc00?client_reference_id=drw-01_devto-agents-md-vs-claude-md-vs-cursor-rules&amp;amp;utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43&amp;amp;utm_content=drw-01" rel="noopener noreferrer"&gt;Buy Vibe Starter · $19&lt;/a&gt;&lt;br&gt;
&lt;a href="https://agentkitdrawer.com/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43#drawers" rel="noopener noreferrer"&gt;See all 5 kits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not ready for a full pack? Start with &lt;a href="https://agentkitdrawer.com/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43#one-file" rel="noopener noreferrer"&gt;One-File Agent Rules ($9)&lt;/a&gt;, one universal AGENTS.md for any stack, or &lt;a href="https://agentkitdrawer.com/agents-md-vs-claude-md-vs-cursor-rules/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43#news-h" rel="noopener noreferrer"&gt;get the free Starter AGENTS.md&lt;/a&gt; by email.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentkitdrawer.com/agents-md-vs-claude-md-vs-cursor-rules/?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=wk43" rel="noopener noreferrer"&gt;agentkitdrawer.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cursor</category>
      <category>claude</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
