@farming-labs/docs 0.2.62 → 0.2.63
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-CQTH7NFu.mjs +624 -0
- package/dist/agent-DKKptIgy.mjs +4365 -0
- package/dist/agent-evals-B7MIxuEX.mjs +2144 -0
- package/dist/agent-export-CBgWgPvH.mjs +910 -0
- package/dist/agent-scope-C_U--OZ7.mjs +283 -0
- package/dist/agent-skills-bundle.d.mts +13 -0
- package/dist/agent-skills-bundle.mjs +12 -0
- package/dist/agent-skills-server-CPja6Syt.d.mts +14 -0
- package/dist/agent-skills-server-DraIb6FV.mjs +415 -0
- package/dist/agent-skills-vite.d.mts +31 -0
- package/dist/agent-skills-vite.mjs +70 -0
- package/dist/agents-XWZBub6f.mjs +221 -0
- package/dist/analytics-Bx44lg6d.mjs +177 -0
- package/dist/cli/index.d.mts +15 -0
- package/dist/cli/index.mjs +452 -0
- package/dist/client/react.d.mts +45 -0
- package/dist/client/react.mjs +223 -0
- package/dist/cloud-analytics-CSyFE6SS.mjs +132 -0
- package/dist/cloud-ask-ai-sbpjOR2K.mjs +382 -0
- package/dist/cloud-ask-ai-zpwkdwnF.d.mts +23 -0
- package/dist/cloud-pdNC-tyj.mjs +1615 -0
- package/dist/code-blocks-DnNVNK2M.mjs +871 -0
- package/dist/codeblocks-CFuurVIH.mjs +250 -0
- package/dist/config-Wcdj-D0a.mjs +369 -0
- package/dist/dev-Cmy6DtdF.mjs +1333 -0
- package/dist/docs-cloud-server.d.mts +70 -0
- package/dist/docs-cloud-server.mjs +310 -0
- package/dist/doctor-CJqtzKZT.mjs +2036 -0
- package/dist/downgrade-w7e6Se0L.mjs +184 -0
- package/dist/errors-DbOhkE1h.mjs +20 -0
- package/dist/golden-evaluations-Dsvh0etV.mjs +1503 -0
- package/dist/i18n-CCaFUnAN.mjs +40 -0
- package/dist/index.d.mts +1150 -0
- package/dist/index.mjs +10 -0
- package/dist/init-CQY0Woe3.mjs +1264 -0
- package/dist/mcp-B9dcsivk.mjs +156 -0
- package/dist/mcp.d.mts +298 -0
- package/dist/mcp.mjs +4430 -0
- package/dist/metadata-DWExHQnx.mjs +237 -0
- package/dist/package-version-n5AFur8a.mjs +128 -0
- package/dist/reading-time-CYZ5VvKU.mjs +742 -0
- package/dist/review-Fndl5brR.mjs +673 -0
- package/dist/robots-BIpC4j4P.mjs +201 -0
- package/dist/robots-CUTahhoY.mjs +179 -0
- package/dist/search-B6V6qtiI.mjs +1826 -0
- package/dist/search-CaSyi6H6.d.mts +279 -0
- package/dist/search-DSjCeOk7.mjs +104 -0
- package/dist/server.d.mts +343 -0
- package/dist/server.mjs +14 -0
- package/dist/sitemap-Cykpe3Tz.mjs +249 -0
- package/dist/sitemap-server-C_6Wes83.mjs +1137 -0
- package/dist/standards-discovery-C4HUqMd2.d.mts +227 -0
- package/dist/standards-discovery-jkykaXq1.mjs +519 -0
- package/dist/templates-Bq_P7ctv.mjs +2465 -0
- package/dist/types-lMBIdZg0.d.mts +3315 -0
- package/dist/upgrade-oz-GChgt.mjs +56 -0
- package/dist/utils-DpiIioYb.mjs +225 -0
- package/package.json +1 -1
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import { Jt as normalizePageAgentFrontmatter, L as findDocsMarkdownPage, Zt as stripGeneratedPageAgentContractMarkdown, tt as renderDocsMarkdownDocument } from "./agent-DKKptIgy.mjs";
|
|
2
|
+
import { _ as GENERATED_AGENT_PROVENANCE_VERSION, b as parseGeneratedAgentDocument, v as hashGeneratedAgentContent, x as serializeGeneratedAgentDocument } from "./search-B6V6qtiI.mjs";
|
|
3
|
+
import { createFilesystemDocsMcpSource } from "./mcp.mjs";
|
|
4
|
+
import { _ as resolveDocsContentDir, a as loadDocsConfigModule, c as loadProjectEnv, d as readNavTitle, f as readNumberProperty, g as resolveDocsConfigPath, h as readTopLevelStringProperty, l as readBooleanProperty, p as readStringProperty, t as extractNestedObjectLiteral, u as readEnvReferenceProperty } from "./config-Wcdj-D0a.mjs";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import matter from "gray-matter";
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
10
|
+
import pc from "picocolors";
|
|
11
|
+
|
|
12
|
+
//#region \0rolldown/runtime.js
|
|
13
|
+
var __defProp = Object.defineProperty;
|
|
14
|
+
var __exportAll = (all, no_symbols) => {
|
|
15
|
+
let target = {};
|
|
16
|
+
for (var name in all) {
|
|
17
|
+
__defProp(target, name, {
|
|
18
|
+
get: all[name],
|
|
19
|
+
enumerable: true
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
if (!no_symbols) {
|
|
23
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
24
|
+
}
|
|
25
|
+
return target;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/cli/agent.ts
|
|
30
|
+
var agent_exports = /* @__PURE__ */ __exportAll({
|
|
31
|
+
compactAgentDocs: () => compactAgentDocs,
|
|
32
|
+
inspectAgentCompactionState: () => inspectAgentCompactionState,
|
|
33
|
+
parseAgentCompactArgs: () => parseAgentCompactArgs,
|
|
34
|
+
printAgentCompactHelp: () => printAgentCompactHelp,
|
|
35
|
+
readPageTokenBudget: () => readPageTokenBudget,
|
|
36
|
+
scanDocsPageTargets: () => scanDocsPageTargets
|
|
37
|
+
});
|
|
38
|
+
const DEFAULT_COMPRESSION_BASE_URL = "https://api.farming-labs.dev";
|
|
39
|
+
const DEFAULT_COMPRESSION_MODEL = "docs-cloud-compress-v1";
|
|
40
|
+
const DEFAULT_COMPRESSION_AGGRESSIVENESS = .3;
|
|
41
|
+
const DEFAULT_DOCS_CLOUD_API_KEY_ENV = "DOCS_CLOUD_API_KEY";
|
|
42
|
+
const INDEX_PAGE_BASENAMES = new Set([
|
|
43
|
+
"index",
|
|
44
|
+
"page",
|
|
45
|
+
"+page"
|
|
46
|
+
]);
|
|
47
|
+
function parseBooleanFlag(raw) {
|
|
48
|
+
if (raw === "true") return true;
|
|
49
|
+
if (raw === "false") return false;
|
|
50
|
+
throw new Error(`Invalid boolean value: ${raw}. Use true or false.`);
|
|
51
|
+
}
|
|
52
|
+
function parseIntegerFlag(raw, name) {
|
|
53
|
+
const parsed = Number.parseInt(raw, 10);
|
|
54
|
+
if (!Number.isFinite(parsed)) throw new Error(`Invalid ${name}: ${raw}.`);
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
function parseFloatFlag(raw, name) {
|
|
58
|
+
const parsed = Number.parseFloat(raw);
|
|
59
|
+
if (!Number.isFinite(parsed)) throw new Error(`Invalid ${name}: ${raw}.`);
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
function parseAgentCompactArgs(argv) {
|
|
63
|
+
const parsed = { pages: [] };
|
|
64
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
65
|
+
const arg = argv[index];
|
|
66
|
+
if (arg === "--help" || arg === "-h") {
|
|
67
|
+
parsed.help = true;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (arg === "--all") {
|
|
71
|
+
parsed.all = true;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (arg === "--dry-run") {
|
|
75
|
+
parsed.dryRun = true;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (arg === "--changed") {
|
|
79
|
+
parsed.changed = true;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (arg === "--stale") {
|
|
83
|
+
parsed.stale = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (arg === "--include-missing") {
|
|
87
|
+
parsed.includeMissing = true;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (arg === "--protect-json") {
|
|
91
|
+
const nextValue = argv[index + 1];
|
|
92
|
+
if (nextValue && !nextValue.startsWith("--")) {
|
|
93
|
+
parsed.protectJson = parseBooleanFlag(nextValue);
|
|
94
|
+
index += 1;
|
|
95
|
+
} else parsed.protectJson = true;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (!arg.startsWith("--")) {
|
|
99
|
+
parsed.pages.push(arg);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const [rawKey, inlineValue] = arg.slice(2).split("=", 2);
|
|
103
|
+
const key = rawKey.trim();
|
|
104
|
+
const hasInlineValue = inlineValue !== void 0;
|
|
105
|
+
const nextValue = !hasInlineValue ? argv[index + 1] : void 0;
|
|
106
|
+
const consumeNextValue = () => {
|
|
107
|
+
if (!nextValue || nextValue.startsWith("--")) throw new Error(`Missing value for --${key}.`);
|
|
108
|
+
index += 1;
|
|
109
|
+
return nextValue;
|
|
110
|
+
};
|
|
111
|
+
const value = hasInlineValue ? inlineValue : consumeNextValue();
|
|
112
|
+
switch (key) {
|
|
113
|
+
case "page":
|
|
114
|
+
parsed.pages.push(value);
|
|
115
|
+
break;
|
|
116
|
+
case "config":
|
|
117
|
+
parsed.configPath = value;
|
|
118
|
+
break;
|
|
119
|
+
case "api-key":
|
|
120
|
+
parsed.apiKey = value;
|
|
121
|
+
break;
|
|
122
|
+
case "base-url":
|
|
123
|
+
parsed.baseUrl = value;
|
|
124
|
+
break;
|
|
125
|
+
case "api-key-env":
|
|
126
|
+
parsed.apiKeyEnv = value;
|
|
127
|
+
break;
|
|
128
|
+
case "model":
|
|
129
|
+
parsed.model = value;
|
|
130
|
+
break;
|
|
131
|
+
case "aggressiveness":
|
|
132
|
+
parsed.aggressiveness = parseFloatFlag(value, "aggressiveness");
|
|
133
|
+
break;
|
|
134
|
+
case "max-output-tokens":
|
|
135
|
+
parsed.maxOutputTokens = parseIntegerFlag(value, "max-output-tokens");
|
|
136
|
+
break;
|
|
137
|
+
case "min-output-tokens":
|
|
138
|
+
parsed.minOutputTokens = parseIntegerFlag(value, "min-output-tokens");
|
|
139
|
+
break;
|
|
140
|
+
case "protect-json":
|
|
141
|
+
parsed.protectJson = parseBooleanFlag(value);
|
|
142
|
+
break;
|
|
143
|
+
default: throw new Error(`Unknown agent compact flag: --${key}.`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return parsed;
|
|
147
|
+
}
|
|
148
|
+
function normalizePathSegment(value) {
|
|
149
|
+
return value.replace(/^\/+|\/+$/g, "");
|
|
150
|
+
}
|
|
151
|
+
function normalizeUrlPath(value) {
|
|
152
|
+
const normalized = value.replace(/\/+/g, "/");
|
|
153
|
+
if (normalized === "/") return normalized;
|
|
154
|
+
return normalized.replace(/\/+$/, "");
|
|
155
|
+
}
|
|
156
|
+
function normalizeRequestedPage(entry, rawValue) {
|
|
157
|
+
const trimmed = rawValue.trim();
|
|
158
|
+
if (!trimmed || trimmed === ".") return `/${normalizePathSegment(entry) || "docs"}`;
|
|
159
|
+
if (/^https?:\/\//i.test(trimmed)) try {
|
|
160
|
+
return normalizeUrlPath(new URL(trimmed).pathname.replace(/\.md$/i, ""));
|
|
161
|
+
} catch {
|
|
162
|
+
return trimmed;
|
|
163
|
+
}
|
|
164
|
+
const withoutMarkdownSuffix = trimmed.replace(/\.md$/i, "");
|
|
165
|
+
const normalizedEntry = `/${normalizePathSegment(entry) || "docs"}`;
|
|
166
|
+
const normalized = normalizeUrlPath(withoutMarkdownSuffix.startsWith("/") ? withoutMarkdownSuffix : `/${withoutMarkdownSuffix}`);
|
|
167
|
+
if (normalized === normalizedEntry || normalized.startsWith(`${normalizedEntry}/`)) return normalized;
|
|
168
|
+
const slug = normalizePathSegment(withoutMarkdownSuffix);
|
|
169
|
+
return slug ? normalizeUrlPath(`${normalizedEntry}/${slug}`) : normalizedEntry;
|
|
170
|
+
}
|
|
171
|
+
function scanDocsPageTargets(rootDir, contentDir, entry) {
|
|
172
|
+
const contentDirAbs = path.resolve(rootDir, contentDir);
|
|
173
|
+
const targets = [];
|
|
174
|
+
const visit = (dir, slugParts) => {
|
|
175
|
+
if (!existsSync(dir)) return;
|
|
176
|
+
for (const name of readdirSync(dir).sort()) {
|
|
177
|
+
const fullPath = path.join(dir, name);
|
|
178
|
+
if (statSync(fullPath).isDirectory()) {
|
|
179
|
+
visit(fullPath, [...slugParts, name]);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (name === "agent.md") continue;
|
|
183
|
+
if (!name.endsWith(".md") && !name.endsWith(".mdx") && !name.endsWith(".svx")) continue;
|
|
184
|
+
const baseName = name.replace(/\.(md|mdx|svx)$/i, "");
|
|
185
|
+
if (!INDEX_PAGE_BASENAMES.has(baseName)) continue;
|
|
186
|
+
const slug = slugParts.join("/");
|
|
187
|
+
const url = slug ? `/${normalizePathSegment(entry)}/${slug}` : `/${normalizePathSegment(entry)}`;
|
|
188
|
+
const agentPath = path.join(dir, "agent.md");
|
|
189
|
+
targets.push({
|
|
190
|
+
slug,
|
|
191
|
+
url,
|
|
192
|
+
pagePath: fullPath,
|
|
193
|
+
pageDir: dir,
|
|
194
|
+
agentPath,
|
|
195
|
+
hasAgentFile: existsSync(agentPath)
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
visit(contentDirAbs, []);
|
|
200
|
+
return targets;
|
|
201
|
+
}
|
|
202
|
+
function resolveCompressionEndpoint(rawBaseUrl) {
|
|
203
|
+
const baseUrl = rawBaseUrl ?? process.env.DOCS_CLOUD_COMPRESSION_BASE_URL ?? process.env.DOCS_CLOUD_API_BASE_URL ?? process.env.FARMING_LABS_DOCS_API_BASE_URL ?? DEFAULT_COMPRESSION_BASE_URL;
|
|
204
|
+
if (/\/v1\/(?:compress|agent\/compact)\/?$/i.test(baseUrl)) return baseUrl.replace(/\/+$/, "");
|
|
205
|
+
return `${baseUrl.replace(/\/+$/, "")}/v1/compress`;
|
|
206
|
+
}
|
|
207
|
+
function runGitCommand(rootDir, args) {
|
|
208
|
+
return execFileSync("git", args, {
|
|
209
|
+
cwd: rootDir,
|
|
210
|
+
encoding: "utf-8"
|
|
211
|
+
}).trim();
|
|
212
|
+
}
|
|
213
|
+
function normalizeGitRelativePath(value) {
|
|
214
|
+
return value.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/^\/+/, "");
|
|
215
|
+
}
|
|
216
|
+
function listGitChangedFiles(rootDir, contentDir) {
|
|
217
|
+
let gitRoot;
|
|
218
|
+
try {
|
|
219
|
+
gitRoot = runGitCommand(rootDir, ["rev-parse", "--show-toplevel"]);
|
|
220
|
+
} catch {
|
|
221
|
+
throw new Error("Use --changed inside a git repository.");
|
|
222
|
+
}
|
|
223
|
+
const contentDirAbs = path.resolve(rootDir, contentDir);
|
|
224
|
+
const relativeContentDir = normalizeGitRelativePath(path.relative(gitRoot, contentDirAbs));
|
|
225
|
+
if (relativeContentDir.startsWith("../")) throw new Error("Configured contentDir must live inside the current git repository for --changed.");
|
|
226
|
+
const pathspec = relativeContentDir || ".";
|
|
227
|
+
const changedFiles = /* @__PURE__ */ new Set();
|
|
228
|
+
const commands = [
|
|
229
|
+
[
|
|
230
|
+
"diff",
|
|
231
|
+
"--name-only",
|
|
232
|
+
"--",
|
|
233
|
+
pathspec
|
|
234
|
+
],
|
|
235
|
+
[
|
|
236
|
+
"diff",
|
|
237
|
+
"--name-only",
|
|
238
|
+
"--cached",
|
|
239
|
+
"--",
|
|
240
|
+
pathspec
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
"ls-files",
|
|
244
|
+
"--others",
|
|
245
|
+
"--exclude-standard",
|
|
246
|
+
"--",
|
|
247
|
+
pathspec
|
|
248
|
+
]
|
|
249
|
+
];
|
|
250
|
+
for (const args of commands) {
|
|
251
|
+
const output = runGitCommand(gitRoot, args);
|
|
252
|
+
if (!output) continue;
|
|
253
|
+
for (const line of output.split("\n")) {
|
|
254
|
+
const normalized = normalizeGitRelativePath(line.trim());
|
|
255
|
+
if (!normalized) continue;
|
|
256
|
+
const absolutePath = path.resolve(gitRoot, normalized);
|
|
257
|
+
const relativeToRoot = normalizeGitRelativePath(path.relative(rootDir, absolutePath));
|
|
258
|
+
if (relativeToRoot && !relativeToRoot.startsWith("../")) changedFiles.add(relativeToRoot);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return changedFiles;
|
|
262
|
+
}
|
|
263
|
+
function resolveCompressionApiKey(explicitApiKey, explicitApiKeyEnv) {
|
|
264
|
+
const apiKey = explicitApiKey ?? (explicitApiKeyEnv && explicitApiKeyEnv.length > 0 ? process.env[explicitApiKeyEnv] : void 0);
|
|
265
|
+
if (!apiKey) {
|
|
266
|
+
if (explicitApiKeyEnv && explicitApiKeyEnv.length > 0) throw new Error(`Missing Docs Cloud API key. The configured API key env "${explicitApiKeyEnv}" was not found in your shell or project .env/.env.local files. Add ${explicitApiKeyEnv}=fl_key_... to .env.local, configure cloud.apiKey.env in your root docs config, or pass --api-key.`);
|
|
267
|
+
throw new Error("Missing Docs Cloud API key. Configure cloud.apiKey.env in your root docs config and set DOCS_CLOUD_API_KEY=fl_key_... in .env/.env.local, or pass --api-key.");
|
|
268
|
+
}
|
|
269
|
+
return apiKey;
|
|
270
|
+
}
|
|
271
|
+
function readCloudApiKeyConfig(content) {
|
|
272
|
+
if (!extractNestedObjectLiteral(content, ["cloud"])) return {};
|
|
273
|
+
const apiKeyBlock = extractNestedObjectLiteral(content, ["cloud", "apiKey"]);
|
|
274
|
+
return { apiKeyEnv: apiKeyBlock ? readStringProperty(apiKeyBlock, "env") ?? DEFAULT_DOCS_CLOUD_API_KEY_ENV : DEFAULT_DOCS_CLOUD_API_KEY_ENV };
|
|
275
|
+
}
|
|
276
|
+
function readAgentCompactConfig(content) {
|
|
277
|
+
const compactBlock = extractNestedObjectLiteral(content, ["agent", "compact"]);
|
|
278
|
+
if (!compactBlock) return {};
|
|
279
|
+
const configuredApiKey = readStringProperty(compactBlock, "apiKey");
|
|
280
|
+
return {
|
|
281
|
+
apiKey: configuredApiKey,
|
|
282
|
+
apiKeyEnv: readStringProperty(compactBlock, "apiKeyEnv") ?? (!configuredApiKey ? readEnvReferenceProperty(compactBlock, "apiKey") : void 0),
|
|
283
|
+
baseUrl: readStringProperty(compactBlock, "baseUrl"),
|
|
284
|
+
model: readStringProperty(compactBlock, "model"),
|
|
285
|
+
aggressiveness: readNumberProperty(compactBlock, "aggressiveness"),
|
|
286
|
+
maxOutputTokens: readNumberProperty(compactBlock, "maxOutputTokens"),
|
|
287
|
+
minOutputTokens: readNumberProperty(compactBlock, "minOutputTokens"),
|
|
288
|
+
protectJson: readBooleanProperty(compactBlock, "protectJson")
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function readCloudApiKeyConfigFromModule(config) {
|
|
292
|
+
if (!config.cloud) return {};
|
|
293
|
+
return { apiKeyEnv: config.cloud.apiKey?.env?.trim() || DEFAULT_DOCS_CLOUD_API_KEY_ENV };
|
|
294
|
+
}
|
|
295
|
+
function readAgentCompactConfigFromModule(config) {
|
|
296
|
+
const compact = config.agent?.compact;
|
|
297
|
+
if (!compact) return {};
|
|
298
|
+
return {
|
|
299
|
+
apiKey: compact.apiKey,
|
|
300
|
+
apiKeyEnv: compact.apiKeyEnv,
|
|
301
|
+
baseUrl: compact.baseUrl,
|
|
302
|
+
model: compact.model,
|
|
303
|
+
aggressiveness: compact.aggressiveness,
|
|
304
|
+
maxOutputTokens: compact.maxOutputTokens,
|
|
305
|
+
minOutputTokens: compact.minOutputTokens,
|
|
306
|
+
protectJson: compact.protectJson
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function mergeAgentCompactOptions(defaults, overrides) {
|
|
310
|
+
return {
|
|
311
|
+
...defaults,
|
|
312
|
+
...Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== void 0))
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function readPageTokenBudget(pagePath) {
|
|
316
|
+
const { data } = matter(readFileSync(pagePath, "utf-8"));
|
|
317
|
+
return normalizePageAgentFrontmatter(data.agent)?.tokenBudget;
|
|
318
|
+
}
|
|
319
|
+
function buildCompactionSettingsHash(options) {
|
|
320
|
+
return hashGeneratedAgentContent(JSON.stringify({
|
|
321
|
+
model: options.model ?? DEFAULT_COMPRESSION_MODEL,
|
|
322
|
+
aggressiveness: options.aggressiveness ?? DEFAULT_COMPRESSION_AGGRESSIVENESS,
|
|
323
|
+
maxOutputTokens: options.maxOutputTokens ?? null,
|
|
324
|
+
minOutputTokens: options.minOutputTokens ?? null,
|
|
325
|
+
protectJson: options.protectJson ?? null
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
function buildPageOptions(defaults, pagePath) {
|
|
329
|
+
const tokenBudget = readPageTokenBudget(pagePath);
|
|
330
|
+
const pageOptions = mergeAgentCompactOptions(defaults, { maxOutputTokens: tokenBudget });
|
|
331
|
+
if (pageOptions.minOutputTokens !== void 0 && pageOptions.maxOutputTokens !== void 0 && pageOptions.minOutputTokens > pageOptions.maxOutputTokens) pageOptions.minOutputTokens = pageOptions.maxOutputTokens;
|
|
332
|
+
return {
|
|
333
|
+
pageOptions,
|
|
334
|
+
tokenBudget
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function buildResolvedPageSourceDocument(page) {
|
|
338
|
+
return stripGeneratedPageAgentContractMarkdown(renderDocsMarkdownDocument({
|
|
339
|
+
...page,
|
|
340
|
+
agentContent: void 0,
|
|
341
|
+
agentRawContent: void 0
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
function buildAgentSourceDocument(page) {
|
|
345
|
+
if (typeof page.agentRawContent === "string") return stripGeneratedPageAgentContractMarkdown(page.agentRawContent);
|
|
346
|
+
return stripGeneratedPageAgentContractMarkdown(renderDocsMarkdownDocument(page));
|
|
347
|
+
}
|
|
348
|
+
function readCurrentAgentDocument(target) {
|
|
349
|
+
if (!target.hasAgentFile || !existsSync(target.agentPath)) return void 0;
|
|
350
|
+
return parseGeneratedAgentDocument(readFileSync(target.agentPath, "utf-8"));
|
|
351
|
+
}
|
|
352
|
+
function shouldCompactChangedAgentFile(target) {
|
|
353
|
+
const currentDocument = readCurrentAgentDocument(target);
|
|
354
|
+
if (!currentDocument) return false;
|
|
355
|
+
if (!currentDocument.provenance) return true;
|
|
356
|
+
if (currentDocument.provenance.sourceKind !== "agent-md") return false;
|
|
357
|
+
return hashGeneratedAgentContent(currentDocument.content) !== currentDocument.provenance.outputHash;
|
|
358
|
+
}
|
|
359
|
+
function resolveSourceKindForCompaction(target, currentDocument) {
|
|
360
|
+
if (!target.hasAgentFile) return "resolved-page";
|
|
361
|
+
if (currentDocument?.provenance?.sourceKind === "resolved-page") return "resolved-page";
|
|
362
|
+
return "agent-md";
|
|
363
|
+
}
|
|
364
|
+
function buildSourceDocumentForCompaction(page, sourceKind) {
|
|
365
|
+
return sourceKind === "resolved-page" ? buildResolvedPageSourceDocument(page) : buildAgentSourceDocument(page);
|
|
366
|
+
}
|
|
367
|
+
function buildGeneratedAgentProvenance(sourceKind, sourceDocument, output, pageOptions) {
|
|
368
|
+
return {
|
|
369
|
+
version: GENERATED_AGENT_PROVENANCE_VERSION,
|
|
370
|
+
sourceKind,
|
|
371
|
+
sourceHash: hashGeneratedAgentContent(sourceDocument),
|
|
372
|
+
settingsHash: buildCompactionSettingsHash(pageOptions),
|
|
373
|
+
outputHash: hashGeneratedAgentContent(output),
|
|
374
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function inspectAgentCompactionState(page, target, defaults) {
|
|
378
|
+
const { pageOptions, tokenBudget } = buildPageOptions(defaults, target.pagePath);
|
|
379
|
+
const currentDocument = readCurrentAgentDocument(target);
|
|
380
|
+
if (!currentDocument) return {
|
|
381
|
+
status: "missing",
|
|
382
|
+
sourceKind: "resolved-page",
|
|
383
|
+
pageOptions,
|
|
384
|
+
sourceDocument: buildResolvedPageSourceDocument(page),
|
|
385
|
+
tokenBudget
|
|
386
|
+
};
|
|
387
|
+
const sourceKind = resolveSourceKindForCompaction(target, currentDocument);
|
|
388
|
+
const sourceDocument = buildSourceDocumentForCompaction(page, sourceKind);
|
|
389
|
+
if (!currentDocument.provenance) return {
|
|
390
|
+
status: "unknown",
|
|
391
|
+
sourceKind,
|
|
392
|
+
pageOptions,
|
|
393
|
+
sourceDocument,
|
|
394
|
+
tokenBudget
|
|
395
|
+
};
|
|
396
|
+
const outputModified = hashGeneratedAgentContent(currentDocument.content) !== currentDocument.provenance.outputHash;
|
|
397
|
+
if (currentDocument.provenance.sourceKind === "agent-md") return {
|
|
398
|
+
status: outputModified ? "modified" : "unknown",
|
|
399
|
+
sourceKind,
|
|
400
|
+
pageOptions,
|
|
401
|
+
sourceDocument,
|
|
402
|
+
provenance: currentDocument.provenance,
|
|
403
|
+
tokenBudget
|
|
404
|
+
};
|
|
405
|
+
const sourceChanged = hashGeneratedAgentContent(sourceDocument) !== currentDocument.provenance.sourceHash;
|
|
406
|
+
const settingsChanged = buildCompactionSettingsHash(pageOptions) !== currentDocument.provenance.settingsHash;
|
|
407
|
+
return {
|
|
408
|
+
status: outputModified && (sourceChanged || settingsChanged) ? "stale-modified" : outputModified ? "modified" : sourceChanged || settingsChanged ? "stale" : "fresh",
|
|
409
|
+
sourceKind,
|
|
410
|
+
pageOptions,
|
|
411
|
+
sourceDocument,
|
|
412
|
+
provenance: currentDocument.provenance,
|
|
413
|
+
tokenBudget
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function protectForCompression(input) {
|
|
417
|
+
const segments = [];
|
|
418
|
+
const stash = (value) => {
|
|
419
|
+
const token = `__DOCS_SAFE_${segments.length}__`;
|
|
420
|
+
segments.push(value);
|
|
421
|
+
return token;
|
|
422
|
+
};
|
|
423
|
+
let result = input;
|
|
424
|
+
result = result.replace(/```[\s\S]*?```/g, stash);
|
|
425
|
+
result = result.replace(/\[[^\]]+\]\([^)]+\)/g, stash);
|
|
426
|
+
result = result.replace(/`[^`\n]+`/g, stash);
|
|
427
|
+
result = result.replace(/^(URL|Description|Related):[^\n]*$/gm, stash);
|
|
428
|
+
result = result.replace(/https?:\/\/[^\s)]+/g, stash);
|
|
429
|
+
for (let index = 0; index < segments.length; index += 1) result = result.replace(`__DOCS_SAFE_${index}__`, `<docs_safe>${segments[index]}</docs_safe>`);
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
function sanitizeCompressedOutput(output) {
|
|
433
|
+
return output.replace(/<\/?docs_safe>/g, "");
|
|
434
|
+
}
|
|
435
|
+
async function compressDocument(input, options) {
|
|
436
|
+
const apiKey = resolveCompressionApiKey(options.apiKey, options.apiKeyEnv);
|
|
437
|
+
const endpoint = resolveCompressionEndpoint(options.baseUrl);
|
|
438
|
+
const aggressiveness = options.aggressiveness ?? DEFAULT_COMPRESSION_AGGRESSIVENESS;
|
|
439
|
+
if (aggressiveness < 0 || aggressiveness > 1) throw new Error("Aggressiveness must be between 0.0 and 1.0.");
|
|
440
|
+
const payload = {
|
|
441
|
+
model: options.model ?? DEFAULT_COMPRESSION_MODEL,
|
|
442
|
+
input: protectForCompression(input),
|
|
443
|
+
compression_settings: {
|
|
444
|
+
aggressiveness,
|
|
445
|
+
...options.maxOutputTokens !== void 0 ? { max_output_tokens: options.maxOutputTokens } : {},
|
|
446
|
+
...options.minOutputTokens !== void 0 ? { min_output_tokens: options.minOutputTokens } : {},
|
|
447
|
+
...options.protectJson !== void 0 ? { protect_json: options.protectJson } : {}
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
const response = await fetch(endpoint, {
|
|
451
|
+
method: "POST",
|
|
452
|
+
headers: {
|
|
453
|
+
Authorization: `Bearer ${apiKey}`,
|
|
454
|
+
"Content-Type": "application/json"
|
|
455
|
+
},
|
|
456
|
+
body: JSON.stringify(payload)
|
|
457
|
+
});
|
|
458
|
+
if (!response.ok) {
|
|
459
|
+
const body = await response.text();
|
|
460
|
+
throw new Error(`Docs Cloud compression request failed (${response.status}): ${body || response.statusText}`);
|
|
461
|
+
}
|
|
462
|
+
const result = await response.json();
|
|
463
|
+
const sanitizedOutput = typeof result.output === "string" ? sanitizeCompressedOutput(result.output) : result.output;
|
|
464
|
+
if (typeof sanitizedOutput !== "string" || sanitizedOutput.trim().length === 0) throw new Error("Docs Cloud compression response did not include a compressed output.");
|
|
465
|
+
return {
|
|
466
|
+
...result,
|
|
467
|
+
output: sanitizedOutput
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
function resolveSelectedPages(pages, targets, entry, requested, includeAll) {
|
|
471
|
+
const compactableBySlug = new Map(targets.map((target) => [target.slug, target]));
|
|
472
|
+
if (includeAll) return targets.map((target) => {
|
|
473
|
+
const page = pages.find((candidate) => candidate.slug === target.slug);
|
|
474
|
+
return page ? {
|
|
475
|
+
page,
|
|
476
|
+
target
|
|
477
|
+
} : null;
|
|
478
|
+
}).filter((value) => value !== null);
|
|
479
|
+
const resolved = [];
|
|
480
|
+
const seen = /* @__PURE__ */ new Set();
|
|
481
|
+
for (const rawRequest of requested) {
|
|
482
|
+
const page = findDocsMarkdownPage(entry, pages, normalizeRequestedPage(entry, rawRequest));
|
|
483
|
+
if (!page) throw new Error(`Could not find a docs page for "${rawRequest}".`);
|
|
484
|
+
const target = compactableBySlug.get(page.slug);
|
|
485
|
+
if (!target) throw new Error(`Page "${rawRequest}" does not use a folder-based page file, so it cannot be written to a sibling agent.md automatically.`);
|
|
486
|
+
if (seen.has(target.slug)) continue;
|
|
487
|
+
seen.add(target.slug);
|
|
488
|
+
resolved.push({
|
|
489
|
+
page,
|
|
490
|
+
target
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
return resolved;
|
|
494
|
+
}
|
|
495
|
+
function filterChangedPages(rootDir, contentDir, selectedPages) {
|
|
496
|
+
const changedFiles = listGitChangedFiles(rootDir, contentDir);
|
|
497
|
+
return selectedPages.filter(({ target }) => {
|
|
498
|
+
const relativePagePath = normalizeGitRelativePath(path.relative(rootDir, target.pagePath));
|
|
499
|
+
if (changedFiles.has(relativePagePath)) return true;
|
|
500
|
+
const relativeAgentPath = normalizeGitRelativePath(path.relative(rootDir, target.agentPath));
|
|
501
|
+
return changedFiles.has(relativeAgentPath) && shouldCompactChangedAgentFile(target);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
async function compactAgentDocs(options = {}) {
|
|
505
|
+
const rootDir = process.cwd();
|
|
506
|
+
const loadedEnv = loadProjectEnv(rootDir);
|
|
507
|
+
for (const [key, value] of Object.entries(loadedEnv)) if (process.env[key] === void 0) process.env[key] = value;
|
|
508
|
+
const loadedConfigModule = await loadDocsConfigModule(rootDir, options.configPath);
|
|
509
|
+
const configContent = readFileSync(loadedConfigModule?.path ?? resolveDocsConfigPath(rootDir, options.configPath), "utf-8");
|
|
510
|
+
const resolvedOptions = mergeAgentCompactOptions(mergeAgentCompactOptions(mergeAgentCompactOptions(readCloudApiKeyConfig(configContent), loadedConfigModule?.config ? readCloudApiKeyConfigFromModule(loadedConfigModule.config) : {}), mergeAgentCompactOptions(readAgentCompactConfig(configContent), loadedConfigModule?.config ? readAgentCompactConfigFromModule(loadedConfigModule.config) : {})), options);
|
|
511
|
+
const entry = normalizePathSegment(loadedConfigModule?.config.entry ?? readTopLevelStringProperty(configContent, "entry") ?? "docs") || "docs";
|
|
512
|
+
const contentDir = typeof loadedConfigModule?.config.contentDir === "string" ? loadedConfigModule.config.contentDir : resolveDocsContentDir(rootDir, configContent, entry);
|
|
513
|
+
const siteTitle = typeof loadedConfigModule?.config.nav?.title === "string" ? loadedConfigModule.config.nav.title : readNavTitle(configContent) ?? "Documentation";
|
|
514
|
+
if (resolvedOptions.all && resolvedOptions.pages && resolvedOptions.pages.length > 0) throw new Error("Use either --all or specific page arguments, not both.");
|
|
515
|
+
if (resolvedOptions.includeMissing && !resolvedOptions.stale) throw new Error("Use --include-missing together with --stale.");
|
|
516
|
+
const requestedPages = resolvedOptions.pages?.filter((value) => value.trim().length > 0) ?? [];
|
|
517
|
+
if (!resolvedOptions.all && requestedPages.length === 0 && !resolvedOptions.stale && !resolvedOptions.changed) throw new Error("Pass --all, --changed, --stale, or at least one docs page slug/path to compact.");
|
|
518
|
+
const pages = await createFilesystemDocsMcpSource({
|
|
519
|
+
rootDir,
|
|
520
|
+
entry,
|
|
521
|
+
contentDir,
|
|
522
|
+
siteTitle
|
|
523
|
+
}).getPages();
|
|
524
|
+
if (pages.length === 0) throw new Error(`No docs content was found under ${contentDir}.`);
|
|
525
|
+
const selectedPages = resolveSelectedPages(pages, scanDocsPageTargets(rootDir, contentDir, entry), entry, requestedPages, resolvedOptions.all === true || resolvedOptions.stale === true && requestedPages.length === 0 || resolvedOptions.changed === true && requestedPages.length === 0);
|
|
526
|
+
const filteredPages = resolvedOptions.changed ? filterChangedPages(rootDir, contentDir, selectedPages) : selectedPages;
|
|
527
|
+
if (filteredPages.length === 0) {
|
|
528
|
+
if (resolvedOptions.changed) {
|
|
529
|
+
console.log(pc.green("No changed docs pages needed compaction."));
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
throw new Error("No compactable docs pages matched the request.");
|
|
533
|
+
}
|
|
534
|
+
let created = 0;
|
|
535
|
+
let overwritten = 0;
|
|
536
|
+
let processed = 0;
|
|
537
|
+
let skippedFresh = 0;
|
|
538
|
+
let skippedModified = 0;
|
|
539
|
+
let skippedUnknown = 0;
|
|
540
|
+
let skippedMissing = 0;
|
|
541
|
+
const requestedExplicitPages = requestedPages.length > 0;
|
|
542
|
+
for (const { page, target } of filteredPages) {
|
|
543
|
+
const state = inspectAgentCompactionState(page, target, resolvedOptions);
|
|
544
|
+
if (resolvedOptions.stale) {
|
|
545
|
+
if (state.status === "fresh") {
|
|
546
|
+
skippedFresh += 1;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (state.status === "modified" || state.status === "stale-modified") {
|
|
550
|
+
skippedModified += 1;
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (state.status === "unknown") {
|
|
554
|
+
skippedUnknown += 1;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
if (state.status === "missing") {
|
|
558
|
+
if (!(resolvedOptions.includeMissing === true && (requestedExplicitPages || state.tokenBudget !== void 0))) {
|
|
559
|
+
skippedMissing += 1;
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
const compressed = await compressDocument(state.sourceDocument, state.pageOptions);
|
|
565
|
+
const nextContent = compressed.output.trimEnd();
|
|
566
|
+
const generatedDocument = serializeGeneratedAgentDocument(nextContent, buildGeneratedAgentProvenance(state.sourceKind, state.sourceDocument, nextContent, state.pageOptions));
|
|
567
|
+
console.log(pc.dim(`Compacting ${page.url} (${compressed.original_input_tokens ?? "?"} -> ${compressed.output_tokens ?? "?"} tokens)...`));
|
|
568
|
+
if (resolvedOptions.dryRun) continue;
|
|
569
|
+
mkdirSync(target.pageDir, { recursive: true });
|
|
570
|
+
writeFileSync(target.agentPath, generatedDocument, "utf-8");
|
|
571
|
+
if (target.hasAgentFile) overwritten += 1;
|
|
572
|
+
else created += 1;
|
|
573
|
+
processed += 1;
|
|
574
|
+
}
|
|
575
|
+
if (resolvedOptions.dryRun) processed = filteredPages.length - skippedFresh - skippedModified - skippedUnknown - skippedMissing;
|
|
576
|
+
if (resolvedOptions.stale && processed === 0) {
|
|
577
|
+
console.log(pc.green("No stale generated agent.md files needed updates."));
|
|
578
|
+
if (skippedFresh + skippedModified + skippedUnknown + skippedMissing > 0) console.log(pc.dim(`Skipped ${skippedFresh} fresh, ${skippedModified} modified, ${skippedUnknown} unknown, and ${skippedMissing} missing page${skippedFresh + skippedModified + skippedUnknown + skippedMissing === 1 ? "" : "s"}.`));
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
const summaryPrefix = resolvedOptions.dryRun ? "Dry run complete" : "Compaction complete";
|
|
582
|
+
console.log(pc.green(`${summaryPrefix}: ${processed} page${processed === 1 ? "" : "s"} processed` + (resolvedOptions.dryRun ? "." : ` (${created} created, ${overwritten} overwritten).`)));
|
|
583
|
+
if (resolvedOptions.stale) console.log(pc.dim(`Skipped ${skippedFresh} fresh, ${skippedModified} modified, ${skippedUnknown} unknown, and ${skippedMissing} missing page${skippedFresh + skippedModified + skippedUnknown + skippedMissing === 1 ? "" : "s"}.`));
|
|
584
|
+
}
|
|
585
|
+
function printAgentCompactHelp() {
|
|
586
|
+
console.log(`
|
|
587
|
+
${pc.bold("docs agent compact")} — Generate sibling ${pc.cyan("agent.md")} files by compacting resolved docs pages.
|
|
588
|
+
|
|
589
|
+
${pc.dim("Usage:")}
|
|
590
|
+
npx @farming-labs/docs@latest ${pc.cyan("agent compact")} ${pc.dim("[page ...]")}
|
|
591
|
+
|
|
592
|
+
${pc.dim("Examples:")}
|
|
593
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact installation configuration")}
|
|
594
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact /docs/installation")}
|
|
595
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact --page installation --page configuration")}
|
|
596
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact --all")}
|
|
597
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact --changed")}
|
|
598
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact --stale")}
|
|
599
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact --stale --include-missing")}
|
|
600
|
+
|
|
601
|
+
${pc.dim("Per-page override:")}
|
|
602
|
+
Add ${pc.cyan("agent.tokenBudget")} to a page frontmatter block to override the compact output target for that page.
|
|
603
|
+
|
|
604
|
+
${pc.dim("Options:")}
|
|
605
|
+
${pc.cyan("--all")} Compact every folder-based docs page under the configured contentDir
|
|
606
|
+
${pc.cyan("--page <slug|path>")} Add a page explicitly (repeatable); positional page args work too
|
|
607
|
+
${pc.cyan("--changed")} Compact only docs pages changed in the current git working tree
|
|
608
|
+
${pc.cyan("--stale")} Re-compact only stale generated agent.md files
|
|
609
|
+
${pc.cyan("--include-missing")} With ${pc.cyan("--stale")}, also create missing agent.md files for explicit pages or pages that define ${pc.cyan("agent.tokenBudget")}
|
|
610
|
+
${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
|
|
611
|
+
${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
|
|
612
|
+
${pc.cyan("--api-key-env <name>")} Env var name for the Docs Cloud API key; prefer ${pc.dim("cloud.apiKey.env")}
|
|
613
|
+
${pc.cyan("--base-url <url>")} Override the compression API base URL (useful for tests)
|
|
614
|
+
${pc.cyan("--model <name>")} Compression model (${pc.dim("docs-cloud-compress-v1")} by default)
|
|
615
|
+
${pc.cyan("--aggressiveness <0-1>")} Compression intensity (${pc.dim("0.3")} by default)
|
|
616
|
+
${pc.cyan("--max-output-tokens <n>")} Pass through to compression settings
|
|
617
|
+
${pc.cyan("--min-output-tokens <n>")} Pass through to compression settings
|
|
618
|
+
${pc.cyan("--protect-json <bool>")} Preserve JSON objects during compression
|
|
619
|
+
${pc.cyan("--dry-run")} Resolve pages and call the compressor without writing files
|
|
620
|
+
`);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
//#endregion
|
|
624
|
+
export { scanDocsPageTargets as i, compactAgentDocs as n, inspectAgentCompactionState as r, agent_exports as t };
|