@farming-labs/docs 0.1.2 → 0.1.3
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/cli/index.mjs +52 -4
- package/dist/config-CSywk3ou.mjs +95 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +4 -1
- package/dist/{init-N0bZQFRd.mjs → init-C7kgy5hD.mjs} +1 -1
- package/dist/mcp-aXyV1jPp.mjs +46 -0
- package/dist/mcp.d.mts +2 -1
- package/dist/mcp.mjs +35 -33
- package/dist/search-BS6C5N1i.mjs +671 -0
- package/dist/search-ChhShKMO.mjs +99 -0
- package/dist/search-KzREATdM.d.mts +21 -0
- package/dist/server.d.mts +3 -2
- package/dist/server.mjs +3 -2
- package/dist/{types-dqnMXLdw.d.mts → types-BAulrjlV.d.mts} +149 -1
- package/dist/{upgrade-BbEyR_JB.mjs → upgrade-J_kkv-ti.mjs} +1 -1
- package/package.json +1 -1
- package/dist/mcp-8rCBy2-U.mjs +0 -93
- /package/dist/{api-reference-wh4_pwG8.mjs → api-reference-DlfH-Y9c.mjs} +0 -0
- /package/dist/{utils-D5Wn7Q5E.mjs → utils-CRhME2g-.mjs} +0 -0
package/dist/cli/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import pc from "picocolors";
|
|
|
4
4
|
//#region src/cli/index.ts
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
6
|
const command = args[0];
|
|
7
|
+
const subcommand = args[1];
|
|
7
8
|
const UPGRADE_TAGS = ["latest", "beta"];
|
|
8
9
|
/** Normalize command aliases like `upgrade@beta` into the base command + dist-tag. */
|
|
9
10
|
function parseCommandAlias(rawCommand) {
|
|
@@ -18,7 +19,11 @@ function parseCommandAlias(rawCommand) {
|
|
|
18
19
|
/** Parse flags like --template next, --name my-docs, --theme concrete, --entry docs, --framework astro (exported for tests). */
|
|
19
20
|
function parseFlags(argv) {
|
|
20
21
|
const flags = {};
|
|
21
|
-
const booleanFlags = new Set([
|
|
22
|
+
const booleanFlags = new Set([
|
|
23
|
+
"api-reference",
|
|
24
|
+
"typesense",
|
|
25
|
+
"algolia"
|
|
26
|
+
]);
|
|
22
27
|
for (let i = 0; i < argv.length; i++) {
|
|
23
28
|
const arg = argv[i];
|
|
24
29
|
if (arg.startsWith("--") && arg.includes("=")) {
|
|
@@ -47,14 +52,38 @@ async function main() {
|
|
|
47
52
|
apiRouteRoot: typeof flags["api-route-root"] === "string" ? flags["api-route-root"] : void 0
|
|
48
53
|
};
|
|
49
54
|
const mcpOptions = { configPath: typeof flags.config === "string" ? flags.config : void 0 };
|
|
55
|
+
const searchSyncOptions = {
|
|
56
|
+
configPath: typeof flags.config === "string" ? flags.config : void 0,
|
|
57
|
+
provider: typeof flags.provider === "string" ? flags.provider : void 0,
|
|
58
|
+
typesense: typeof flags.typesense === "boolean" ? flags.typesense : void 0,
|
|
59
|
+
algolia: typeof flags.algolia === "boolean" ? flags.algolia : void 0,
|
|
60
|
+
baseUrl: typeof flags["base-url"] === "string" ? flags["base-url"] : void 0,
|
|
61
|
+
collection: typeof flags.collection === "string" ? flags.collection : void 0,
|
|
62
|
+
apiKey: typeof flags["api-key"] === "string" ? flags["api-key"] : void 0,
|
|
63
|
+
adminApiKey: typeof flags["admin-api-key"] === "string" ? flags["admin-api-key"] : void 0,
|
|
64
|
+
mode: typeof flags.mode === "string" ? flags.mode : void 0,
|
|
65
|
+
ollamaModel: typeof flags["ollama-model"] === "string" ? flags["ollama-model"] : void 0,
|
|
66
|
+
ollamaBaseUrl: typeof flags["ollama-base-url"] === "string" ? flags["ollama-base-url"] : void 0,
|
|
67
|
+
appId: typeof flags["app-id"] === "string" ? flags["app-id"] : void 0,
|
|
68
|
+
indexName: typeof flags["index-name"] === "string" ? flags["index-name"] : void 0,
|
|
69
|
+
searchApiKey: typeof flags["search-api-key"] === "string" ? flags["search-api-key"] : void 0
|
|
70
|
+
};
|
|
50
71
|
if (!parsedCommand.command || parsedCommand.command === "init") {
|
|
51
|
-
const { init } = await import("../init-
|
|
72
|
+
const { init } = await import("../init-C7kgy5hD.mjs");
|
|
52
73
|
await init(initOptions);
|
|
53
74
|
} else if (parsedCommand.command === "mcp") {
|
|
54
|
-
const { runMcp } = await import("../mcp-
|
|
75
|
+
const { runMcp } = await import("../mcp-aXyV1jPp.mjs");
|
|
55
76
|
await runMcp(mcpOptions);
|
|
77
|
+
} else if (parsedCommand.command === "search" && subcommand === "sync") {
|
|
78
|
+
const { syncSearch } = await import("../search-ChhShKMO.mjs");
|
|
79
|
+
await syncSearch(searchSyncOptions);
|
|
80
|
+
} else if (parsedCommand.command === "search") {
|
|
81
|
+
console.error(pc.red(`Unknown search subcommand: ${subcommand ?? "(missing)"}`));
|
|
82
|
+
console.error();
|
|
83
|
+
printHelp();
|
|
84
|
+
process.exit(1);
|
|
56
85
|
} else if (parsedCommand.command === "upgrade") {
|
|
57
|
-
const { upgrade } = await import("../upgrade-
|
|
86
|
+
const { upgrade } = await import("../upgrade-J_kkv-ti.mjs");
|
|
58
87
|
await upgrade({
|
|
59
88
|
framework: (typeof flags.framework === "string" ? flags.framework : void 0) ?? (args[1] && !args[1].startsWith("--") ? args[1] : void 0),
|
|
60
89
|
tag: args.includes("--beta") ? "beta" : args.includes("--latest") ? "latest" : parsedCommand.tag ?? "latest"
|
|
@@ -78,6 +107,7 @@ ${pc.dim("Usage:")}
|
|
|
78
107
|
${pc.dim("Commands:")}
|
|
79
108
|
${pc.cyan("init")} Scaffold docs in your project (default)
|
|
80
109
|
${pc.cyan("mcp")} Run the built-in docs MCP server over stdio
|
|
110
|
+
${pc.cyan("search")} Search utilities (${pc.dim("sync")} for external indexes)
|
|
81
111
|
${pc.cyan("upgrade")} Upgrade @farming-labs/* packages to latest (auto-detect or use --framework)
|
|
82
112
|
|
|
83
113
|
${pc.dim("Supported frameworks:")}
|
|
@@ -95,6 +125,24 @@ ${pc.dim("Options for init:")}
|
|
|
95
125
|
${pc.dim("Options for mcp:")}
|
|
96
126
|
${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
|
|
97
127
|
|
|
128
|
+
${pc.dim("Options for search sync:")}
|
|
129
|
+
${pc.cyan("search sync --typesense")} Sync docs content to Typesense using env/flags
|
|
130
|
+
${pc.cyan("search sync --algolia")} Sync docs content to Algolia using env/flags
|
|
131
|
+
${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
|
|
132
|
+
${pc.cyan("--provider <name>")} Explicit provider (${pc.dim("typesense")}, ${pc.dim("algolia")})
|
|
133
|
+
${pc.cyan("--typesense")} Shortcut for ${pc.cyan("--provider typesense")}
|
|
134
|
+
${pc.cyan("--algolia")} Shortcut for ${pc.cyan("--provider algolia")}
|
|
135
|
+
${pc.cyan("--base-url <url>")} Typesense base URL (or use ${pc.dim("TYPESENSE_URL")})
|
|
136
|
+
${pc.cyan("--collection <name>")} Typesense collection name (default ${pc.dim("docs")})
|
|
137
|
+
${pc.cyan("--api-key <key>")} Typesense search/api key (or use ${pc.dim("TYPESENSE_API_KEY")})
|
|
138
|
+
${pc.cyan("--admin-api-key <key>")} Admin-capable sync key for Typesense/Algolia
|
|
139
|
+
${pc.cyan("--mode <keyword|hybrid>")} Typesense mode (default ${pc.dim("keyword")})
|
|
140
|
+
${pc.cyan("--ollama-model <name>")} Embeddings model for Typesense hybrid sync
|
|
141
|
+
${pc.cyan("--ollama-base-url <url>")} Ollama base URL for hybrid embeddings
|
|
142
|
+
${pc.cyan("--app-id <id>")} Algolia app id (or use ${pc.dim("ALGOLIA_APP_ID")})
|
|
143
|
+
${pc.cyan("--index-name <name>")} Algolia index name (default ${pc.dim("docs")})
|
|
144
|
+
${pc.cyan("--search-api-key <key>")} Algolia search key (or use ${pc.dim("ALGOLIA_SEARCH_API_KEY")})
|
|
145
|
+
|
|
98
146
|
${pc.dim("Options for upgrade:")}
|
|
99
147
|
${pc.cyan("--framework <name>")} Explicit framework (${pc.dim("next")}, ${pc.dim("tanstack-start")}, ${pc.dim("nuxt")}, ${pc.dim("sveltekit")}, ${pc.dim("astro")}); omit to auto-detect
|
|
100
148
|
${pc.cyan("--latest")} Install latest stable (default)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
//#region src/cli/config.ts
|
|
5
|
+
const FILE_EXTS = [
|
|
6
|
+
"tsx",
|
|
7
|
+
"ts",
|
|
8
|
+
"jsx",
|
|
9
|
+
"js"
|
|
10
|
+
];
|
|
11
|
+
function escapeRegExp(value) {
|
|
12
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
+
}
|
|
14
|
+
function createPropertyPattern(key, valuePattern) {
|
|
15
|
+
return new RegExp(`\\b${escapeRegExp(key)}\\b\\s*:\\s*${valuePattern}`);
|
|
16
|
+
}
|
|
17
|
+
function resolveDocsConfigPath(rootDir, explicitPath) {
|
|
18
|
+
if (explicitPath) {
|
|
19
|
+
const resolvedPath = resolve(rootDir, explicitPath);
|
|
20
|
+
if (!existsSync(resolvedPath)) throw new Error(`Could not find docs config at ${explicitPath}.`);
|
|
21
|
+
return resolvedPath;
|
|
22
|
+
}
|
|
23
|
+
for (const ext of FILE_EXTS) {
|
|
24
|
+
const configPath = join(rootDir, `docs.config.${ext}`);
|
|
25
|
+
if (existsSync(configPath)) return configPath;
|
|
26
|
+
}
|
|
27
|
+
throw new Error("Could not find docs.config.ts or docs.config.tsx in the current project. Use --config to point at a custom path.");
|
|
28
|
+
}
|
|
29
|
+
function readStringProperty(content, key) {
|
|
30
|
+
return content.match(createPropertyPattern(key, `["']([^"']+)["']`))?.[1];
|
|
31
|
+
}
|
|
32
|
+
function readBooleanProperty(content, key) {
|
|
33
|
+
const match = content.match(createPropertyPattern(key, "(true|false)"));
|
|
34
|
+
return match ? match[1] === "true" : void 0;
|
|
35
|
+
}
|
|
36
|
+
function extractObjectLiteral(content, key) {
|
|
37
|
+
const keyIndex = content.search(new RegExp(`${key}\\s*:\\s*\\{`));
|
|
38
|
+
if (keyIndex === -1) return void 0;
|
|
39
|
+
const braceStart = content.indexOf("{", keyIndex);
|
|
40
|
+
if (braceStart === -1) return void 0;
|
|
41
|
+
let depth = 0;
|
|
42
|
+
for (let index = braceStart; index < content.length; index += 1) {
|
|
43
|
+
const char = content[index];
|
|
44
|
+
if (char === "{") {
|
|
45
|
+
depth += 1;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (char !== "}") continue;
|
|
49
|
+
depth -= 1;
|
|
50
|
+
if (depth === 0) return content.slice(braceStart + 1, index);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function readNavTitle(content) {
|
|
54
|
+
const block = extractObjectLiteral(content, "nav");
|
|
55
|
+
if (!block) return void 0;
|
|
56
|
+
return readStringProperty(block, "title");
|
|
57
|
+
}
|
|
58
|
+
function resolveDocsContentDir(rootDir, content, entry) {
|
|
59
|
+
const configuredContentDir = readStringProperty(content, "contentDir");
|
|
60
|
+
if (configuredContentDir) return configuredContentDir;
|
|
61
|
+
const candidates = [
|
|
62
|
+
entry,
|
|
63
|
+
join("app", entry),
|
|
64
|
+
join("src", "app", entry)
|
|
65
|
+
];
|
|
66
|
+
for (const candidate of candidates) if (existsSync(join(rootDir, candidate))) return candidate;
|
|
67
|
+
return entry;
|
|
68
|
+
}
|
|
69
|
+
function parseEnvValue(rawValue) {
|
|
70
|
+
const value = rawValue.trim();
|
|
71
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
function loadProjectEnv(rootDir) {
|
|
75
|
+
const env = {};
|
|
76
|
+
for (const filename of [".env", ".env.local"]) {
|
|
77
|
+
const fullPath = join(rootDir, filename);
|
|
78
|
+
if (!existsSync(fullPath)) continue;
|
|
79
|
+
const lines = readFileSync(fullPath, "utf-8").split(/\r?\n/);
|
|
80
|
+
for (const line of lines) {
|
|
81
|
+
const trimmed = line.trim();
|
|
82
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
83
|
+
const equalsIndex = trimmed.indexOf("=");
|
|
84
|
+
if (equalsIndex === -1) continue;
|
|
85
|
+
const key = trimmed.slice(0, equalsIndex).trim();
|
|
86
|
+
const rawValue = trimmed.slice(equalsIndex + 1);
|
|
87
|
+
if (!key) continue;
|
|
88
|
+
env[key] = parseEnvValue(rawValue);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return env;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { readStringProperty as a, readNavTitle as i, loadProjectEnv as n, resolveDocsConfigPath as o, readBooleanProperty as r, resolveDocsContentDir as s, extractObjectLiteral as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { $ as UIConfig, A as GithubConfig, B as PageFrontmatter, C as DocsSearchQuery, D as DocsTheme, E as DocsSearchSourcePage, F as OpenDocsConfig, G as SidebarFolderNode, H as PageTwitter, I as OpenDocsProvider, J as SidebarTree, K as SidebarNode, L as OpenGraphImage, M as LlmsTxtConfig, N as McpDocsSearchConfig, O as FeedbackConfig, P as OGConfig, Q as TypographyConfig, R as OrderingItem, S as DocsSearchEmbeddingsConfig, T as DocsSearchResultType, U as SidebarComponentProps, V as PageOpenGraph, W as SidebarConfig, X as ThemeToggleConfig, Y as SimpleDocsSearchConfig, Z as TypesenseDocsSearchConfig, _ as DocsSearchAdapterContext, a as CodeBlockCopyData, b as DocsSearchConfig, c as DocsConfig, d as DocsI18nConfig, f as DocsMcpConfig, g as DocsSearchAdapter, h as DocsNav, i as BreadcrumbConfig, j as LastUpdatedConfig, k as FontStyle, l as DocsFeedbackData, m as DocsMetadata, n as AlgoliaDocsSearchConfig, o as CopyMarkdownConfig, p as DocsMcpToolsConfig, q as SidebarPageNode, r as ApiReferenceConfig, s as CustomDocsSearchConfig, t as AIConfig, u as DocsFeedbackValue, v as DocsSearchAdapterFactory, w as DocsSearchResult, x as DocsSearchDocument, y as DocsSearchChunkingConfig, z as PageActionsConfig } from "./types-BAulrjlV.mjs";
|
|
2
|
+
import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-KzREATdM.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/define-docs.d.ts
|
|
4
5
|
/**
|
|
@@ -97,4 +98,4 @@ declare function buildPageOpenGraph(page: Pick<PageFrontmatter, "title" | "descr
|
|
|
97
98
|
*/
|
|
98
99
|
declare function buildPageTwitter(page: Pick<PageFrontmatter, "title" | "description" | "ogImage" | "openGraph" | "twitter">, ogConfig?: OGConfig, baseUrl?: string): PageTwitter | undefined;
|
|
99
100
|
//#endregion
|
|
100
|
-
export { type AIConfig, type ApiReferenceConfig, type BreadcrumbConfig, type CodeBlockCopyData, type CopyMarkdownConfig, type DocsConfig, type DocsFeedbackData, type DocsFeedbackValue, type DocsI18nConfig, type DocsMcpConfig, type DocsMcpToolsConfig, type DocsMetadata, type DocsNav, type DocsPathMatch, type DocsTheme, type FeedbackConfig, type FontStyle, type GithubConfig, type LastUpdatedConfig, type LlmsTxtConfig, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, type OpenGraphImage, type OrderingItem, type PageActionsConfig, type PageFrontmatter, type PageOpenGraph, type PageTwitter, type ResolvedDocsI18n, type SidebarComponentProps, type SidebarConfig, type SidebarFolderNode, type SidebarNode, type SidebarPageNode, type SidebarTree, type ThemeToggleConfig, type TypographyConfig, type UIConfig, buildPageOpenGraph, buildPageTwitter, createTheme, deepMerge, defineDocs, extendTheme, resolveDocsI18n, resolveDocsLocale, resolveDocsPath, resolveOGImage, resolveTitle };
|
|
101
|
+
export { type AIConfig, type AlgoliaDocsSearchConfig, type ApiReferenceConfig, type BreadcrumbConfig, type CodeBlockCopyData, type CopyMarkdownConfig, type CustomDocsSearchConfig, type DocsConfig, type DocsFeedbackData, type DocsFeedbackValue, type DocsI18nConfig, type DocsMcpConfig, type DocsMcpToolsConfig, type DocsMetadata, type DocsNav, type DocsPathMatch, type DocsSearchAdapter, type DocsSearchAdapterContext, type DocsSearchAdapterFactory, type DocsSearchChunkingConfig, type DocsSearchConfig, type DocsSearchDocument, type DocsSearchEmbeddingsConfig, type DocsSearchQuery, type DocsSearchResult, type DocsSearchResultType, type DocsSearchSourcePage, type DocsTheme, type FeedbackConfig, type FontStyle, type GithubConfig, type LastUpdatedConfig, type LlmsTxtConfig, type McpDocsSearchConfig, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, type OpenGraphImage, type OrderingItem, type PageActionsConfig, type PageFrontmatter, type PageOpenGraph, type PageTwitter, type ResolvedDocsI18n, type SidebarComponentProps, type SidebarConfig, type SidebarFolderNode, type SidebarNode, type SidebarPageNode, type SidebarTree, type SimpleDocsSearchConfig, type ThemeToggleConfig, type TypesenseDocsSearchConfig, type TypographyConfig, type UIConfig, buildDocsSearchDocuments, buildPageOpenGraph, buildPageTwitter, createAlgoliaSearchAdapter, createCustomSearchAdapter, createMcpSearchAdapter, createSimpleSearchAdapter, createTheme, createTypesenseSearchAdapter, deepMerge, defineDocs, extendTheme, performDocsSearch, resolveDocsI18n, resolveDocsLocale, resolveDocsPath, resolveOGImage, resolveSearchRequestConfig, resolveTitle };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-BS6C5N1i.mjs";
|
|
2
|
+
|
|
1
3
|
//#region src/define-docs.ts
|
|
2
4
|
/**
|
|
3
5
|
* Define docs configuration. Validates and returns the config.
|
|
@@ -16,6 +18,7 @@ function defineDocs(config) {
|
|
|
16
18
|
components: config.components,
|
|
17
19
|
onCopyClick: config.onCopyClick,
|
|
18
20
|
feedback: config.feedback,
|
|
21
|
+
search: config.search,
|
|
19
22
|
mcp: config.mcp,
|
|
20
23
|
icons: config.icons,
|
|
21
24
|
pageActions: config.pageActions,
|
|
@@ -224,4 +227,4 @@ function buildPageTwitter(page, ogConfig, baseUrl) {
|
|
|
224
227
|
}
|
|
225
228
|
|
|
226
229
|
//#endregion
|
|
227
|
-
export { buildPageOpenGraph, buildPageTwitter, createTheme, deepMerge, defineDocs, extendTheme, resolveDocsI18n, resolveDocsLocale, resolveDocsPath, resolveOGImage, resolveTitle };
|
|
230
|
+
export { buildDocsSearchDocuments, buildPageOpenGraph, buildPageTwitter, createAlgoliaSearchAdapter, createCustomSearchAdapter, createMcpSearchAdapter, createSimpleSearchAdapter, createTheme, createTypesenseSearchAdapter, deepMerge, defineDocs, extendTheme, performDocsSearch, resolveDocsI18n, resolveDocsLocale, resolveDocsPath, resolveOGImage, resolveSearchRequestConfig, resolveTitle };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as devInstallCommand, c as installCommand, d as writeFileSafe, i as detectPackageManagerFromLockfile, l as readFileSafe, n as detectGlobalCssFiles, o as exec, r as detectNextAppDir, s as fileExists, t as detectFramework, u as spawnAndWaitFor } from "./utils-
|
|
1
|
+
import { a as devInstallCommand, c as installCommand, d as writeFileSafe, i as detectPackageManagerFromLockfile, l as readFileSafe, n as detectGlobalCssFiles, o as exec, r as detectNextAppDir, s as fileExists, t as detectFramework, u as spawnAndWaitFor } from "./utils-CRhME2g-.mjs";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import pc from "picocolors";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import "./api-reference-DlfH-Y9c.mjs";
|
|
2
|
+
import { createFilesystemDocsMcpSource, resolveDocsMcpConfig, runDocsMcpStdio } from "./mcp.mjs";
|
|
3
|
+
import "./server.mjs";
|
|
4
|
+
import { a as readStringProperty, i as readNavTitle, o as resolveDocsConfigPath, r as readBooleanProperty, s as resolveDocsContentDir, t as extractObjectLiteral } from "./config-CSywk3ou.mjs";
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
|
|
7
|
+
//#region src/cli/mcp.ts
|
|
8
|
+
async function runMcp(options = {}) {
|
|
9
|
+
const rootDir = process.cwd();
|
|
10
|
+
const content = readFileSync(resolveDocsConfigPath(rootDir, options.configPath), "utf-8");
|
|
11
|
+
const entry = readStringProperty(content, "entry") ?? "docs";
|
|
12
|
+
const contentDir = resolveDocsContentDir(rootDir, content, entry);
|
|
13
|
+
const navTitle = readNavTitle(content);
|
|
14
|
+
const mcp = readMcpConfig(content);
|
|
15
|
+
await runDocsMcpStdio({
|
|
16
|
+
source: createFilesystemDocsMcpSource({
|
|
17
|
+
rootDir,
|
|
18
|
+
entry,
|
|
19
|
+
contentDir,
|
|
20
|
+
siteTitle: navTitle ?? "Documentation"
|
|
21
|
+
}),
|
|
22
|
+
mcp: resolveDocsMcpConfig(mcp ?? true, { defaultName: navTitle ?? "@farming-labs/docs" }),
|
|
23
|
+
defaultName: navTitle ?? "@farming-labs/docs"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function readMcpConfig(content) {
|
|
27
|
+
if (content.match(/mcp\s*:\s*false/)) return false;
|
|
28
|
+
if (content.match(/mcp\s*:\s*true/)) return true;
|
|
29
|
+
const block = extractObjectLiteral(content, "mcp");
|
|
30
|
+
if (!block) return void 0;
|
|
31
|
+
return {
|
|
32
|
+
enabled: readBooleanProperty(block, "enabled"),
|
|
33
|
+
route: readStringProperty(block, "route"),
|
|
34
|
+
name: readStringProperty(block, "name"),
|
|
35
|
+
version: readStringProperty(block, "version"),
|
|
36
|
+
tools: {
|
|
37
|
+
listPages: readBooleanProperty(block, "listPages"),
|
|
38
|
+
readPage: readBooleanProperty(block, "readPage"),
|
|
39
|
+
searchDocs: readBooleanProperty(block, "searchDocs"),
|
|
40
|
+
getNavigation: readBooleanProperty(block, "getNavigation")
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { runMcp };
|
package/dist/mcp.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { R as OrderingItem, b as DocsSearchConfig, f as DocsMcpConfig } from "./types-BAulrjlV.mjs";
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
|
|
4
4
|
//#region src/mcp.d.ts
|
|
@@ -62,6 +62,7 @@ interface DocsMcpHttpHandlers {
|
|
|
62
62
|
interface CreateDocsMcpServerOptions {
|
|
63
63
|
source: DocsMcpSource;
|
|
64
64
|
mcp?: boolean | DocsMcpConfig;
|
|
65
|
+
search?: boolean | DocsSearchConfig;
|
|
65
66
|
defaultName?: string;
|
|
66
67
|
defaultVersion?: string;
|
|
67
68
|
}
|
package/dist/mcp.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { s as performDocsSearch } from "./search-BS6C5N1i.mjs";
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -88,6 +89,7 @@ async function createDocsMcpServer(options) {
|
|
|
88
89
|
defaultName: options.defaultName ?? options.source.siteTitle ?? DEFAULT_MCP_NAME,
|
|
89
90
|
defaultVersion: options.defaultVersion
|
|
90
91
|
});
|
|
92
|
+
const toolSearchConfig = resolveMcpToolSearchConfig(options.search, resolved.route);
|
|
91
93
|
const server = new McpServer({
|
|
92
94
|
name: resolved.name,
|
|
93
95
|
version: resolved.version
|
|
@@ -144,7 +146,14 @@ async function createDocsMcpServer(options) {
|
|
|
144
146
|
inputSchema: searchDocsInputSchema,
|
|
145
147
|
annotations: { readOnlyHint: true }
|
|
146
148
|
}, async ({ query, limit, locale }) => {
|
|
147
|
-
const results =
|
|
149
|
+
const results = await performDocsSearch({
|
|
150
|
+
pages: toSearchSourcePages(dedupePages(await options.source.getPages(locale))),
|
|
151
|
+
query,
|
|
152
|
+
search: toolSearchConfig ?? true,
|
|
153
|
+
locale,
|
|
154
|
+
siteTitle: options.source.siteTitle,
|
|
155
|
+
limit: limit ?? 10
|
|
156
|
+
});
|
|
148
157
|
return { content: [{
|
|
149
158
|
type: "text",
|
|
150
159
|
text: JSON.stringify({ results }, null, 2)
|
|
@@ -382,6 +391,31 @@ function dedupePages(pages) {
|
|
|
382
391
|
for (const page of pages) seen.set(page.url, page);
|
|
383
392
|
return [...seen.values()];
|
|
384
393
|
}
|
|
394
|
+
function toSearchSourcePages(pages) {
|
|
395
|
+
return pages.map((page) => ({
|
|
396
|
+
title: page.title,
|
|
397
|
+
url: page.url,
|
|
398
|
+
content: page.content,
|
|
399
|
+
rawContent: page.rawContent,
|
|
400
|
+
description: page.description
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
function isSelfMcpSearchEndpoint(search, route) {
|
|
404
|
+
if (!search || search === true || typeof search !== "object" || search.provider !== "mcp") return false;
|
|
405
|
+
const endpoint = search.endpoint.trim();
|
|
406
|
+
if (!endpoint.startsWith("/")) return false;
|
|
407
|
+
return normalizeDocsMcpRoute(endpoint) === normalizeDocsMcpRoute(route);
|
|
408
|
+
}
|
|
409
|
+
function resolveMcpToolSearchConfig(search, route) {
|
|
410
|
+
if (!isSelfMcpSearchEndpoint(search, route)) return search;
|
|
411
|
+
const config = search;
|
|
412
|
+
return {
|
|
413
|
+
provider: "simple",
|
|
414
|
+
enabled: config.enabled,
|
|
415
|
+
maxResults: config.maxResults,
|
|
416
|
+
chunking: config.chunking
|
|
417
|
+
};
|
|
418
|
+
}
|
|
385
419
|
function toPageSummaries(pages) {
|
|
386
420
|
return pages.map((page) => ({
|
|
387
421
|
slug: page.slug,
|
|
@@ -418,38 +452,6 @@ function normalizeUrlPath(value) {
|
|
|
418
452
|
if (normalized === "/") return normalized;
|
|
419
453
|
return normalized.replace(/\/+$/, "");
|
|
420
454
|
}
|
|
421
|
-
function searchDocsPages(pages, query, limit) {
|
|
422
|
-
const normalizedQuery = query.toLowerCase().trim();
|
|
423
|
-
if (!normalizedQuery) return [];
|
|
424
|
-
const words = normalizedQuery.split(/\s+/).filter(Boolean);
|
|
425
|
-
return pages.map((page) => {
|
|
426
|
-
const titleScore = page.title.toLowerCase().includes(normalizedQuery) ? 10 : 0;
|
|
427
|
-
const descriptionScore = page.description?.toLowerCase().includes(normalizedQuery) ? 4 : 0;
|
|
428
|
-
const contentScore = words.reduce((score, word) => {
|
|
429
|
-
return score + (page.content.toLowerCase().includes(word) ? 1 : 0);
|
|
430
|
-
}, 0);
|
|
431
|
-
return {
|
|
432
|
-
slug: page.slug,
|
|
433
|
-
url: page.url,
|
|
434
|
-
title: page.title,
|
|
435
|
-
description: page.description,
|
|
436
|
-
icon: page.icon,
|
|
437
|
-
excerpt: buildExcerpt(page, words),
|
|
438
|
-
score: titleScore + descriptionScore + contentScore
|
|
439
|
-
};
|
|
440
|
-
}).filter((page) => page.score > 0).sort((left, right) => right.score - left.score).slice(0, limit).map(({ score: _score, ...page }) => page);
|
|
441
|
-
}
|
|
442
|
-
function buildExcerpt(page, words) {
|
|
443
|
-
const haystack = page.rawContent ?? page.content;
|
|
444
|
-
const lower = haystack.toLowerCase();
|
|
445
|
-
const firstHit = words.find((word) => lower.includes(word.toLowerCase()));
|
|
446
|
-
if (!firstHit) return page.description;
|
|
447
|
-
const index = lower.indexOf(firstHit.toLowerCase());
|
|
448
|
-
const start = Math.max(0, index - 80);
|
|
449
|
-
const end = Math.min(haystack.length, index + 140);
|
|
450
|
-
const excerpt = haystack.slice(start, end).replace(/\s+/g, " ").trim();
|
|
451
|
-
return excerpt.length > 0 ? excerpt : page.description;
|
|
452
|
-
}
|
|
453
455
|
function renderPageDocument(page) {
|
|
454
456
|
const lines = [`# ${page.title}`, `URL: ${page.url}`];
|
|
455
457
|
if (page.description) lines.push(`Description: ${page.description}`);
|