diff --git a/.agents/skills/.claude-plugin/plugin.json b/.agents/skills/.claude-plugin/plugin.json
new file mode 100644
index 000000000000..9e5e266bc392
--- /dev/null
+++ b/.agents/skills/.claude-plugin/plugin.json
@@ -0,0 +1,24 @@
+{
+ "name": "data-client",
+ "displayName": "Reactive Data Client",
+ "description": "Sets up Data Client, then points your agent at the skills shipped in your installed @data-client packages, so React or Vue apps get the guidance for their version",
+ "author": {
+ "name": "Nathaniel Tucker",
+ "email": "me@ntucker.me"
+ },
+ "homepage": "https://dataclient.io/docs/getting-started/agent-skills",
+ "repository": "https://github.com/reactive/data-client",
+ "license": "Apache-2.0",
+ "keywords": [
+ "data-client",
+ "react",
+ "vue",
+ "rest",
+ "graphql",
+ "normalized-cache"
+ ],
+ "skills": ["./data-client", "./data-client-setup"],
+ "icon": "./assets/logo.svg",
+ "documentationUrl": "https://dataclient.io/docs/getting-started/agent-skills",
+ "supportUrl": "https://github.com/reactive/data-client/issues"
+}
diff --git a/.agents/skills/.cursor-plugin/plugin.json b/.agents/skills/.cursor-plugin/plugin.json
index 642a6d8b0b11..e46c20c16059 100644
--- a/.agents/skills/.cursor-plugin/plugin.json
+++ b/.agents/skills/.cursor-plugin/plugin.json
@@ -1,7 +1,6 @@
{
"name": "data-client",
"displayName": "Reactive Data Client",
- "version": "1.0.0",
"description": "Sets up Data Client, then points your agent at the skills shipped in your installed @data-client packages, so React or Vue apps get the guidance for their version",
"author": {
"name": "Nathaniel Tucker",
@@ -18,8 +17,9 @@
"graphql",
"normalized-cache"
],
+ "skills": ["./data-client", "./data-client-setup"],
+ "version": "1.0.0",
"category": "developer-tools",
"tags": ["react", "vue", "data-fetching"],
- "logo": "assets/logo.svg",
- "skills": ["./data-client", "./data-client-setup"]
+ "logo": "assets/logo.svg"
}
diff --git a/.agents/skills/README.md b/.agents/skills/README.md
new file mode 100644
index 000000000000..06bb638dba8e
--- /dev/null
+++ b/.agents/skills/README.md
@@ -0,0 +1,58 @@
+# Reactive Data Client
+
+[Reactive Data Client](https://dataclient.io) manages server state in React and Vue apps: fetching, caching,
+mutations and real-time updates, all kept consistent through a normalized store. This plugin teaches your coding
+agent to use it well.
+
+The plugin carries two skills:
+
+- **`data-client`** starts any task in an app that uses Data Client, or that should. It sends the agent to the
+ skill for the work at hand: schemas, REST endpoints, React hooks, Vue composables, tests, or real-time managers.
+- **`data-client-setup`** adds Data Client to an app. It runs only when you ask for it. It detects your framework
+ (NextJS, Expo, React Native, Vue or plain React) and protocol (REST, GraphQL or other async code), tells you which
+ packages it will install, installs them with your package manager, adds the provider, and follows the setup guide
+ for your protocol.
+
+Every other skill ships inside the `@data-client/*` package it covers, so its guidance always matches the version
+your project installs. The setup skill links them into your project's `.agents/skills` folder (and `.claude/skills`)
+by running `npx --no data-client-skills`, which uses only your installed packages and never downloads one.
+
+## Install
+
+With Claude Code, for everyone working on the project:
+
+```bash
+claude plugin marketplace add reactive/data-client --scope project \
+ --sparse .claude-plugin .agents/skills
+claude plugin install data-client@data-client --scope project
+```
+
+In Cursor's agent chat:
+
+```
+/add-plugin data-client@https://github.com/reactive/data-client
+```
+
+In Codex:
+
+```bash
+codex plugin marketplace add reactive/data-client \
+ --sparse .claude-plugin --sparse .agents/skills
+codex plugin add data-client@data-client
+```
+
+Then ask your agent to set up Data Client, or start the setup skill yourself: `/data-client:data-client-setup` in
+Claude Code, `/data-client-setup` in Cursor, `$data-client:data-client-setup` in Codex.
+
+## What it runs
+
+The plugin has no hooks, MCP servers or scripts of its own, and it sends no data anywhere. When you ask it to set up
+Data Client, the agent runs your package manager to install the `@data-client/*` packages and runs the
+`data-client-skills` command those packages include. Projects on packages older than 0.19 install the skills with
+the [skills CLI](https://www.npmjs.com/package/skills) instead.
+
+## Learn more
+
+- [Agent skills guide](https://dataclient.io/docs/getting-started/agent-skills)
+- [Documentation](https://dataclient.io/docs)
+- [Source](https://github.com/reactive/data-client), licensed under Apache-2.0
diff --git a/.agents/skills/assets/logo.svg b/.agents/skills/assets/logo.svg
index cfe8593d10fc..59f2742f5c01 100644
--- a/.agents/skills/assets/logo.svg
+++ b/.agents/skills/assets/logo.svg
@@ -1,22 +1,17 @@
-
-
-
+
diff --git a/.claude-plugin/README.md b/.claude-plugin/README.md
index a460bde43b29..e66c98c186b8 100644
--- a/.claude-plugin/README.md
+++ b/.claude-plugin/README.md
@@ -11,12 +11,28 @@ endpoint, rest, graphql, react and vue, implemented in `packages/core/agents/` a
| File | What |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `marketplace.json` | The marketplace Claude Code reads (`claude plugin marketplace add reactive/data-client`). `data-client` is the plugin; `core`, `react`, `vue` and `migrations` are old names kept as copies so existing installs keep updating (`plans/next-breaking-release.md` removes them). |
-| `cursor.mjs` | Writes Cursor's copy of the plugin (see below). |
-| `*.test.mjs` | `node --test '.claude-plugin/*.test.mjs'`: `marketplace.json` and the Cursor copy against the repo; READMEs against the site. |
+| `plugin-dir.mjs` | Writes the manifests that make `.agents/skills` a self-contained copy of the plugin, for Cursor and Claude's plugin directory (see below). |
+| `openai-zip.mjs` | Packs the plugin as the ZIP OpenAI's plugin directory uploads (see below). |
+| `*.test.mjs` | `node --test '.claude-plugin/*.test.mjs'`: `marketplace.json` and the plugin directory copies against the repo; READMEs against the site. |
## Codex and Cursor
-Codex reads `marketplace.json` as is (`codex plugin add data-client@data-client`). Cursor plugins can't reach files outside their folder, so `cursor.mjs` writes a copy of the `data-client` entry rooted at `.agents/skills` (`.cursor-plugin/marketplace.json`, `.agents/skills/.cursor-plugin/plugin.json`), and `marketplace.test.mjs` fails when they drift. The marketplace source is a `git-subdir` pinned to `master`: Cursor's `/add-plugin` otherwise checks out `main`, which this repo does not have. Cursor's published `marketplace.schema.json` still types `source` as a string; the object form comes from its plugin docs, and the Cursor agent's install resolved it.
+Codex reads `marketplace.json` as is (`codex plugin add data-client@data-client`). Cursor plugins can't reach files outside their folder, so `plugin-dir.mjs` writes a copy of the `data-client` entry rooted at `.agents/skills` (`.cursor-plugin/marketplace.json`, `.agents/skills/.cursor-plugin/plugin.json`, and the `.agents/skills/.claude-plugin/plugin.json` Claude's directory reads), and `marketplace.test.mjs` fails when they drift. The marketplace source is a `git-subdir` pinned to `master`: Cursor's `/add-plugin` otherwise checks out `main`, which this repo does not have. Cursor's published `marketplace.schema.json` still types `source` as a string; the object form comes from its plugin docs, and the Cursor agent's install resolved it.
+
+## Public listings
+
+Each listing reads the plugin from `.agents/skills`, whose `README.md` is the listing's description.
+
+- **Claude's plugin directory** ([claude.ai/directory/manage](https://claude.ai/directory/manage), plugin path
+ `.agents/skills`, branch `master`) reads `.agents/skills/.claude-plugin/plugin.json`, written without a `version` so
+ every commit is an update. The `agent-rules` workflow checks its manifest and skills with `claude plugin validate ./.agents/skills`; the
+ directory's safety scan and policy review run only on submission. The
+ directory refuses repositories whose GitHub archive passes 50 MiB.
+- **Cursor Marketplace** ([cursor.com/marketplace/publish](https://cursor.com/marketplace/publish)) reviews the
+ repository link and `.cursor-plugin/marketplace.json`.
+- **OpenAI's plugin directory** (ChatGPT and Codex) takes an uploaded ZIP, not the repository:
+ `node .claude-plugin/openai-zip.mjs` writes `data-client-plugin.zip`, versioned by its build time. Upload a new one
+ whenever the plugin's skills change.
## What it reads from the rest of the repo
@@ -28,5 +44,5 @@ Codex reads `marketplace.json` as is (`codex plugin add data-client@data-client`
## CI
The `agent-rules` workflow runs these tests and the `data-client-skills` tests (`packages/core/agents/*.test.mjs`), with
-no install. Its `paths` cover this directory and the inputs above, and `marketplace.test.mjs` fails when the old names
-drift from `data-client` or a plugin skill goes missing or stops shipping in a package. `cursor-plugin.test.mjs` checks that the Cursor manifests still install.
+no install, then fetches a pinned Claude Code CLI for `claude plugin validate`. Its `paths` cover this directory and the inputs above, and `marketplace.test.mjs` fails when the old names
+drift from `data-client` or a plugin skill goes missing or stops shipping in a package. `plugin-dir.test.mjs` checks the skill names, logo and README the plugin directory listings read.
diff --git a/.claude-plugin/cursor.mjs b/.claude-plugin/cursor.mjs
deleted file mode 100644
index 96eac7173f42..000000000000
--- a/.claude-plugin/cursor.mjs
+++ /dev/null
@@ -1,82 +0,0 @@
-// Writes the Cursor copy of the `data-client` plugin: node .claude-plugin/cursor.mjs
-// Cursor plugins can't reach files outside their folder, so it's rooted at
-// .agents/skills. marketplace.test.mjs fails when the written files drift
-// from marketplace.json.
-//
-// `/add-plugin` checks out ref "main" when the marketplace entry's source
-// doesn't pin one, and a branch on the GitHub URL is ignored. This repo's
-// default branch is master, so a relative source fails with
-// `git ls-remote did not return a resolvable commit for ref "main"`.
-// git-subdir sparse-checks out only the plugin directory at master.
-import fs from 'node:fs';
-import path from 'node:path';
-import { fileURLToPath } from 'node:url';
-
-export const root = path.resolve(
- path.dirname(fileURLToPath(import.meta.url)),
- '..',
-);
-const skills = '.agents/skills';
-const repository = 'https://github.com/reactive/data-client';
-
-/** Cursor's manifests (path => JSON): the `data-client` entry of
- * marketplace.json plus Cursor's own listing fields */
-export function cursorFiles() {
- const marketplace = JSON.parse(
- fs.readFileSync(path.join(root, '.claude-plugin/marketplace.json'), 'utf8'),
- );
- const plugin = marketplace.plugins.find(p => p.name === marketplace.name);
- return {
- '.cursor-plugin/marketplace.json': {
- name: marketplace.name,
- owner: marketplace.owner,
- metadata: marketplace.metadata,
- plugins: [
- {
- name: plugin.name,
- source: {
- source: 'git-subdir',
- url: `${repository}.git`,
- path: skills,
- ref: 'master',
- },
- description: plugin.description,
- },
- ],
- },
- [`${skills}/.cursor-plugin/plugin.json`]: {
- name: plugin.name,
- displayName: 'Reactive Data Client',
- version: '1.0.0',
- description: plugin.description,
- author: marketplace.owner,
- homepage: plugin.homepage,
- repository,
- license: plugin.license,
- keywords: [
- 'data-client',
- 'react',
- 'vue',
- 'rest',
- 'graphql',
- 'normalized-cache',
- ],
- category: 'developer-tools',
- tags: ['react', 'vue', 'data-fetching'],
- logo: 'assets/logo.svg',
- // Each entry is a skill directory (it contains SKILL.md). Listing the
- // plugin root would also publish the maintainer skills beside them.
- skills: plugin.skills.map(skill => `./${path.relative(skills, skill)}`),
- },
- };
-}
-
-if (process.argv[1] === fileURLToPath(import.meta.url)) {
- for (const [file, json] of Object.entries(cursorFiles())) {
- fs.mkdirSync(path.dirname(path.join(root, file)), { recursive: true });
- fs.writeFileSync(
- path.join(root, file),
- `${JSON.stringify(json, null, 2)}\n`,
- );
- }
-}
diff --git a/.claude-plugin/marketplace.test.mjs b/.claude-plugin/marketplace.test.mjs
index 6efb7a06efa3..f470df26beea 100644
--- a/.claude-plugin/marketplace.test.mjs
+++ b/.claude-plugin/marketplace.test.mjs
@@ -27,9 +27,9 @@ test('plugin skills exist and ship in a package', () => {
}
});
-test('the Cursor plugin matches marketplace.json', async () => {
- // regenerate with node .claude-plugin/cursor.mjs
- const { cursorFiles } = await import('./cursor.mjs');
- for (const [file, json] of Object.entries(cursorFiles()))
+test('the plugin directory matches marketplace.json', async () => {
+ // regenerate with node .claude-plugin/plugin-dir.mjs
+ const { pluginDirFiles } = await import('./plugin-dir.mjs');
+ for (const [file, json] of Object.entries(pluginDirFiles()))
assert.deepEqual(readJSON(file), json, file);
});
diff --git a/.claude-plugin/openai-zip.mjs b/.claude-plugin/openai-zip.mjs
new file mode 100644
index 000000000000..10682cbc9d22
--- /dev/null
+++ b/.claude-plugin/openai-zip.mjs
@@ -0,0 +1,46 @@
+// Packs the `data-client` plugin as the ZIP OpenAI's plugin directory
+// (ChatGPT and Codex) uploads: node .claude-plugin/openai-zip.mjs [out.zip]
+// The directory takes a Claude plugin archive with each skill at
+// skills//SKILL.md, and doesn't follow the repo, so upload a new ZIP
+// whenever the plugin's skills change.
+import { execFileSync } from 'node:child_process';
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+
+import { pluginDirFiles, root } from './plugin-dir.mjs';
+
+const skills = path.join(root, '.agents/skills');
+const out = path.resolve(process.argv[2] ?? 'data-client-plugin.zip');
+const manifest = pluginDirFiles()['.agents/skills/.claude-plugin/plugin.json'];
+// OpenAI requires a semver `version` that grows with each upload: the UTC
+// date and time of the build. The repo's manifest has none so Claude Code
+// updates on every commit.
+const [date, time] = new Date().toISOString().replace(/[-:]/g, '').split('T');
+const version = `1.${date}.${Number(time.slice(0, 6))}`;
+const stage = fs.mkdtempSync(path.join(os.tmpdir(), 'data-client-plugin-'));
+
+try {
+ for (const skill of manifest.skills)
+ fs.cpSync(path.join(skills, skill), path.join(stage, 'skills', skill), {
+ recursive: true,
+ dereference: true,
+ // repo tooling, left out like scripts/pack-skills.mjs does
+ filter: src => !['evals', 'references.json'].includes(path.basename(src)),
+ });
+ for (const file of ['README.md', manifest.icon])
+ fs.cpSync(path.join(skills, file), path.join(stage, file));
+ fs.cpSync(path.join(root, 'LICENSE'), path.join(stage, 'LICENSE'));
+ fs.mkdirSync(path.join(stage, '.claude-plugin'));
+ fs.writeFileSync(
+ path.join(stage, '.claude-plugin/plugin.json'),
+ `${JSON.stringify({ ...manifest, version, skills: './skills/' }, null, 2)}\n`,
+ );
+
+ fs.rmSync(out, { force: true });
+ // needs the zip CLI
+ execFileSync('zip', ['-qrX', out, '.'], { cwd: stage });
+} finally {
+ fs.rmSync(stage, { recursive: true });
+}
+console.log(`Wrote ${path.relative(process.cwd(), out)}`);
diff --git a/.claude-plugin/plugin-dir.mjs b/.claude-plugin/plugin-dir.mjs
new file mode 100644
index 000000000000..e467c1736d6c
--- /dev/null
+++ b/.claude-plugin/plugin-dir.mjs
@@ -0,0 +1,98 @@
+// Writes the manifests that make .agents/skills a self-contained copy of the
+// `data-client` plugin: node .claude-plugin/plugin-dir.mjs
+// Cursor plugins and plugins listed in Claude's directory can't reach files
+// outside their folder, so they're rooted at .agents/skills.
+// marketplace.test.mjs fails when the written files drift from
+// marketplace.json.
+//
+// `/add-plugin` checks out ref "main" when the marketplace entry's source
+// doesn't pin one, and a branch on the GitHub URL is ignored. This repo's
+// default branch is master, so a relative source fails with
+// `git ls-remote did not return a resolvable commit for ref "main"`.
+// git-subdir sparse-checks out only the plugin directory at master.
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+export const root = path.resolve(
+ path.dirname(fileURLToPath(import.meta.url)),
+ '..',
+);
+const skills = '.agents/skills';
+const repository = 'https://github.com/reactive/data-client';
+const logo = 'assets/logo.svg';
+
+/** The plugin directory's manifests (path => JSON): the `data-client` entry
+ * of marketplace.json plus each listing's own fields */
+export function pluginDirFiles() {
+ const marketplace = JSON.parse(
+ fs.readFileSync(path.join(root, '.claude-plugin/marketplace.json'), 'utf8'),
+ );
+ const plugin = marketplace.plugins.find(p => p.name === marketplace.name);
+ // the fields both plugin.json listings share
+ const listing = {
+ name: plugin.name,
+ displayName: 'Reactive Data Client',
+ description: plugin.description,
+ author: marketplace.owner,
+ homepage: plugin.homepage,
+ repository,
+ license: plugin.license,
+ keywords: [
+ 'data-client',
+ 'react',
+ 'vue',
+ 'rest',
+ 'graphql',
+ 'normalized-cache',
+ ],
+ // Each entry is a skill directory (it contains SKILL.md). Listing the
+ // plugin root would also publish the maintainer skills beside them.
+ skills: plugin.skills.map(skill => `./${path.relative(skills, skill)}`),
+ };
+ return {
+ '.cursor-plugin/marketplace.json': {
+ name: marketplace.name,
+ owner: marketplace.owner,
+ metadata: marketplace.metadata,
+ plugins: [
+ {
+ name: plugin.name,
+ source: {
+ source: 'git-subdir',
+ url: `${repository}.git`,
+ path: skills,
+ ref: 'master',
+ },
+ description: plugin.description,
+ },
+ ],
+ },
+ [`${skills}/.cursor-plugin/plugin.json`]: {
+ ...listing,
+ version: '1.0.0',
+ category: 'developer-tools',
+ tags: ['react', 'vue', 'data-fetching'],
+ logo,
+ },
+ // Claude's plugin directory submits this folder. No `version`: Claude Code
+ // then treats every commit as an update, like the root marketplace does.
+ [`${skills}/.claude-plugin/plugin.json`]: {
+ ...listing,
+ // listing fields only Claude's directory reads
+ icon: `./${logo}`,
+ documentationUrl: plugin.homepage,
+ supportUrl: `${repository}/issues`,
+ },
+ };
+}
+
+if (process.argv[1] === fileURLToPath(import.meta.url)) {
+ for (const [file, json] of Object.entries(pluginDirFiles())) {
+ fs.mkdirSync(path.dirname(path.join(root, file)), { recursive: true });
+ fs.writeFileSync(
+ path.join(root, file),
+ `${JSON.stringify(json, null, 2)}\n`,
+ );
+ }
+}
diff --git a/.claude-plugin/cursor-plugin.test.mjs b/.claude-plugin/plugin-dir.test.mjs
similarity index 54%
rename from .claude-plugin/cursor-plugin.test.mjs
rename to .claude-plugin/plugin-dir.test.mjs
index 79a40c1d6858..dc0f60e8cd23 100644
--- a/.claude-plugin/cursor-plugin.test.mjs
+++ b/.claude-plugin/plugin-dir.test.mjs
@@ -1,18 +1,17 @@
-// The Cursor manifests are written by cursor.mjs, and marketplace.test.mjs
-// checks they match it. This checks what they point at still installs:
+// The plugin directory's manifests are written by plugin-dir.mjs, and
+// marketplace.test.mjs checks they match it. This checks what they point at
+// still installs:
// node --test '.claude-plugin/*.test.mjs'
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { test } from 'node:test';
-import { root } from './cursor.mjs';
+import { root } from './plugin-dir.mjs';
const pluginRoot = path.join(root, '.agents/skills');
-const manifest = JSON.parse(
- fs.readFileSync(path.join(pluginRoot, '.cursor-plugin/plugin.json'), 'utf8'),
-);
const read = file => fs.readFileSync(path.join(pluginRoot, file), 'utf8');
+const manifest = JSON.parse(read('.cursor-plugin/plugin.json'));
test('plugin skills are directories inside the plugin, named by SKILL.md', () => {
for (const skill of manifest.skills) {
@@ -32,6 +31,16 @@ test('plugin skills are directories inside the plugin, named by SKILL.md', () =>
});
test('logo stays inside the plugin directory', () => {
- assert.equal(manifest.logo.includes('..'), false);
- assert.ok(fs.existsSync(path.join(pluginRoot, manifest.logo)));
+ const { icon } = JSON.parse(read('.claude-plugin/plugin.json'));
+ for (const file of [manifest.logo, icon]) {
+ assert.equal(file.includes('..'), false, file);
+ assert.ok(fs.existsSync(path.join(pluginRoot, file)), file);
+ }
+});
+
+test('README is long enough for the Claude directory listing', () => {
+ // the directory shows it as the description and blocks under 40 words,
+ // not counting code blocks
+ const prose = read('README.md').replace(/```[\s\S]*?```/g, '');
+ assert.ok(prose.split(/\s+/).filter(Boolean).length >= 40);
});
diff --git a/.claude-plugin/readme-commands.test.mjs b/.claude-plugin/readme-commands.test.mjs
index a4a854cba039..80cdcf0595a0 100644
--- a/.claude-plugin/readme-commands.test.mjs
+++ b/.claude-plugin/readme-commands.test.mjs
@@ -5,7 +5,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { test } from 'node:test';
-import { root } from './cursor.mjs';
+import { root } from './plugin-dir.mjs';
import {
claudePluginCommand,
claudeSetupPrompt,
@@ -40,6 +40,17 @@ for (const [file, framework] of Object.entries({
});
}
+// the plugin directory listings' description
+test('.agents/skills/README.md plugin commands match skillCommands.mjs', () => {
+ const readme = read('.agents/skills/README.md');
+ for (const command of [
+ claudePluginCommand,
+ cursorPluginCommand,
+ codexPluginCommand,
+ ])
+ assert.ok(readme.includes(command), command);
+});
+
// protocol READMEs carry only the prompt
for (const pkg of ['rest', 'graphql', 'endpoint']) {
test(`packages/${pkg}/README.md setup prompt matches skillCommands.mjs`, () => {
diff --git a/.github/workflows/agent-rules.yml b/.github/workflows/agent-rules.yml
index be98f46643ad..5cd40255ad53 100644
--- a/.github/workflows/agent-rules.yml
+++ b/.github/workflows/agent-rules.yml
@@ -68,3 +68,8 @@ jobs:
run: node scripts/breaking-evals.mjs
- name: Test agent hooks and the skill sync
run: node --test '.cursor/hooks/*.test.js' '.claude-plugin/*.test.mjs' 'packages/core/agents/*.test.mjs'
+ # the folder Claude's plugin directory lists (.claude-plugin/README.md).
+ # The one step that downloads (claude-code from npm); pinned so a CLI
+ # release can't change the check without a PR bumping it.
+ - name: Validate the plugin directory
+ run: npx -y @anthropic-ai/claude-code@2.1.306 plugin validate ./.agents/skills
diff --git a/.github/workflows/regenerate.yml b/.github/workflows/regenerate.yml
index b3ace622ec00..37f57955e655 100644
--- a/.github/workflows/regenerate.yml
+++ b/.github/workflows/regenerate.yml
@@ -4,7 +4,7 @@ name: regenerate
# Fails when the committed output is stale: PR authors regenerate and commit it.
# Also type-checks the docs' React and Vue examples, and runs the agents'
# browser debugging snippets in Chromium.
-# Claude Code rules are left to agent-rules.yml, which needs no install.
+# Claude Code rules are left to agent-rules.yml, which installs no dependencies.
on:
push:
branches:
diff --git a/.gitignore b/.gitignore
index acff5beeb886..37c8f0b16306 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,3 +96,4 @@ packages/*/skills/
# data-client-skills code copied in at pack time (from packages/core/agents)
packages/*/agents/
!packages/core/agents/
+data-client-plugin.zip