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
7 changes: 7 additions & 0 deletions .changeset/brave-skills-point.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"solid-relay": patch
---

feat: add `solid-relay` agent skill

Adds a pointer skill under `skills/solid-relay` that routes agents to the guide docs. The guides are embedded into the skill's `references/` directory at build time, and the reference table is generated from each guide's `skillPointer` frontmatter field.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist/
__generated__/
.pnpm-store/
coverage/
skills/solid-relay/references/
2 changes: 1 addition & 1 deletion .oxfmtrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"sortImports": {
"newlinesBetween": false,
},
"ignorePatterns": [".changeset/", "docs/**/api"],
"ignorePatterns": [".changeset/", "docs/**/api", "skills/"],
}
1 change: 1 addition & 0 deletions docs/src/routes/guide/fragments.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Fragments
skillPointer: "`createFragment`, composition, data masking, `createRefetchableFragment`, fragment variables"
---

# Fragments
Expand Down
1 change: 1 addition & 0 deletions docs/src/routes/guide/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Getting Started
skillPointer: "Installing, Vite/SolidStart plugin config, `relay.config`, environment + `RelayEnvironmentProvider`"
---

# Getting Started
Expand Down
1 change: 1 addition & 0 deletions docs/src/routes/guide/invalidation.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Store Invalidation
skillPointer: "`createSubscriptionToInvalidationState`, reacting to `invalidateRecord` / `invalidateStore`, stale-data indicators, refetch-on-invalidation"
---

# Store Invalidation
Expand Down
1 change: 1 addition & 0 deletions docs/src/routes/guide/mutations.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Mutations
skillPointer: "`createMutation`, optimistic updates, updater functions, connection updates"
---

# Mutations
Expand Down
1 change: 1 addition & 0 deletions docs/src/routes/guide/pagination.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Pagination
skillPointer: "`createPaginationFragment`, `@connection`, bidirectional / infinite scroll, search, virtualized lists"
---

# Pagination
Expand Down
1 change: 1 addition & 0 deletions docs/src/routes/guide/querying.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Querying Data
skillPointer: "Fetching with `createLazyLoadQuery`, variables, fetch policies, route preloading with `loadQuery` / `createQueryLoader` / `createPreloadedQuery`, error and loading states"
---

# Querying Data
Expand Down
1 change: 1 addition & 0 deletions docs/src/routes/guide/subscriptions.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Subscriptions
skillPointer: "`createSubscription`, real-time store updates"
---

# Subscriptions
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"url": "git+https://github.com/XiNiHa/solid-relay.git"
},
"files": [
"dist"
"dist",
"skills"
],
"type": "module",
"main": "./dist/index.cjs",
Expand All @@ -31,7 +32,8 @@
},
"scripts": {
"prepare": "playwright install",
"build": "tsdown",
"build": "tsdown && pnpm build:skill",
"build:skill": "node scripts/build-skill.ts",
"lint": "oxlint",
"format": "oxfmt",
"check": "oxlint && oxfmt --check",
Expand Down
79 changes: 79 additions & 0 deletions scripts/build-skill.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Embeds the guide docs (docs/src/routes/guide) into skills/solid-relay/references
* as plain markdown, and regenerates the reference table in SKILL.md from each
* guide's `skillPointer` frontmatter field.
*/
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
import path from "node:path";

const root = path.resolve(import.meta.dirname, "..");
const guideDir = path.join(root, "docs/src/routes/guide");
const skillDir = path.join(root, "skills/solid-relay");
const outDir = path.join(skillDir, "references");
const skillFile = path.join(skillDir, "SKILL.md");

const outputName = (file: string) =>
file === "index.mdx" ? "getting-started.md" : file.replace(/\.mdx$/, ".md");

// YAML scalars quoted for reserved characters (backticks, colons) — unwrap and unescape them
const unquote = (value: string) => {
const match = value.match(/^"(.*)"$/) ?? value.match(/^'(.*)'$/);
if (!match) return value;
return value.startsWith('"')
? match[1].replace(/\\(["\\])/g, "$1")
: match[1].replace(/''/g, "'");
};

const parseFrontmatter = (source: string) => {
const match = source.match(/^---\n([\s\S]*?)\n---\n+/);
if (!match) throw new Error("missing frontmatter");
const fields = Object.fromEntries(
match[1].split("\n").map((line) => {
const [key, ...rest] = line.split(":");
return [key.trim(), unquote(rest.join(":").trim())];
}),
);
return { fields, body: source.slice(match[0].length) };
};

const transform = (body: string) =>
body
// docs-site-only fences for the package manager tabs
.replace(
/```package-install-dev\n([\s\S]*?)```/g,
(_, pkgs: string) => "```sh\nnpm install -D " + pkgs.trim() + "\n```",
)
.replace(
/```package-install\n([\s\S]*?)```/g,
(_, pkgs: string) => "```sh\nnpm install " + pkgs.trim() + "\n```",
)
// site-relative links → sibling files
.replace(/\]\(\/guide\/([\w-]+)\)/g, (_, slug: string) => `](./${slug}.md)`)
.replace(/\]\(\/guide\/?\)/g, "](./getting-started.md)")
.trimEnd() + "\n";

await rm(outDir, { recursive: true, force: true });
await mkdir(outDir, { recursive: true });

const files = (await readdir(guideDir)).filter((f) => f.endsWith(".mdx")).sort();

const tableHeader = "| Task | Reference |";
const rows = [tableHeader, "| --- | --- |"];
for (const file of files) {
const { fields, body } = parseFrontmatter(await readFile(path.join(guideDir, file), "utf8"));
if (!fields.skillPointer) throw new Error(`${file}: missing \`skillPointer\` frontmatter field`);
const name = outputName(file);
await writeFile(path.join(outDir, name), transform(body));
rows.push(`| ${fields.skillPointer} | \`references/${name}\` |`);
}

// replace the markdown table that opens with `tableHeader` (header row through the last contiguous `|` row)
const skill = await readFile(skillFile, "utf8");
const lines = skill.split("\n");
const start = lines.indexOf(tableHeader);
if (start === -1) throw new Error(`SKILL.md: missing table with header ${tableHeader}`);
let end = start;
while (end + 1 < lines.length && lines[end + 1].startsWith("|")) end++;
lines.splice(start, end - start + 1, ...rows);
await writeFile(skillFile, lines.join("\n"));
console.log(`Embedded ${files.length} guide docs into ${path.relative(root, outDir)}`);
Loading
Loading