@farming-labs/docs 0.2.102 → 0.2.104

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.
Files changed (58) hide show
  1. package/dist/{agent-lWTq6pgr.d.mts → agent-C9IdC__R.d.mts} +9 -3
  2. package/dist/{agent-BLHMoVId.mjs → agent-NerpHcQB.mjs} +3 -3
  3. package/dist/{agent-CFqECAG0.mjs → agent-PSrgiJD8.mjs} +165 -8
  4. package/dist/{agent-evals-GpcaCbRb.mjs → agent-evals-U9d_db2u.mjs} +2 -2
  5. package/dist/{agent-export-oiWxB5aX.mjs → agent-export-DlBUKkTt.mjs} +20 -9
  6. package/dist/agent-feedback-loop-CGrc-gDW.mjs +196 -0
  7. package/dist/agent-skills-bundle.d.mts +1 -1
  8. package/dist/{agent-skills-server-CICMh0zv.d.mts → agent-skills-server-CIoL58X7.d.mts} +2 -2
  9. package/dist/agent-skills-vite.d.mts +3 -3
  10. package/dist/{agents-CtqIUW5I.mjs → agents-D3SLnQet.mjs} +8 -7
  11. package/dist/{analytics-C75BnTEW.d.mts → analytics-B-jz7re8.d.mts} +1 -1
  12. package/dist/authoring-BsWXMH43.mjs +41 -0
  13. package/dist/authoring-mcp-BCM5CJbJ.mjs +380 -0
  14. package/dist/browser.d.mts +3 -3
  15. package/dist/cli/index.mjs +67 -35
  16. package/dist/client/react.d.mts +1 -1
  17. package/dist/{cloud-FNfcbfli.mjs → cloud-BLxt_HYO.mjs} +3 -3
  18. package/dist/{cloud-ask-ai-BmbZVAT5.d.mts → cloud-ask-ai-19G2HJ62.d.mts} +1 -1
  19. package/dist/{code-blocks-Bel6vDqI.mjs → code-blocks-c3nIrWOM.mjs} +1 -1
  20. package/dist/{codeblocks-jbWuvtJQ.mjs → codeblocks-qHdKGVaE.mjs} +2 -2
  21. package/dist/{dev-BvbYmTLz.mjs → dev-B0iHJjTz.mjs} +3 -3
  22. package/dist/docs-cloud-server.d.mts +2 -2
  23. package/dist/docs-cloud-server.mjs +1 -1
  24. package/dist/{doctor-Bmm0uY98.mjs → doctor-x3k6aE2I.mjs} +16 -12
  25. package/dist/{downgrade-Dqm0rJMy.mjs → downgrade-Dd3vvVdD.mjs} +2 -2
  26. package/dist/feedback-DdWc33oO.mjs +72 -0
  27. package/dist/{golden-evaluations-K45fcWGW.mjs → golden-evaluations-7k7Akp3R.mjs} +2 -2
  28. package/dist/index.d.mts +70 -8
  29. package/dist/index.mjs +5 -5
  30. package/dist/{init-jDQSo-Or.mjs → init-DKukQdOQ.mjs} +3 -3
  31. package/dist/{mcp-CwgKlbIz.mjs → mcp-B6ZZdp6F.mjs} +7 -6
  32. package/dist/mcp.d.mts +18 -5
  33. package/dist/mcp.mjs +457 -38
  34. package/dist/{content-change-hydration-BsbBXAUi.mjs → openapi-mcp-B7u7Y0gB.mjs} +116 -2
  35. package/dist/{package-version--oQiHwZj.mjs → package-version-P4TT_9IO.mjs} +1 -1
  36. package/dist/{prompt-references-DW5vG5zs.mjs → prompt-references-Ca2Rcrpw.mjs} +2 -2
  37. package/dist/{retrieval-digest-DYEsIeCE.d.mts → retrieval-digest-DT9Ktc4C.d.mts} +2 -2
  38. package/dist/{review-C_5wB0rR.mjs → review-DgaNTtP0.mjs} +8 -8
  39. package/dist/{robots-NQesPX5L.mjs → robots-DNod0XKB.mjs} +1 -1
  40. package/dist/{robots-BQ42Y84a.mjs → robots-msD1xoH5.mjs} +4 -4
  41. package/dist/{search-B5etKU-v.mjs → search-CNzq9S5S.mjs} +7 -6
  42. package/dist/server.d.mts +92 -7
  43. package/dist/server.mjs +9 -7
  44. package/dist/{sidebar-CwZlgyAz.d.mts → sidebar-DmXRpAAK.d.mts} +1 -1
  45. package/dist/{sitemap-DjhxvQP1.mjs → sitemap-CHvips7o.mjs} +8 -7
  46. package/dist/{sitemap-server-B0SiREN0.mjs → sitemap-server-C2TauhKa.mjs} +11 -1
  47. package/dist/{skills-BFang8vF.mjs → skills-aaE5L1go.mjs} +8 -7
  48. package/dist/{standards-discovery-D1Ys4J3h.d.mts → standards-discovery-Cqq8QmlH.d.mts} +1 -1
  49. package/dist/{types-DLLefoU5.d.mts → types-BthCiAD9.d.mts} +114 -1
  50. package/dist/{upgrade-kj2crJSk.mjs → upgrade-B0OA_7jE.mjs} +2 -2
  51. package/package.json +1 -1
  52. /package/dist/{agent-skills-progressive-disclosure-Da9LKEKv.mjs → agent-skills-progressive-disclosure-CfIsq4Ew.mjs} +0 -0
  53. /package/dist/{audience-exposure-Cw3SA4WE.d.mts → audience-exposure-BW13cI2O.d.mts} +0 -0
  54. /package/dist/{cloud-ask-ai-CkCYjf2N.mjs → cloud-ask-ai-DeojA5lB.mjs} +0 -0
  55. /package/dist/{config-BaTmjDqD.mjs → config-BhF6J2N4.mjs} +0 -0
  56. /package/dist/{errors-DZiNXv5c.mjs → errors-B5KTe1Xo.mjs} +0 -0
  57. /package/dist/{templates-kc7jxspR.mjs → templates-m_j4ARnu.mjs} +0 -0
  58. /package/dist/{utils-DOMeZW9V.mjs → utils-DcCUwxqS.mjs} +0 -0
