Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/data-client-manager/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: data-client-manager
description: Write @data-client Managers for app-wide side effects and real-time data such as websockets, SSE and polling: code that reacts to every request or to events from outside the app and updates the store. Use one instead of useEffect for behavior that belongs to the whole app, such as error toasts and reporting, logging and analytics, auth failures, refetching on focus or reconnect, and cross-tab sync.
description: "Write @data-client Managers for app-wide side effects and real-time data such as websockets, SSE and polling: code that reacts to every request or to events from outside the app and updates the store. Use one instead of useEffect for behavior that belongs to the whole app, such as error toasts and reporting, logging and analytics, auth failures, refetching on focus or reconnect, and cross-tab sync."
license: Apache 2.0
---

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/data-client-react-testing/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: data-client-react-testing
description: Test React code that uses @data-client/react: components and hooks that fetch or change data, with mocked responses for loading, error, mutation and polling cases. Covers renderDataHook, MockResolver and fixtures.
description: "Test React code that uses @data-client/react: components and hooks that fetch or change data, with mocked responses for loading, error, mutation and polling cases. Covers renderDataHook, MockResolver and fixtures."
license: Apache 2.0
---

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/data-client-react/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: data-client-react
description: Read, change and render async and shared state in React with @data-client/react: any data from an API or other async source, and global state shared across components (it has to persist somewhere, which makes it async). Use it for loading and error states, mutations and forms, filtered or derived views, and debugging data that looks wrong or stale on screen. Use it in place of fetch in useEffect, or app-wide useState and context; its main hooks are useSuspense, useQuery and useController.
description: "Read, change and render async and shared state in React with @data-client/react: any data from an API or other async source, and global state shared across components (it has to persist somewhere, which makes it async). Use it for loading and error states, mutations and forms, filtered or derived views, and debugging data that looks wrong or stale on screen. Use it in place of fetch in useEffect, or app-wide useState and context; its main hooks are useSuspense, useQuery and useController."
license: Apache 2.0
---
## Rendering
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/data-client-vue-testing/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: data-client-vue-testing
description: Test Vue code that uses @data-client/vue: components and composables that fetch or change data, with mocked responses for loading, error, mutation and polling cases. Covers renderDataCompose, mountDataClient and fixtures.
description: "Test Vue code that uses @data-client/vue: components and composables that fetch or change data, with mocked responses for loading, error, mutation and polling cases. Covers renderDataCompose, mountDataClient and fixtures."
license: Apache 2.0
---

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/data-client-vue/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: data-client-vue
description: Read, change and render async and shared state in Vue with @data-client/vue: any data from an API or other async source, and global state shared across components (it has to persist somewhere, which makes it async). Use it for loading and error states, mutations and forms, filtered or derived views, and debugging data that looks wrong or stale on screen. Use it in place of fetch in onMounted or watch, or app-wide refs and provide/inject; its main composables are useSuspense, useQuery and useController.
description: "Read, change and render async and shared state in Vue with @data-client/vue: any data from an API or other async source, and global state shared across components (it has to persist somewhere, which makes it async). Use it for loading and error states, mutations and forms, filtered or derived views, and debugging data that looks wrong or stale on screen. Use it in place of fetch in onMounted or watch, or app-wide refs and provide/inject; its main composables are useSuspense, useQuery and useController."
license: Apache 2.0
---
## Setup
Expand Down
1 change: 1 addition & 0 deletions .claude-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Each listing reads the plugin from `.agents/skills`, whose `README.md` is the li
- `.agents/skill-packages.json`: which package ships which skill, so the test can check the plugin's skills also ship
in a package.
- `website/framework-docs/skillCommands.mjs` and the READMEs that copy its setup commands (root, react, vue, rest, graphql, endpoint), and `website/src/components/AgentPrompt.tsx`'s default prompt: `readme-commands.test.mjs`.
- Every `.agents/skills/*/SKILL.md` frontmatter, which `npx skills add` parses as strict YAML: `skill-frontmatter.test.mjs`.

## CI

Expand Down
5 changes: 4 additions & 1 deletion .claude-plugin/readme-commands.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
cursorSetupPrompt,
setupCommand,
setupPrompt,
setupSkillFile,
} from '../website/framework-docs/skillCommands.mjs';

