diff --git a/website/blog/2026-10-03-v0.19-batch-set.md b/website/blog/2026-10-03-v0.19-batch-set.md
index 42b0b41b5d06..4cd7d41038fe 100644
--- a/website/blog/2026-10-03-v0.19-batch-set.md
+++ b/website/blog/2026-10-03-v0.19-batch-set.md
@@ -9,6 +9,8 @@ draft: true
v0.19 lets a [Manager](/docs/concepts/managers) write a whole batch of streamed rows in one store update, type-checks
endpoint code about 2x faster, and fixes a round of TypeScript and Vue issues.
+Coding agents also get a Claude Code plugin, Vue skills and llms.txt; see
+[Data Client for Coding Agents](/blog/2026/10/06/coding-agents).
**New APIs:**
diff --git a/website/blog/2026-10-06-coding-agents.md b/website/blog/2026-10-06-coding-agents.md
new file mode 100644
index 000000000000..8c4463b3528d
--- /dev/null
+++ b/website/blog/2026-10-06-coding-agents.md
@@ -0,0 +1,123 @@
+---
+title: Data Client for Coding Agents
+description: Agent skills, a Claude Code plugin, Chrome DevTools MCP debugging and llms.txt help coding agents write, debug and upgrade Data Client apps
+image: /img/social/coding-agents-card.png
+authors: [ntucker]
+tags: [agents, vue]
+draft: true
+---
+
+import SkillTabs from '@site/src/components/SkillTabs';
+
+Coding agents write a lot of Data Client code now, and they write it better when they have the current docs and
+workflows instead of guessing from older releases. This release cycle makes that easier to set up for any agent.
+
+**New:**
+
+- [Claude Code plugin](/blog/2026/10/06/coding-agents#claude-plugin) - `claude plugin install react@data-client` installs the skills for your framework
+- [Vue skills](/blog/2026/10/06/coding-agents#vue-skills) - Vue projects get Vue composables and tests, not React hooks
+- [llms.txt](/blog/2026/10/06/coding-agents#llms-txt) - Any agent can read the docs as markdown, and every page tells it where to look
+
+**Also available:**
+
+- [Agent skills](/blog/2026/10/06/coding-agents#skills) - Define REST APIs, design schemas, write Managers, and test; [/data-client-setup](/blog/2026/10/06/coding-agents#setup) now installs just the ones you need, and they're [generated from these docs](/blog/2026/10/06/coding-agents#generated-references)
+- [Chrome DevTools MCP](/blog/2026/10/06/coding-agents#debugging) - Agents inspect your running app's store, or use Expo MCP for React Native
+- [Upgrade skills](/blog/2026/10/06/coding-agents#upgrades) - Migration skills upgrade your code, including what codemods can't
+
+{/* truncate */}
+
+## Get started {#setup}
+
+Install the [/data-client-setup](https://skills.sh/reactive/data-client/data-client-setup) skill and run it. It
+detects React or Vue and your API style (REST, GraphQL or custom), installs the skills your project needs, and sets up
+Data Client. For REST it hands off to
+[data-client-rest-setup](https://skills.sh/reactive/data-client/data-client-rest-setup), which moves existing `fetch` or
+axios calls to endpoints ([#4153](https://github.com/reactive/data-client/pull/4153)).
+
+
+
+[Agent Skills](/docs/getting-started/agent-skills) lists every skill and how to install them all up front.
+
+## Claude Code plugin {#claude-plugin}
+
+The Data Client repo is a Claude Code plugin marketplace. The `core` plugin holds the setup, REST, GraphQL, schema and
+Manager skills; the `react` and `vue` plugins add their framework's skills and pull in `core`; and the `migrations`
+plugin adds the upgrade skills ([#4153](https://github.com/reactive/data-client/pull/4153)). The
+[Agent Skills](/docs/getting-started/agent-skills) page shows the same install with the skills CLI and OpenSkills.
+
+```bash
+claude plugin marketplace add reactive/data-client
+claude plugin install react@data-client
+```
+
+Use `vue@data-client` for Vue, `core@data-client` for neither, and add `migrations@data-client` before an upgrade.
+
+## Vue skills {#vue-skills}
+
+Vue projects used to get the React hooks skill, so agents reached for React patterns. The
+[data-client-vue](https://skills.sh/reactive/data-client/data-client-vue) skill writes `DataClientPlugin` and composable
+code, and [debugs a running app](/vue/getting-started/debugging#debugging-with-agents)
+([#4126](https://github.com/reactive/data-client/pull/4126)).
+[data-client-vue-testing](https://skills.sh/reactive/data-client/data-client-vue-testing) writes `renderDataCompose()`
+and `mountDataClient()` tests ([#4126](https://github.com/reactive/data-client/pull/4126)) that follow the
+[Vue testing guides](/vue/guides/unit-testing-composables) ([#4181](https://github.com/reactive/data-client/pull/4181)).
+
+## llms.txt {#llms-txt}
+
+Agents without skills can read the docs as markdown, following the [llms.txt](https://llmstxt.org) convention
+([#4171](https://github.com/reactive/data-client/pull/4171), [#4236](https://github.com/reactive/data-client/pull/4236)):
+
+- [llms.txt](https://dataclient.io/llms.txt) for React and [vue/llms.txt](https://dataclient.io/vue/llms.txt) for Vue index every page, and `llms-full.txt` beside each holds them all in one file
+- [rest/llms.txt](https://dataclient.io/rest/llms.txt) and [graphql/llms.txt](https://dataclient.io/graphql/llms.txt) cover just that package's API
+- Any docs page is markdown when you add `.md` to its URL, like [/docs/api/useSuspense.md](https://dataclient.io/docs/api/useSuspense.md)
+
+Every page links its llms.txt in `
`, and every docs page links its markdown, so an agent given any page URL
+finds them on its own.
+
+## Skills match the docs {#generated-references}
+
+Skill reference pages are now generated per framework from these docs, so they carry shared snippets like install
+commands and give Vue projects only Vue code. A CI check fails when a skill drifts from the docs, so skills stay
+current with each release ([#4170](https://github.com/reactive/data-client/pull/4170)).
+
+## Also available
+
+### Skills for each task {#skills}
+
+Beyond setup, there are skills for each part of a Data Client app. `/data-client-setup` installs the ones your project
+uses:
+
+- [data-client-rest-setup](https://skills.sh/reactive/data-client/data-client-rest-setup) moves `fetch`, axios, ky, got or superagent clients to [RestEndpoint](/rest/api/RestEndpoint), alongside the [axios migration guide](/rest/guides/axios-migration) and its codemod ([#3867](https://github.com/reactive/data-client/pull/3867))
+- [data-client-graphql-setup](https://skills.sh/reactive/data-client/data-client-graphql-setup) and [data-client-endpoint-setup](https://skills.sh/reactive/data-client/data-client-endpoint-setup) cover GraphQL and custom async functions
+- [data-client-rest](https://skills.sh/reactive/data-client/data-client-rest) defines APIs with [resource()](/rest/api/resource) and [RestEndpoint](/rest/api/RestEndpoint)
+- [data-client-schema](https://skills.sh/reactive/data-client/data-client-schema) designs [Entity](/rest/api/Entity), [Collection](/rest/api/Collection), [Union](/rest/api/Union) and [Query](/rest/api/Query) schemas
+- [data-client-manager](https://skills.sh/reactive/data-client/data-client-manager) writes [Managers](/docs/concepts/managers) for websockets, SSE, polling and logging
+- [data-client-react](https://skills.sh/reactive/data-client/data-client-react) and [data-client-react-testing](https://skills.sh/reactive/data-client/data-client-react-testing) use and test the React hooks
+
+Browse them all at [skills.sh/reactive/data-client](https://skills.sh/reactive/data-client).
+
+### Debug a running app {#debugging}
+
+Ask your agent "why didn't this refetch?" or "what's in the cache right now?" and it can check the running app instead
+of guessing from code. In dev mode, [DevToolsManager](/docs/api/DevToolsManager#controllers) puts each store's
+[Controller](/docs/api/Controller) on `globalThis.__DC_CONTROLLERS__`, so an agent connected through
+[Chrome DevTools MCP](https://github.com/ChromeDevTools/chrome-devtools-mcp), or
+[Expo MCP](https://docs.expo.dev/eas/ai/mcp/) for React Native, can read the store, follow dispatched actions, and
+match them to network requests. The map is there in browsers, React Native and Node
+([#3753](https://github.com/reactive/data-client/pull/3753)).
+
+```js title="Browser DevTools console"
+__DC_CONTROLLERS__.get('Data Client: My App').getState();
+```
+
+The React and Vue skills drive this through Chrome DevTools MCP; see
+[debugging with agents](/docs/getting-started/debugging#debugging-with-agents) for what they can inspect.
+
+### Upgrade skills {#upgrades}
+
+Skills also handle upgrades. The v0.18 skill runs the [codemod](/codemods/v0.18.js), then handles what it can't, like
+registering [`argsKey`](/rest/api/SchemaSimple) for schemas whose output depends on endpoint args
+([v0.18 migration](/blog/2026/05/01/v0.18-scalar-typed-downloads#migration-guide)). The path-to-regexp v8 skill rewrites
+[path syntax](/blog/2026/04/01/v0.16-parallel-fetching-collection-move-lazy#path-to-regexp-v8) in your endpoints.
+
+
diff --git a/website/blog/tags.yml b/website/blog/tags.yml
index 0d521d19175d..67a9297d8442 100644
--- a/website/blog/tags.yml
+++ b/website/blog/tags.yml
@@ -67,3 +67,8 @@ performance:
label: 'Performance'
description: 'Performance improvements and optimizations'
permalink: /performance
+
+agents:
+ label: 'AI Agents'
+ description: 'Skills, llms.txt and tools for AI coding agents'
+ permalink: /agents
diff --git a/website/scripts/card-icons/README.md b/website/scripts/card-icons/README.md
new file mode 100644
index 000000000000..3db133a135de
--- /dev/null
+++ b/website/scripts/card-icons/README.md
@@ -0,0 +1 @@
+Brand icons from [simple-icons](https://simpleicons.org) 16.34.0 (CC0 1.0), used by `../socialCard.mjs`.
diff --git a/website/scripts/card-icons/claude.svg b/website/scripts/card-icons/claude.svg
new file mode 100644
index 000000000000..1beee8612185
--- /dev/null
+++ b/website/scripts/card-icons/claude.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/cline.svg b/website/scripts/card-icons/cline.svg
new file mode 100644
index 000000000000..0fe6a51fd8d1
--- /dev/null
+++ b/website/scripts/card-icons/cline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/cursor.svg b/website/scripts/card-icons/cursor.svg
new file mode 100644
index 000000000000..61303fbcc34f
--- /dev/null
+++ b/website/scripts/card-icons/cursor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/expo.svg b/website/scripts/card-icons/expo.svg
new file mode 100644
index 000000000000..46a6a4182136
--- /dev/null
+++ b/website/scripts/card-icons/expo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/githubcopilot.svg b/website/scripts/card-icons/githubcopilot.svg
new file mode 100644
index 000000000000..f064947d1daa
--- /dev/null
+++ b/website/scripts/card-icons/githubcopilot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/googlechrome.svg b/website/scripts/card-icons/googlechrome.svg
new file mode 100644
index 000000000000..919ac2a1a8bf
--- /dev/null
+++ b/website/scripts/card-icons/googlechrome.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/googlegemini.svg b/website/scripts/card-icons/googlegemini.svg
new file mode 100644
index 000000000000..765adb0887e6
--- /dev/null
+++ b/website/scripts/card-icons/googlegemini.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/markdown.svg b/website/scripts/card-icons/markdown.svg
new file mode 100644
index 000000000000..53130fa5105f
--- /dev/null
+++ b/website/scripts/card-icons/markdown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/vuedotjs.svg b/website/scripts/card-icons/vuedotjs.svg
new file mode 100644
index 000000000000..46110dc05ca8
--- /dev/null
+++ b/website/scripts/card-icons/vuedotjs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/card-icons/windsurf.svg b/website/scripts/card-icons/windsurf.svg
new file mode 100644
index 000000000000..bb77fc9348b5
--- /dev/null
+++ b/website/scripts/card-icons/windsurf.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/scripts/socialCard.mjs b/website/scripts/socialCard.mjs
index 57b74c132bae..4aa8e576d440 100644
--- a/website/scripts/socialCard.mjs
+++ b/website/scripts/socialCard.mjs
@@ -5,6 +5,11 @@
* Usage (from repo root):
* yarn workspace rdc-website social-card blog/2026-10-03-v0.19-batch-set.md
* yarn workspace rdc-website social-card 0.19 --force # overwrite existing
+ * yarn workspace rdc-website social-card coding-agents # non-release post, by slug
+ *
+ * Release posts (titled `vX.Y: ...`) write X.Y-card.png with a RELEASE pill and
+ * a visual per headline feature. Other posts write -card.png with their
+ * first tag's label as the pill and their summary bullets listed beside it.
*
* Cards before v0.19 are hand-made; don't --force over them.
*
@@ -34,43 +39,115 @@ const OUT_DIR = path.join(WEBSITE_ROOT, 'static/img/social');
const WIDTH = 1731;
const HEIGHT = 909;
const MAX_ROWS = 4;
+// Non-release cards list features instead of showing visuals, so fit more
+const MAX_FEATURE_ROWS = 6;
+// simple-icons brand marks (CC0): single-path, monochrome, 24x24
+const ICON_DIR = path.join(WEBSITE_ROOT, 'scripts/card-icons');
+// Agents the skills CLI (`npx skills add`) installs into, shown under the
+// tagline of posts tagged `agents`
+const AGENT_ICONS = [
+ 'claude',
+ 'cursor',
+ 'githubcopilot',
+ 'windsurf',
+ 'googlegemini',
+ 'cline',
+];
function findPost(arg) {
- if (!arg) throw new Error('Pass a blog post path or a version like 0.19');
+ if (!arg)
+ throw new Error('Pass a blog post path, a version like 0.19, or a slug');
if (fs.existsSync(arg)) return arg;
const inBlog = path.join(WEBSITE_ROOT, arg);
if (fs.existsSync(inBlog)) return inBlog;
const version = arg.replace(/^v/, '');
- const match = fs
- .readdirSync(BLOG_DIR)
- .find(f => f.includes(`-v${version}-`) && /\.mdx?$/.test(f));
- if (!match) throw new Error(`No blog post found for v${version}`);
+ const posts = fs.readdirSync(BLOG_DIR).filter(f => /\.mdx?$/.test(f));
+ const match =
+ posts.find(f => f.includes(`-v${version}-`)) ??
+ posts.find(f => postSlug(f) === arg);
+ if (!match) throw new Error(`No blog post found for ${arg}`);
return path.join(BLOG_DIR, match);
}
+// File name without its date prefix and extension, as Docusaurus slugs it
+const postSlug = file =>
+ path.basename(file).replace(/^\d{4}-\d{2}-\d{2}-|\.mdx?$/g, '');
+
+// A tag's label from tags.yml, like 'AI Agents' for `agents`
+function tagLabel(tag) {
+ const tags = fs.readFileSync(path.join(BLOG_DIR, 'tags.yml'), 'utf8');
+ return tags.match(
+ new RegExp(`^${tag}:\\s*\\n\\s*label: '([^']+)'`, 'm'),
+ )?.[1];
+}
+
async function parsePost(postFile) {
const { frontMatter, content: body } = await DEFAULT_PARSE_FRONT_MATTER({
filePath: postFile,
fileContent: fs.readFileSync(postFile, 'utf8').replace(/\r\n/g, '\n'),
});
- const { title, description = '', image } = frontMatter;
- const [, version, headline] = title.match(/^v(\d+\.\d+):?\s*(.*)$/);
+ const { title, description = '', image, tags = [] } = frontMatter;
+ const [, version, headline = title] =
+ title.match(/^v(\d+\.\d+):?\s*(.*)$/) ?? [];
+ const name = version ?? postSlug(postFile);
+ const pill =
+ version ? 'RELEASE' : (tagLabel(tags[0]) ?? tags[0] ?? '').toUpperCase();
const [summary, details = ''] = body.split(
/\{\/\*\s*truncate\s*\*\/\}|/,
);
const bullets = summaryBullets(summary);
return {
version,
+ name,
+ pill,
headline,
description,
image,
rows: bullets
- .slice(0, MAX_ROWS)
+ .slice(0, version ? MAX_ROWS : MAX_FEATURE_ROWS)
.map(({ tag, text }) => ({ tag, label: bulletLabel(text) })),
- feature: featureVisual(headline, bullets, details),
+ feature: version && featureVisual(headline, bullets, details),
+ agents: tags.includes('agents') && AGENT_ICONS,
};
}
+// Stroked glyphs for rows without a brand
+const GLYPHS = {
+ sparkles:
+ '',
+ upgrade:
+ '',
+};
+// A row's icon by label keyword: a brand mark in its brand color (the site
+// accent for black-on-white marks), else a glyph. Brands go before `skill`
+// since most labels end in it.
+const ROW_ICONS = [
+ [/claude/i, 'claude', '#e08a6c'],
+ [/vue/i, 'vuedotjs', '#4fc08d'],
+ [/chrome|devtools/i, 'googlechrome', '#4285f4'],
+ [/expo|react native/i, 'expo', '#e6edf7'],
+ [/llms|markdown/i, 'markdown', '#4aa3ff'],
+ [/upgrade|migrat|codemod/i, 'upgrade', '#a78bfa'],
+ [/skill|agent/i, 'sparkles', '#f5c76b'],
+];
+
+function brandIcon(name, color = 'currentColor') {
+ return fs
+ .readFileSync(path.join(ICON_DIR, `${name}.svg`), 'utf8')
+ .replace(/.*?<\/title>/, '')
+ .replace('