@@ -1,15 +1,16 @@
1
- import { a as DEFAULT_AGENT_MD_ROUTE, lt as resolveDocsAgentFeedbackConfig, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, o as DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE, rt as renderDocsAgentsDocument, t as DEFAULT_AGENTS_MD_ROUTE } from "./agent-CFqECAG0.mjs";
1
+ import { a as DEFAULT_AGENT_MD_ROUTE, lt as resolveDocsAgentFeedbackConfig, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, o as DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE, rt as renderDocsAgentsDocument, t as DEFAULT_AGENTS_MD_ROUTE } from "./agent-PSrgiJD8.mjs";
2
2
  import "./markdown-sections-7OoA7ylx.mjs";
3
3
  import "./standards-discovery-Ckx0tN7B.mjs";
4
- import "./content-change-hydration-BsbBXAUi.mjs";
5
- import { C as resolveApiReferenceOpenApiDiscovery } from "./sitemap-server-B0SiREN0.mjs";
4
+ import "./openapi-mcp-B7u7Y0gB.mjs";
5
+ import { C as resolveApiReferenceOpenApiDiscovery } from "./sitemap-server-C2TauhKa.mjs";
6
6
  import "./agent-skills-server-DFqQyO_V.mjs";
7
- import "./agent-evals-GpcaCbRb.mjs";
7
+ import "./agent-evals-U9d_db2u.mjs";
8
8
  import { resolveDocsMcpConfig } from "./mcp.mjs";
9
- import "./code-blocks-Bel6vDqI.mjs";
9
+ import "./authoring-mcp-BCM5CJbJ.mjs";
10
+ import "./code-blocks-c3nIrWOM.mjs";
10
11
  import "./server.mjs";
