@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.
- package/dist/{agent-lWTq6pgr.d.mts → agent-C9IdC__R.d.mts} +9 -3
- package/dist/{agent-BLHMoVId.mjs → agent-NerpHcQB.mjs} +3 -3
- package/dist/{agent-CFqECAG0.mjs → agent-PSrgiJD8.mjs} +165 -8
- package/dist/{agent-evals-GpcaCbRb.mjs → agent-evals-U9d_db2u.mjs} +2 -2
- package/dist/{agent-export-oiWxB5aX.mjs → agent-export-DlBUKkTt.mjs} +20 -9
- package/dist/agent-feedback-loop-CGrc-gDW.mjs +196 -0
- package/dist/agent-skills-bundle.d.mts +1 -1
- package/dist/{agent-skills-server-CICMh0zv.d.mts → agent-skills-server-CIoL58X7.d.mts} +2 -2
- package/dist/agent-skills-vite.d.mts +3 -3
- package/dist/{agents-CtqIUW5I.mjs → agents-D3SLnQet.mjs} +8 -7
- package/dist/{analytics-C75BnTEW.d.mts → analytics-B-jz7re8.d.mts} +1 -1
- package/dist/authoring-BsWXMH43.mjs +41 -0
- package/dist/authoring-mcp-BCM5CJbJ.mjs +380 -0
- package/dist/browser.d.mts +3 -3
- package/dist/cli/index.mjs +67 -35
- package/dist/client/react.d.mts +1 -1
- package/dist/{cloud-FNfcbfli.mjs → cloud-BLxt_HYO.mjs} +3 -3
- package/dist/{cloud-ask-ai-BmbZVAT5.d.mts → cloud-ask-ai-19G2HJ62.d.mts} +1 -1
- package/dist/{code-blocks-Bel6vDqI.mjs → code-blocks-c3nIrWOM.mjs} +1 -1
- package/dist/{codeblocks-jbWuvtJQ.mjs → codeblocks-qHdKGVaE.mjs} +2 -2
- package/dist/{dev-BvbYmTLz.mjs → dev-B0iHJjTz.mjs} +3 -3
- package/dist/docs-cloud-server.d.mts +2 -2
- package/dist/docs-cloud-server.mjs +1 -1
- package/dist/{doctor-Bmm0uY98.mjs → doctor-x3k6aE2I.mjs} +16 -12
- package/dist/{downgrade-Dqm0rJMy.mjs → downgrade-Dd3vvVdD.mjs} +2 -2
- package/dist/feedback-DdWc33oO.mjs +72 -0
- package/dist/{golden-evaluations-K45fcWGW.mjs → golden-evaluations-7k7Akp3R.mjs} +2 -2
- package/dist/index.d.mts +70 -8
- package/dist/index.mjs +5 -5
- package/dist/{init-jDQSo-Or.mjs → init-DKukQdOQ.mjs} +3 -3
- package/dist/{mcp-CwgKlbIz.mjs → mcp-B6ZZdp6F.mjs} +7 -6
- package/dist/mcp.d.mts +18 -5
- package/dist/mcp.mjs +457 -38
- package/dist/{content-change-hydration-BsbBXAUi.mjs → openapi-mcp-B7u7Y0gB.mjs} +116 -2
- package/dist/{package-version--oQiHwZj.mjs → package-version-P4TT_9IO.mjs} +1 -1
- package/dist/{prompt-references-DW5vG5zs.mjs → prompt-references-Ca2Rcrpw.mjs} +2 -2
- package/dist/{retrieval-digest-DYEsIeCE.d.mts → retrieval-digest-DT9Ktc4C.d.mts} +2 -2
- package/dist/{review-C_5wB0rR.mjs → review-DgaNTtP0.mjs} +8 -8
- package/dist/{robots-NQesPX5L.mjs → robots-DNod0XKB.mjs} +1 -1
- package/dist/{robots-BQ42Y84a.mjs → robots-msD1xoH5.mjs} +4 -4
- package/dist/{search-B5etKU-v.mjs → search-CNzq9S5S.mjs} +7 -6
- package/dist/server.d.mts +92 -7
- package/dist/server.mjs +9 -7
- package/dist/{sidebar-CwZlgyAz.d.mts → sidebar-DmXRpAAK.d.mts} +1 -1
- package/dist/{sitemap-DjhxvQP1.mjs → sitemap-CHvips7o.mjs} +8 -7
- package/dist/{sitemap-server-B0SiREN0.mjs → sitemap-server-C2TauhKa.mjs} +11 -1
- package/dist/{skills-BFang8vF.mjs → skills-aaE5L1go.mjs} +8 -7
- package/dist/{standards-discovery-D1Ys4J3h.d.mts → standards-discovery-Cqq8QmlH.d.mts} +1 -1
- package/dist/{types-DLLefoU5.d.mts → types-BthCiAD9.d.mts} +114 -1
- package/dist/{upgrade-kj2crJSk.mjs → upgrade-B0OA_7jE.mjs} +2 -2
- package/package.json +1 -1
- /package/dist/{agent-skills-progressive-disclosure-Da9LKEKv.mjs → agent-skills-progressive-disclosure-CfIsq4Ew.mjs} +0 -0
- /package/dist/{audience-exposure-Cw3SA4WE.d.mts → audience-exposure-BW13cI2O.d.mts} +0 -0
- /package/dist/{cloud-ask-ai-CkCYjf2N.mjs → cloud-ask-ai-DeojA5lB.mjs} +0 -0
- /package/dist/{config-BaTmjDqD.mjs → config-BhF6J2N4.mjs} +0 -0
- /package/dist/{errors-DZiNXv5c.mjs → errors-B5KTe1Xo.mjs} +0 -0
- /package/dist/{templates-kc7jxspR.mjs → templates-m_j4ARnu.mjs} +0 -0
- /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-
|
|
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 "./
|
|
5
|
-
import { C as resolveApiReferenceOpenApiDiscovery } from "./sitemap-server-
|
|
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-
|
|
7
|
+
import "./agent-evals-U9d_db2u.mjs";
|
|
8
8
|
import { resolveDocsMcpConfig } from "./mcp.mjs";
|
|
9
|
-
import "./
|
|
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-
|
|
12
|
-
import { t as detectFramework } from "./utils-
|
|
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-
|
|
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 };
|
package/dist/browser.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as emitDocsAnalyticsEvent, d as resolveDocsAnalyticsConfig } from "./analytics-
|
|
2
|
-
import { t as applySidebarFolderIndexBehavior } from "./sidebar-
|
|
3
|
-
import { n as resolveDocsAudienceExposure, t as DocsContentAudience } from "./audience-exposure-
|
|
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 };
|