@farming-labs/theme 0.1.37 → 0.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs-api.d.mts +2 -1
- package/dist/docs-api.mjs +74 -0
- package/package.json +2 -2
package/dist/docs-api.d.mts
CHANGED
|
@@ -64,6 +64,7 @@ interface DocsMCPAPIOptions {
|
|
|
64
64
|
* - **GET ?query=…** → full-text search
|
|
65
65
|
* - **GET ?format=llms** → llms.txt (concise page listing)
|
|
66
66
|
* - **GET ?format=llms-full** → llms-full.txt (full page content)
|
|
67
|
+
* - **GET ?format=skill** → skill.md
|
|
67
68
|
* - **POST** → AI-powered chat with RAG
|
|
68
69
|
*
|
|
69
70
|
* @example
|
|
@@ -82,7 +83,7 @@ interface DocsMCPAPIOptions {
|
|
|
82
83
|
*/
|
|
83
84
|
declare function createDocsAPI(options?: DocsAPIOptions): {
|
|
84
85
|
/**
|
|
85
|
-
* GET handler — search, llms.txt,
|
|
86
|
+
* GET handler — search, markdown, llms.txt, llms-full.txt, or skill.md.
|
|
86
87
|
*/
|
|
87
88
|
GET(request: Request): Promise<Response>;
|
|
88
89
|
/**
|
package/dist/docs-api.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { createDocsMcpHttpHandler, createFilesystemDocsMcpSource, resolveDocsMcp
|
|
|
13
13
|
* A single route handler that serves **both** search and AI chat:
|
|
14
14
|
*
|
|
15
15
|
* - `GET /api/docs?query=…` → full-text search over indexed MDX pages
|
|
16
|
+
* - `GET /api/docs?format=skill` → skill.md for agent discovery
|
|
16
17
|
* - `POST /api/docs` → RAG-powered "Ask AI" (searches relevant docs,
|
|
17
18
|
* then streams an LLM response using the docs as context)
|
|
18
19
|
*
|
|
@@ -43,6 +44,8 @@ const DEFAULT_LLMS_TXT_ROUTE = "/llms.txt";
|
|
|
43
44
|
const DEFAULT_LLMS_FULL_TXT_ROUTE = "/llms-full.txt";
|
|
44
45
|
const DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms.txt";
|
|
45
46
|
const DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms-full.txt";
|
|
47
|
+
const DEFAULT_SKILL_MD_ROUTE = "/skill.md";
|
|
48
|
+
const DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE = "/.well-known/skill.md";
|
|
46
49
|
const DEFAULT_AGENT_FEEDBACK_ROUTE = "/api/docs/agent/feedback";
|
|
47
50
|
const DEFAULT_AGENT_FEEDBACK_PAYLOAD_SCHEMA = {
|
|
48
51
|
type: "object",
|
|
@@ -159,6 +162,11 @@ function resolveAgentSpecRequest(url) {
|
|
|
159
162
|
const pathname = normalizeUrlPath(url.pathname);
|
|
160
163
|
return pathname === DEFAULT_AGENT_SPEC_ROUTE || pathname === DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE || pathname === DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE;
|
|
161
164
|
}
|
|
165
|
+
function resolveSkillRequest(url) {
|
|
166
|
+
const pathname = normalizeUrlPath(url.pathname);
|
|
167
|
+
if (pathname === DEFAULT_SKILL_MD_ROUTE || pathname === DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE) return true;
|
|
168
|
+
return url.searchParams.get("format")?.trim() === "skill";
|
|
169
|
+
}
|
|
162
170
|
function isSearchEnabled(search) {
|
|
163
171
|
if (search === false) return false;
|
|
164
172
|
if (search && typeof search === "object" && search.enabled === false) return false;
|
|
@@ -237,6 +245,11 @@ function buildAgentSpec({ origin, entry, i18n, search, mcp, feedback, llms }) {
|
|
|
237
245
|
},
|
|
238
246
|
skills: {
|
|
239
247
|
enabled: true,
|
|
248
|
+
file: "skill.md",
|
|
249
|
+
route: DEFAULT_SKILL_MD_ROUTE,
|
|
250
|
+
wellKnown: DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE,
|
|
251
|
+
api: `${DEFAULT_DOCS_API_ROUTE}?format=skill`,
|
|
252
|
+
generatedFallback: true,
|
|
240
253
|
registry: "skills.sh",
|
|
241
254
|
install: "npx skills add farming-labs/docs",
|
|
242
255
|
recommended: [{
|
|
@@ -820,6 +833,53 @@ function renderMarkdownDocument(page) {
|
|
|
820
833
|
lines.push("", page.agentFallbackRawContent ?? page.rawContent ?? page.content);
|
|
821
834
|
return lines.join("\n");
|
|
822
835
|
}
|
|
836
|
+
function renderSkillDocument({ origin, entry, search, mcp, feedback, llms }) {
|
|
837
|
+
const normalizedEntry = normalizePathSegment(entry) || "docs";
|
|
838
|
+
const siteTitle = compactSkillText(llms.siteTitle ?? "Documentation");
|
|
839
|
+
const siteDescription = llms.siteDescription ? compactSkillText(llms.siteDescription) : void 0;
|
|
840
|
+
const searchEnabled = isSearchEnabled(search);
|
|
841
|
+
const lines = [
|
|
842
|
+
"---",
|
|
843
|
+
"name: docs",
|
|
844
|
+
`description: ${toYamlString(truncateSkillDescription(`Use ${siteTitle} through markdown routes, llms.txt, agent discovery, search, and MCP when available.`))}`,
|
|
845
|
+
"---",
|
|
846
|
+
"",
|
|
847
|
+
`# ${siteTitle} Skill`,
|
|
848
|
+
"",
|
|
849
|
+
`Base URL: ${origin}`
|
|
850
|
+
];
|
|
851
|
+
if (siteDescription) lines.push(`Description: ${siteDescription}`);
|
|
852
|
+
lines.push("", "## When To Use", "Use this skill when you need to read or implement against this documentation site.", "", "## Start Here", `- Fetch ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}; fall back to ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE} or ${DEFAULT_AGENT_SPEC_ROUTE}.`, `- Fetch /${normalizedEntry}.md for the root docs page.`, `- Fetch /${normalizedEntry}/{slug}.md for page-specific context.`, "- You can also request text/markdown from normal page URLs.");
|
|
853
|
+
if (searchEnabled) lines.push(`- Search with ${DEFAULT_DOCS_API_ROUTE}?query={query} when you do not know the page.`);
|
|
854
|
+
if (llms.enabled) lines.push(`- Use ${DEFAULT_LLMS_TXT_ROUTE} for a compact docs index.`, `- Use ${DEFAULT_LLMS_FULL_TXT_ROUTE} for full markdown context.`);
|
|
855
|
+
if (mcp.enabled) lines.push(`- Use ${DEFAULT_MCP_WELL_KNOWN_ROUTE} or ${DEFAULT_MCP_PUBLIC_ROUTE} for MCP tools when your environment supports MCP.`);
|
|
856
|
+
if (feedback.enabled) lines.push(`- Read ${feedback.schemaRoute} before posting agent feedback to ${feedback.route}.`);
|
|
857
|
+
lines.push("", "## Routes", `- Skill document: ${DEFAULT_SKILL_MD_ROUTE}`, `- Skill well-known alias: ${DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE}`, `- Skill API format: ${DEFAULT_DOCS_API_ROUTE}?format=skill`, `- Agent discovery: ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}`, `- Agent discovery fallback: ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE}`, `- Markdown root: /${normalizedEntry}.md`, `- Markdown pages: /${normalizedEntry}/{slug}.md`);
|
|
858
|
+
if (llms.enabled) lines.push(`- llms.txt: ${DEFAULT_LLMS_TXT_ROUTE}`, `- llms-full.txt: ${DEFAULT_LLMS_FULL_TXT_ROUTE}`, `- llms well-known aliases: ${DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE}, ${DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE}`);
|
|
859
|
+
if (mcp.enabled) lines.push(`- MCP: ${DEFAULT_MCP_PUBLIC_ROUTE}, ${DEFAULT_MCP_WELL_KNOWN_ROUTE}`);
|
|
860
|
+
lines.push("", "## Reusable Framework Skills", "For framework setup, CLI, page actions, Ask AI, or configuration work, install the reusable Farming Labs skills:", "", "```sh", "npx skills add farming-labs/docs", "```");
|
|
861
|
+
return lines.join("\n");
|
|
862
|
+
}
|
|
863
|
+
function readRootSkillDocument(rootDir) {
|
|
864
|
+
const candidate = path.join(rootDir, "skill.md");
|
|
865
|
+
try {
|
|
866
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) return fs.readFileSync(candidate, "utf-8");
|
|
867
|
+
} catch {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
function compactSkillText(value) {
|
|
873
|
+
return value.replace(/\s+/g, " ").trim();
|
|
874
|
+
}
|
|
875
|
+
function truncateSkillDescription(value) {
|
|
876
|
+
const normalized = compactSkillText(value);
|
|
877
|
+
if (normalized.length <= 1024) return normalized;
|
|
878
|
+
return `${normalized.slice(0, 1021).trimEnd()}...`;
|
|
879
|
+
}
|
|
880
|
+
function toYamlString(value) {
|
|
881
|
+
return JSON.stringify(value);
|
|
882
|
+
}
|
|
823
883
|
const DEFAULT_SYSTEM_PROMPT = `You are a helpful documentation assistant. Answer questions based on the provided documentation context. Be concise and accurate. If the answer is not in the context, say so honestly. Use markdown formatting for code examples and links.`;
|
|
824
884
|
function resolveModelAndProvider(aiConfig, requestedModelId) {
|
|
825
885
|
const raw = aiConfig.model;
|
|
@@ -946,6 +1006,7 @@ function generateLlmsTxt(indexes, options) {
|
|
|
946
1006
|
* - **GET ?query=…** → full-text search
|
|
947
1007
|
* - **GET ?format=llms** → llms.txt (concise page listing)
|
|
948
1008
|
* - **GET ?format=llms-full** → llms-full.txt (full page content)
|
|
1009
|
+
* - **GET ?format=skill** → skill.md
|
|
949
1010
|
* - **POST** → AI-powered chat with RAG
|
|
950
1011
|
*
|
|
951
1012
|
* @example
|
|
@@ -1117,6 +1178,19 @@ function createDocsAPI(options) {
|
|
|
1117
1178
|
"X-Robots-Tag": "noindex"
|
|
1118
1179
|
} });
|
|
1119
1180
|
}
|
|
1181
|
+
if (resolveSkillRequest(url)) return new Response(readRootSkillDocument(root) ?? renderSkillDocument({
|
|
1182
|
+
origin: url.origin,
|
|
1183
|
+
entry,
|
|
1184
|
+
i18n,
|
|
1185
|
+
search: searchConfig,
|
|
1186
|
+
mcp: mcpConfig,
|
|
1187
|
+
feedback: agentFeedbackConfig,
|
|
1188
|
+
llms: llmsConfig
|
|
1189
|
+
}), { headers: {
|
|
1190
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
1191
|
+
"Cache-Control": "public, max-age=0, s-maxage=3600",
|
|
1192
|
+
"X-Robots-Tag": "noindex"
|
|
1193
|
+
} });
|
|
1120
1194
|
const markdownRequest = resolveMarkdownRequest(entry, url, request);
|
|
1121
1195
|
if (markdownRequest) {
|
|
1122
1196
|
const document = await getMarkdownDocument(ctx, markdownRequest.requestedPath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/theme",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docs",
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
"tsdown": "^0.20.3",
|
|
134
134
|
"typescript": "^5.9.3",
|
|
135
135
|
"vitest": "^3.2.4",
|
|
136
|
-
"@farming-labs/docs": "0.1.
|
|
136
|
+
"@farming-labs/docs": "0.1.39"
|
|
137
137
|
},
|
|
138
138
|
"peerDependencies": {
|
|
139
139
|
"@farming-labs/docs": ">=0.0.1",
|