11
- import { a as loadDocsConfigModule, d as readNavTitle, g as resolveDocsConfigPath, h as readTopLevelStringProperty, l as readBooleanProperty, p as readStringProperty, t as extractNestedObjectLiteral } from "./config-BaTmjDqD.mjs";
12
- import { t as detectFramework } from "./utils-DOMeZW9V.mjs";
12
+ import { a as loadDocsConfigModule, d as readNavTitle, g as resolveDocsConfigPath, h as readTopLevelStringProperty, l as readBooleanProperty, p as readStringProperty, t as extractNestedObjectLiteral } from "./config-BhF6J2N4.mjs";
13
+ import { t as detectFramework } from "./utils-DcCUwxqS.mjs";
13
14
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
14
15
  import path from "node:path";
15
16
  import pc from "picocolors";
@@ -1,4 +1,4 @@
1
- import { dn as DocsObservabilityEvent, fn as DocsObservabilityEventInput, ft as DocsAnalyticsConfig, lt as DocsAgentTraceEventInput, mt as DocsAnalyticsEventInput, pt as DocsAnalyticsEvent, un as DocsObservabilityConfig } from "./types-DLLefoU5.mjs";
1
+ import { dn as DocsObservabilityEvent, fn as DocsObservabilityEventInput, ft as DocsAnalyticsConfig, lt as DocsAgentTraceEventInput, mt as DocsAnalyticsEventInput, pt as DocsAnalyticsEvent, un as DocsObservabilityConfig } from "./types-BthCiAD9.mjs";
2
2
 
3
3
  //#region src/analytics.d.ts
4
4
  declare const DOCS_AGENT_TRACE_EVENT_TYPES: readonly ["run.start", "run.end", "run.error", "user.input", "prompt.build", "retrieval.query", "retrieval.result", "retrieval.error", "model.call", "model.response", "model.stream", "model.error", "tool.call", "tool.result", "tool.error", "retry", "timeout", "error", "agent.final"];