const read = file => fs.readFileSync(path.join(root, file), 'utf8');
Expand Down Expand Up @@ -60,7 +61,9 @@ for (const pkg of ['rest', 'graphql', 'endpoint']) {

test("AgentPrompt's default prompt matches setupPrompt", () => {
// its translate() literal can't import setupPrompt (i18n extraction needs a literal)
const message = setupPrompt('{framework}').replace(setupCommand, '{command}');
const message = setupPrompt('{framework}')
.replace(setupCommand, '{command}')
.replace(setupSkillFile, '{skillFile}');
assert.ok(
read('website/src/components/AgentPrompt.tsx').includes(`'${message}'`),
message,
Expand Down
33 changes: 33 additions & 0 deletions .claude-plugin/skill-frontmatter.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Keeps every skill's top-level frontmatter values valid YAML: `npx skills add` parses it strictly
// and skips a skill it can't parse, with only a warning:
// 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 './plugin-dir.mjs';

const skills = path.join(root, '.agents/skills');

test('skill frontmatter values that need quotes have them', () => {
for (const skill of fs.readdirSync(skills)) {
const file = path.join(skills, skill, 'SKILL.md');
if (!fs.existsSync(file)) continue;
const match = fs
.readFileSync(file, 'utf8')
.match(/^---\r?\n([^]*?)\r?\n---\r?\n/);
assert.ok(match, `${skill}: frontmatter must open the file and close`);
for (const line of match[1].split(/\r?\n/)) {
const [, key, value] = line.match(/^(\w[\w-]*): (.*)$/) ?? [];
// a plain (unquoted) scalar can't start with an indicator like `@`, and
// `: ` (or a trailing `:`) starts a mapping and ` #` a comment
if (value && !/^("(?:[^"\\]|\\.)*"|'(?:[^']|'')*'|[>|]-?)$/.test(value))
assert.doesNotMatch(
value,
/^[-?:,[\]{}#&*!|>'"%@`]|: | #|:$/,
`${skill} ${key}: quote it`,
);
}
}
});
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Then [add the provider](https://dataclient.io/docs/getting-started/installation#
Or skip the steps above and paste this into your coding agent's chat. It installs Data Client's setup skill, which adds the packages, wires up the provider and links the skills they ship:

```prompt
Set up `@data-client/react` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then follow it.
Set up `@data-client/react` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then read `.agents/skills/data-client-setup/SKILL.md` and follow it.
```

<details>
Expand Down
2 changes: 1 addition & 1 deletion packages/endpoint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Declarative, strongly typed, reusable network definitions for networking librari
Tell your coding agent:

```prompt
Set up `@data-client/endpoint` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then follow it.
Set up `@data-client/endpoint` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then read `.agents/skills/data-client-setup/SKILL.md` and follow it.
```

It installs the packages, wires up React or Vue, and links the skills they ship.
Expand Down
2 changes: 1 addition & 1 deletion packages/graphql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
Tell your coding agent:

```prompt
Set up `@data-client/graphql` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then follow it.
Set up `@data-client/graphql` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then read `.agents/skills/data-client-setup/SKILL.md` and follow it.
```

It installs the packages, wires up React or Vue, and links the skills they ship.
Expand Down
2 changes: 1 addition & 1 deletion packages/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Then [add the provider](https://dataclient.io/docs/getting-started/installation#
Or skip the steps above and paste this into your coding agent's chat. It installs Data Client's setup skill, which adds the packages, wires up the provider and links the skills they ship:

```prompt
Set up `@data-client/react` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then follow it.
Set up `@data-client/react` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then read `.agents/skills/data-client-setup/SKILL.md` and follow it.
```

<details>
Expand Down
2 changes: 1 addition & 1 deletion packages/rest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
Tell your coding agent:

```prompt
Set up `@data-client/rest` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then follow it.
Set up `@data-client/rest` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then read `.agents/skills/data-client-setup/SKILL.md` and follow it.
```

It installs the packages, wires up React or Vue, and links the skills they ship.
Expand Down
2 changes: 1 addition & 1 deletion packages/vue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ app.mount('#app');
Or skip the steps above and paste this into your coding agent's chat. It installs Data Client's setup skill, which adds the packages, wires up the provider and links the skills they ship:

```prompt
Set up `@data-client/vue` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then follow it.
Set up `@data-client/vue` and use it for all API data. Install its setup skill first: `npx skills add reactive/data-client --skill data-client-setup -y`, then read `.agents/skills/data-client-setup/SKILL.md` and follow it.
```

<details>
Expand Down
8 changes: 6 additions & 2 deletions website/framework-docs/skillCommands.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ export const claudePluginCommand = [
const setupTask = framework =>
`Set up \`@data-client/${framework}\` and use it for all API data`;

/** The prompt any agent takes: installs the setup skill, then follows it.
/** Where setupCommand puts the setup skill for every agent; prompts have the
* agent read it by path, since only users can invoke it by name */
export const setupSkillFile = `${skillsDir}/data-client-setup/SKILL.md`;

/** The prompt any agent takes: installs the setup skill, then reads and follows it.
* AgentPrompt's translate() default repeats it as a literal for i18n extraction */
export const setupPrompt = pkg =>
`${setupTask(pkg)}. Install its setup skill first: \`${setupCommand}\`, then follow it.`;
`${setupTask(pkg)}. Install its setup skill first: \`${setupCommand}\`, then read \`${setupSkillFile}\` and follow it.`;

/** Starts the plugin's setup skill by name; through the router skill, Claude
* would read it from the plugin cache, outside the project, and ask first */
Expand Down
4 changes: 2 additions & 2 deletions website/llms-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ module.exports = function llmsPlugin(context) {
if (context.i18n.currentLocale !== context.i18n.defaultLocale) return;
const { docToMarkdown, agentReferenceUrl, ROOT } =
await import('./framework-docs/docsToMarkdown.mjs');
const { setupCommand, linkCommand } =
const { setupCommand, setupSkillFile, linkCommand } =
await import('./framework-docs/skillCommands.mjs');

// docs instance id -> its folder and sidebar sections
Expand Down Expand Up @@ -139,7 +139,7 @@ module.exports = function llmsPlugin(context) {
o => `Using ${o.name}? See ${url}${llmsTxtRoute(o.id)}`,
);
// the same steps the site's setup prompt gives (AgentPrompt)
const setupStep = `To add Data Client to an app, run \`${setupCommand}\` from the project root, then read \`.agents/skills/data-client-setup/SKILL.md\` and follow it.`;
const setupStep = `To add Data Client to an app, run \`${setupCommand}\` from the project root, then read \`${setupSkillFile}\` and follow it.`;
/** Title, summary and intro of a docs instance's llms.txt */
const header = instance => {
const { id, name, framework } = instance;
Expand Down
11 changes: 7 additions & 4 deletions website/src/components/AgentPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import React, { useCallback, useId } from 'react';
import styles from './AgentPrompt.module.css';
import useFramework from './useFramework';
import useTapped from './useTapped';
import { setupCommand } from '../../framework-docs/skillCommands.mjs';
import {
setupCommand,
setupSkillFile,
} from '../../framework-docs/skillCommands.mjs';

const FEEDBACK_MS = 2000;

Expand All @@ -27,11 +30,11 @@ export default function AgentPrompt({ prompt, title, framed }: Props) {
{
id: 'agentPrompt.prompt',
message:
'Set up `@data-client/{framework}` and use it for all API data. Install its setup skill first: `{command}`, then follow it.',
'Set up `@data-client/{framework}` and use it for all API data. Install its setup skill first: `{command}`, then read `{skillFile}` and follow it.',
description:
'Prompt readers copy into their coding agent; {command} is the shell command the agent runs. Keep the backticks around package names and the command: they keep each on one line.',
'Prompt readers copy into their coding agent; {command} is the shell command the agent runs and {skillFile} the file it then reads. Keep the backticks around package names and the command: they keep each on one line.',
},
{ framework, command: setupCommand },
{ framework, command: setupCommand, skillFile: setupSkillFile },
);
const tipId = useId();
// the check icon and the tooltip (which touch screens, lacking hover, need)
Expand Down
Loading