@@ -0,0 +1,41 @@
1
+ import "./agent-PSrgiJD8.mjs";
2
+ import "./markdown-sections-7OoA7ylx.mjs";
3
+ import "./standards-discovery-Ckx0tN7B.mjs";
4
+ import "./openapi-mcp-B7u7Y0gB.mjs";
5
+ import { r as runDocsAuthoringMcpStdio } from "./authoring-mcp-BCM5CJbJ.mjs";
6
+ import { _ as resolveDocsContentDir, d as readNavTitle, g as resolveDocsConfigPath, o as loadDocsConfigModuleResult, p as readStringProperty } from "./config-BhF6J2N4.mjs";
7
+ import { readFileSync } from "node:fs";
8
+ import path from "node:path";
9
+
10
+ //#region src/cli/authoring.ts
11
+ async function runAuthoringMcp(options = {}) {
12
+ const rootDir = process.cwd();
13
+ const content = readFileSync(resolveDocsConfigPath(rootDir, options.configPath), "utf8");
14
+ const configLoad = await loadDocsConfigModuleResult(rootDir, options.configPath);
15
+ const config = configLoad.status === "evaluated" ? configLoad.config : void 0;
16
+ const entry = config?.entry ?? readStringProperty(content, "entry") ?? "docs";
17
+ const contentDir = config?.contentDir ?? resolveDocsContentDir(rootDir, content, entry);
18
+ const navTitle = typeof config?.nav?.title === "string" ? config.nav.title : readNavTitle(content);
19
+ const cliPath = process.argv[1];
20
+ if (!cliPath) throw new Error("Could not resolve the docs CLI executable for doctor checks.");
21
+ await runDocsAuthoringMcpStdio({
22
+ rootDir,
23
+ entry,
24
+ contentDir,
25
+ name: `${navTitle ?? "Documentation"} authoring`,
26
+ branchPrefix: options.branchPrefix,
27
+ baseBranch: options.baseBranch,
28
+ allowPublish: options.allowPublish,
29
+ doctorCommand: [
30
+ process.execPath,
31
+ path.resolve(cliPath),
32
+ "doctor",
33
+ "--agent",
34
+ "--json",
35
+ ...options.configPath ? ["--config", options.configPath] : []
36
+ ]
37
+ });
38
+ }
39
+
40
+ //#endregion
41
+ export { runAuthoringMcp };
@@ -0,0 +1,380 @@
1
+ import { at as renderDocsMarkdownDocument } from "./agent-PSrgiJD8.mjs";
2
+ import { createFilesystemDocsMcpSource } from "./mcp.mjs";
3
+ import { n as analyzeDocsAgentFeedback, r as readDocsAgentFeedbackFile } from "./agent-feedback-loop-CGrc-gDW.mjs";
4
+ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { createHash, randomUUID } from "node:crypto";
7
+ import { serveStdio } from "@modelcontextprotocol/server/stdio";
8
+ import { McpServer } from "@modelcontextprotocol/server";
9
+ import { execFile } from "node:child_process";
10
+ import { promisify } from "node:util";
11
+ import { z } from "zod";
12
+
13
+ //#region src/authoring-mcp.ts
14
+ const execFileAsync = promisify(execFile);
15
+ const AUTHORING_MAX_COMMAND_OUTPUT_BYTES = 1e6;
16
+ const DEFAULT_MAX_AUTHORING_FILE_BYTES = 1e6;
17
+ const DOCS_AUTHORING_EXTENSIONS = new Set([
18
+ ".md",
19
+ ".mdx",
20
+ ".svx"
21
+ ]);
22
+ function structuredResult(value) {
23
+ return {
24
+ content: [{
25
+ type: "text",
26
+ text: JSON.stringify(value, null, 2)
27
+ }],
28
+ structuredContent: value
29
+ };
30
+ }
31
+ function isContained(parent, candidate) {
32
+ const relative = path.relative(parent, candidate);
33
+ return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
34
+ }
35
+ function projectPath(rootDir, absolutePath) {
36
+ return path.relative(rootDir, absolutePath).replace(/\\/g, "/");
37
+ }
38
+ function resolveContentRoot(options) {
39
+ return path.resolve(options.rootDir, options.contentDir ?? options.entry ?? "docs");
40
+ }
41
+ function assertPathHasNoSymlinks(basePath, candidate) {
42
+ const relative = path.relative(basePath, candidate);
43
+ let current = basePath;
44
+ for (const segment of relative.split(path.sep).filter(Boolean)) {
45
+ current = path.join(current, segment);
46
+ if (existsSync(current) && lstatSync(current).isSymbolicLink()) throw new Error(`Authoring paths cannot traverse symbolic links: ${current}`);
47
+ }
48
+ }
49
+ function resolveDocsFile(options, value) {
50
+ if (!value.trim() || path.isAbsolute(value) || value.includes("\0")) throw new Error("Authoring paths must be non-empty project-relative paths.");
51
+ const rootDir = path.resolve(options.rootDir);
52
+ const contentRoot = resolveContentRoot(options);
53
+ const candidate = path.resolve(rootDir, value);
54
+ if (!isContained(contentRoot, candidate)) throw new Error(`Authoring is restricted to ${projectPath(rootDir, contentRoot)}/.`);
55
+ if (existsSync(contentRoot) && lstatSync(contentRoot).isSymbolicLink()) throw new Error("The authoring content directory cannot be a symbolic link.");
56
+ assertPathHasNoSymlinks(contentRoot, candidate);
57
+ if (!DOCS_AUTHORING_EXTENSIONS.has(path.extname(candidate).toLowerCase())) throw new Error("Authoring only supports .md, .mdx, and .svx documentation files.");
58
+ return candidate;
59
+ }
60
+ function resolveProjectInputFile(rootDir, value) {
61
+ if (!value.trim() || path.isAbsolute(value) || value.includes("\0")) throw new Error("Input paths must be non-empty project-relative paths.");
62
+ const resolvedRoot = path.resolve(rootDir);
63
+ const candidate = path.resolve(resolvedRoot, value);
64
+ if (!isContained(resolvedRoot, candidate)) throw new Error("Input files must stay inside the project root.");
65
+ assertPathHasNoSymlinks(resolvedRoot, candidate);
66
+ if (existsSync(candidate) && !isContained(realpathSync(resolvedRoot), realpathSync(candidate))) throw new Error("Input files must stay inside the project root.");
67
+ return candidate;
68
+ }
69
+ function sha256(value) {
70
+ return createHash("sha256").update(value, "utf8").digest("hex");
71
+ }
72
+ function readDocsAuthoringFile(options, filePath) {
73
+ const absolutePath = resolveDocsFile(options, filePath);
74
+ if (!existsSync(absolutePath) || !statSync(absolutePath).isFile()) throw new Error(`Documentation file does not exist: ${filePath}`);
75
+ const content = readFileSync(absolutePath, "utf8");
76
+ return {
77
+ path: projectPath(path.resolve(options.rootDir), absolutePath),
78
+ sha256: sha256(content),
79
+ bytes: Buffer.byteLength(content, "utf8"),
80
+ content
81
+ };
82
+ }
83
+ function writeDocsAuthoringFile(options, input) {
84
+ const absolutePath = resolveDocsFile(options, input.path);
85
+ const maxFileBytes = Math.max(1, options.maxFileBytes ?? DEFAULT_MAX_AUTHORING_FILE_BYTES);
86
+ if (Buffer.byteLength(input.content, "utf8") > maxFileBytes) throw new Error(`Authoring write exceeds the ${maxFileBytes}-byte file limit.`);
87
+ if (existsSync(absolutePath)) {
88
+ const current = readFileSync(absolutePath, "utf8");
89
+ if (!input.expectedSha256) throw new Error("expectedSha256 is required when overwriting an existing documentation file.");
90
+ if (sha256(current) !== input.expectedSha256) throw new Error("Documentation changed since it was read; read it again before overwriting.");
91
+ } else if (input.expectedSha256) throw new Error("expectedSha256 was provided, but the documentation file does not exist.");
92
+ mkdirSync(path.dirname(absolutePath), { recursive: true });
93
+ const temporaryPath = path.join(path.dirname(absolutePath), `.${path.basename(absolutePath)}.${randomUUID()}.tmp`);
94
+ try {
95
+ writeFileSync(temporaryPath, input.content, {
96
+ encoding: "utf8",
97
+ flag: "wx"
98
+ });
99
+ renameSync(temporaryPath, absolutePath);
100
+ } catch (error) {
101
+ if (existsSync(temporaryPath)) unlinkSync(temporaryPath);
102
+ throw error;
103
+ }
104
+ return readDocsAuthoringFile(options, input.path);
105
+ }
106
+ async function runCommand(rootDir, command, timeout = 12e4) {
107
+ try {
108
+ const result = await execFileAsync(command[0], command.slice(1), {
109
+ cwd: rootDir,
110
+ encoding: "utf8",
111
+ maxBuffer: AUTHORING_MAX_COMMAND_OUTPUT_BYTES,
112
+ timeout
113
+ });
114
+ return {
115
+ ok: true,
116
+ command: [...command],
117
+ stdout: result.stdout,
118
+ stderr: result.stderr,
119
+ exitCode: 0
120
+ };
121
+ } catch (error) {
122
+ const failure = error;
123
+ return {
124
+ ok: false,
125
+ command: [...command],
126
+ stdout: failure.stdout ?? "",
127
+ stderr: failure.stderr ?? failure.message,
128
+ exitCode: typeof failure.code === "number" ? failure.code : 1
129
+ };
130
+ }
131
+ }
132
+ async function runGit(rootDir, args) {
133
+ return runCommand(rootDir, ["git", ...args]);
134
+ }
135
+ async function requireGitSuccess(rootDir, args) {
136
+ const result = await runGit(rootDir, args);
137
+ if (!result.ok) throw new Error(result.stderr.trim() || `git ${args.join(" ")} failed.`);
138
+ return result.stdout.trim();
139
+ }
140
+ async function listChangedFiles(rootDir) {
141
+ const [unstaged, staged, untracked] = await Promise.all([
142
+ requireGitSuccess(rootDir, [
143
+ "diff",
144
+ "--name-only",
145
+ "--no-ext-diff"
146
+ ]),
147
+ requireGitSuccess(rootDir, [
148
+ "diff",
149
+ "--cached",
150
+ "--name-only",
151
+ "--no-ext-diff"
152
+ ]),
153
+ requireGitSuccess(rootDir, [
154
+ "ls-files",
155
+ "--others",
156
+ "--exclude-standard"
157
+ ])
158
+ ]);
159
+ return [...new Set([
160
+ unstaged,
161
+ staged,
162
+ untracked
163
+ ].flatMap((value) => value.split(/\r?\n/)).filter(Boolean))].map((value) => value.replace(/\\/g, "/")).sort();
164
+ }
165
+ function validateAuthoringBranch(branch, prefix) {
166
+ if (!branch.startsWith(prefix) || branch === prefix || /\s|\.\.|~|\^|:|\?|\*|\[|\\/.test(branch)) throw new Error(`Authoring branches must start with ${prefix} and use a valid Git ref name.`);
167
+ }
168
+ async function previewDocsFile(options, filePath) {
169
+ const state = readDocsAuthoringFile(options, filePath);
170
+ const source = createFilesystemDocsMcpSource({
171
+ rootDir: options.rootDir,
172
+ entry: options.entry ?? "docs",
173
+ contentDir: options.contentDir ?? options.entry ?? "docs"
174
+ });
175
+ const page = (await Promise.resolve(source.getPages())).find((candidate) => candidate.sourcePath === state.path);
176
+ if (!page) throw new Error(`The docs source did not resolve a public page for ${state.path}.`);
177
+ const document = renderDocsMarkdownDocument(page);
178
+ return {
179
+ file: state.path,
180
+ url: page.url,
181
+ title: page.title,
182
+ sha256: state.sha256,
183
+ markdown: document.slice(0, 1e5),
184
+ truncated: document.length > 1e5
185
+ };
186
+ }
187
+ async function publishDraftPullRequest(options, input) {
188
+ const rootDir = path.resolve(options.rootDir);
189
+ const contentRootRelative = projectPath(rootDir, resolveContentRoot(options));
190
+ const branch = await requireGitSuccess(rootDir, ["branch", "--show-current"]);
191
+ validateAuthoringBranch(branch, options.branchPrefix?.trim() || "docs/");
192
+ const changedFiles = await listChangedFiles(rootDir);
193
+ if (changedFiles.length === 0) throw new Error("No documentation changes are available to publish.");
194
+ const outsideContent = changedFiles.filter((file) => file !== contentRootRelative && !file.startsWith(`${contentRootRelative}/`));
195
+ if (outsideContent.length > 0) throw new Error(`Draft PR publishing refuses changes outside ${contentRootRelative}/: ${outsideContent.join(", ")}`);
196
+ if (!(await runCommand(rootDir, [
197
+ "gh",
198
+ "auth",
199
+ "status"
200
+ ], 3e4)).ok) throw new Error("GitHub CLI authentication is required before publishing.");
201
+ await requireGitSuccess(rootDir, [
202
+ "add",
203
+ "--",
204
+ contentRootRelative
205
+ ]);
206
+ await requireGitSuccess(rootDir, [
207
+ "commit",
208
+ "-m",
209
+ input.commitMessage
210
+ ]);
211
+ await requireGitSuccess(rootDir, [
212
+ "push",
213
+ "-u",
214
+ "origin",
215
+ branch
216
+ ]);
217
+ const created = await runCommand(rootDir, [
218
+ "gh",
219
+ "pr",
220
+ "create",
221
+ "--draft",
222
+ ...options.baseBranch ? ["--base", options.baseBranch] : [],
223
+ "--head",
224
+ branch,
225
+ "--title",
226
+ input.title,
227
+ "--body",
228
+ input.body
229
+ ], 6e4);
230
+ if (!created.ok) throw new Error(created.stderr.trim() || "Could not create the draft PR.");
231
+ return {
232
+ branch,
233
+ changedFiles,
234
+ url: created.stdout.trim().split(/\r?\n/).at(-1) ?? "",
235
+ draft: true
236
+ };
237
+ }
238
+ /**
239
+ * Create a protected authoring-only MCP server.
240
+ *
241
+ * This server is intentionally separate from createDocsMcpServer and is never wired into public
242
+ * framework adapters. Use stdio locally or place the returned server behind an authenticated,
243
+ * authoring-scoped transport.
244
+ */
245
+ async function createDocsAuthoringMcpServer(options) {
246
+ const rootDir = path.resolve(options.rootDir);
247
+ const contentRoot = resolveContentRoot(options);
248
+ if (!isContained(rootDir, contentRoot)) throw new Error("The authoring content directory must stay inside the project root.");
249
+ if (existsSync(contentRoot) && lstatSync(contentRoot).isSymbolicLink()) throw new Error("The authoring content directory cannot be a symbolic link.");
250
+ const branchPrefix = options.branchPrefix?.trim() || "docs/";
251
+ const server = new McpServer({
252
+ name: options.name ?? "@farming-labs/docs-authoring",
253
+ version: options.version ?? "0.0.0"
254
+ }, { capabilities: { logging: {} } });
255
+ server.registerTool("authoring_status", {
256
+ title: "Inspect documentation authoring status",
257
+ description: "Return the current branch and changed files before an authoring operation.",
258
+ annotations: { readOnlyHint: true }
259
+ }, async () => {
260
+ return structuredResult({
261
+ branch: await requireGitSuccess(rootDir, ["branch", "--show-current"]),
262
+ branchPrefix,
263
+ contentDirectory: projectPath(rootDir, contentRoot),
264
+ changedFiles: await listChangedFiles(rootDir),
265
+ publishEnabled: options.allowPublish === true
266
+ });
267
+ });
268
+ server.registerTool("authoring_create_branch", {
269
+ title: "Create a documentation authoring branch",
270
+ description: "Create a constrained branch after verifying the complete worktree is clean.",
271
+ inputSchema: z.object({ branch: z.string().trim().min(1).max(200) }),
272
+ annotations: {
273
+ readOnlyHint: false,
274
+ idempotentHint: false
275
+ }
276
+ }, async ({ branch }) => {
277
+ validateAuthoringBranch(branch, branchPrefix);
278
+ if ((await listChangedFiles(rootDir)).length > 0) throw new Error("Create the authoring branch before editing; the worktree is not clean.");
279
+ await requireGitSuccess(rootDir, [
280
+ "switch",
281
+ "-c",
282
+ branch
283
+ ]);
284
+ return structuredResult({
285
+ branch,
286
+ created: true
287
+ });
288
+ });
289
+ server.registerTool("authoring_read_doc", {
290
+ title: "Read a documentation source file",
291
+ description: "Read a docs-tree file and return the SHA-256 required for safe overwrites.",
292
+ inputSchema: z.object({ path: z.string().trim().min(1).max(4096) }),
293
+ annotations: { readOnlyHint: true }
294
+ }, async ({ path: filePath }) => structuredResult(readDocsAuthoringFile(options, filePath)));
295
+ server.registerTool("authoring_write_doc", {
296
+ title: "Write a documentation source file",
297
+ description: "Atomically write inside the docs tree. Existing files require the SHA-256 returned by authoring_read_doc.",
298
+ inputSchema: z.object({
299
+ path: z.string().trim().min(1).max(4096),
300
+ content: z.string(),
301
+ expectedSha256: z.string().regex(/^[a-f0-9]{64}$/).optional()
302
+ }),
303
+ annotations: {
304
+ readOnlyHint: false,
305
+ idempotentHint: true
306
+ }
307
+ }, async (input) => structuredResult(writeDocsAuthoringFile(options, input)));
308
+ server.registerTool("authoring_preview_doc", {
309
+ title: "Preview machine-readable documentation",
310
+ description: "Render the edited page through the same Markdown projection used by agents.",
311
+ inputSchema: z.object({ path: z.string().trim().min(1).max(4096) }),
312
+ annotations: { readOnlyHint: true }
313
+ }, async ({ path: filePath }) => structuredResult(await previewDocsFile(options, filePath)));
314
+ server.registerTool("authoring_run_doctor", {
315
+ title: "Run docs doctor",
316
+ description: "Run the operator-configured docs doctor command without a shell.",
317
+ annotations: { readOnlyHint: true }
318
+ }, async () => {
319
+ if (!options.doctorCommand) throw new Error("No doctorCommand was configured for this authoring server.");
320
+ return structuredResult(await runCommand(rootDir, options.doctorCommand));
321
+ });
322
+ server.registerTool("authoring_diff", {
323
+ title: "Review documentation diff",
324
+ description: "Return the docs-tree Git diff and any untracked documentation paths.",
325
+ annotations: { readOnlyHint: true }
326
+ }, async () => {
327
+ const diff = await runGit(rootDir, [
328
+ "diff",
329
+ "--no-ext-diff",
330
+ "--unified=3",
331
+ "HEAD",
332
+ "--",
333
+ projectPath(rootDir, contentRoot)
334
+ ]);
335
+ if (!diff.ok) throw new Error(diff.stderr || "Could not render the documentation diff.");
336
+ return structuredResult({
337
+ changedFiles: await listChangedFiles(rootDir),
338
+ diff: diff.stdout.slice(0, AUTHORING_MAX_COMMAND_OUTPUT_BYTES),
339
+ truncated: diff.stdout.length > AUTHORING_MAX_COMMAND_OUTPUT_BYTES
340
+ });
341
+ });
342
+ server.registerTool("authoring_analyze_feedback", {
343
+ title: "Turn recurring agent feedback into improvement drafts",
344
+ description: "Cluster a project-local JSON or JSONL feedback export and draft golden tasks, issues, and a docs PR description.",
345
+ inputSchema: z.object({
346
+ path: z.string().trim().min(1).max(4096),
347
+ minOccurrences: z.number().int().min(2).max(100).optional()
348
+ }),
349
+ annotations: { readOnlyHint: true }
350
+ }, async ({ path: filePath, minOccurrences }) => {
351
+ return structuredResult(analyzeDocsAgentFeedback(readDocsAgentFeedbackFile(resolveProjectInputFile(rootDir, filePath)), { minOccurrences }));
352
+ });
353
+ if (options.allowPublish === true) server.registerTool("authoring_publish_draft_pr", {
354
+ title: "Commit, push, and open a draft documentation PR",
355
+ description: "Publish docs-only changes after an explicit confirmation. Rejects changes outside the configured content directory.",
356
+ inputSchema: z.object({
357
+ confirm: z.literal(true),
358
+ title: z.string().trim().min(1).max(256),
359
+ body: z.string().max(65536),
360
+ commitMessage: z.string().trim().min(1).max(256)
361
+ }),
362
+ annotations: {
363
+ readOnlyHint: false,
364
+ destructiveHint: true,
365
+ idempotentHint: false,
366
+ openWorldHint: true
367
+ }
368
+ }, async ({ title, body, commitMessage }) => structuredResult(await publishDraftPullRequest(options, {
369
+ title,
370
+ body,
371
+ commitMessage
372
+ })));
373
+ return server;
374
+ }
375
+ async function runDocsAuthoringMcpStdio(options) {
376
+ await serveStdio(() => createDocsAuthoringMcpServer(options));
377
+ }
378
+
379
+ //#endregion
380
+ export { writeDocsAuthoringFile as i, readDocsAuthoringFile as n, runDocsAuthoringMcpStdio as r, createDocsAuthoringMcpServer as t };
@@ -1,4 +1,4 @@
1
- import { c as emitDocsAnalyticsEvent, d as resolveDocsAnalyticsConfig } from "./analytics-C75BnTEW.mjs";
2
- import { t as applySidebarFolderIndexBehavior } from "./sidebar-CwZlgyAz.mjs";
3
- import { n as resolveDocsAudienceExposure, t as DocsContentAudience } from "./audience-exposure-Cw3SA4WE.mjs";
1
+ import { c as emitDocsAnalyticsEvent, d as resolveDocsAnalyticsConfig } from "./analytics-B-jz7re8.mjs";
2
+ import { t as applySidebarFolderIndexBehavior } from "./sidebar-DmXRpAAK.mjs";
3
+ import { n as resolveDocsAudienceExposure, t as DocsContentAudience } from "./audience-exposure-BW13cI2O.mjs";
4
4
  export { type DocsContentAudience, applySidebarFolderIndexBehavior, emitDocsAnalyticsEvent, resolveDocsAnalyticsConfig, resolveDocsAudienceExposure };