@farming-labs/docs 0.1.123 → 0.1.125
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-cmkIPvWo.mjs → agent-CBhuGaVu.mjs} +3 -3
- package/dist/agents-BPX4YRPs.mjs +209 -0
- package/dist/cli/index.mjs +31 -16
- package/dist/codeblocks-B4QzFqNC.mjs +1706 -0
- package/dist/{config-BHRL4R2v.mjs → config-B8wA38OC.mjs} +2 -2
- package/dist/{dev-C03tUSTz.mjs → dev-1VFeAWUi.mjs} +2 -2
- package/dist/{doctor-C9wz-4CE.mjs → doctor-lV9lhqGA.mjs} +3 -3
- package/dist/{downgrade-Bv7E5LV2.mjs → downgrade-BGANzvQb.mjs} +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/{init-_HAuo5Dv.mjs → init-DzQWiVN1.mjs} +1 -1
- package/dist/{mcp-veym29Yc.mjs → mcp-BvERlrP_.mjs} +1 -1
- package/dist/mcp.d.mts +1 -1
- package/dist/mcp.mjs +68 -7
- package/dist/{reading-time-Io7iRZ7S.mjs → reading-time-HpTq-0VB.mjs} +1 -0
- package/dist/{review-DEG_UnxV.mjs → review-ClNDYRnn.mjs} +1 -1
- package/dist/{robots-BkRZN-56.mjs → robots-CNLiZCay.mjs} +1 -1
- package/dist/{search-HqdPzNb3.mjs → search-BjyDm30M.mjs} +1 -1
- package/dist/{search-XmPPiA0V.d.mts → search-G0otDMfP.d.mts} +1 -1
- package/dist/server.d.mts +2 -2
- package/dist/{sitemap-wtr3w_D2.mjs → sitemap-BdSdcAmS.mjs} +1 -1
- package/dist/{types-CLofDwjw.d.mts → types-Dg82p0Fm.d.mts} +137 -1
- package/dist/{upgrade-DrOWQIKI.mjs → upgrade-C7DvvfTR.mjs} +1 -1
- package/package.json +2 -1
- package/dist/agents-B3kj54S3.mjs +0 -795
- /package/dist/{package-version-L4GZowaF.mjs → package-version-OrjpmqoR.mjs} +0 -0
- /package/dist/{templates-CakZBXK8.mjs → templates-CGaORZOY.mjs} +0 -0
package/dist/agents-B3kj54S3.mjs
DELETED
|
@@ -1,795 +0,0 @@
|
|
|
1
|
-
import { _ as parseGeneratedAgentDocument, h as hashGeneratedAgentContent, m as GENERATED_AGENT_PROVENANCE_VERSION, v as serializeGeneratedAgentDocument } from "./search-BL7o2rXk.mjs";
|
|
2
|
-
import { E as findDocsMarkdownPage, K as resolveDocsAgentFeedbackConfig, U as renderDocsMarkdownDocument, V as renderDocsAgentsDocument, a as DEFAULT_AGENT_MD_ROUTE, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, o as DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, y as DEFAULT_OPENAPI_SCHEMA_ROUTE } from "./agent-BS39vnhS.mjs";
|
|
3
|
-
import "./index.mjs";
|
|
4
|
-
import { S as resolveApiReferenceConfig } from "./sitemap-server-BZHoJHqC.mjs";
|
|
5
|
-
import { createFilesystemDocsMcpSource, resolveDocsMcpConfig } from "./mcp.mjs";
|
|
6
|
-
import "./server.mjs";
|
|
7
|
-
import { a as loadProjectEnv, c as readNavTitle, d as readTopLevelStringProperty, f as resolveDocsConfigPath, i as loadDocsConfigModule, l as readNumberProperty, o as readBooleanProperty, p as resolveDocsContentDir, s as readEnvReferenceProperty, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-BHRL4R2v.mjs";
|
|
8
|
-
import { t as detectFramework } from "./utils-x5EtYWjC.mjs";
|
|
9
|
-
import matter from "gray-matter";
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
11
|
-
import path from "node:path";
|
|
12
|
-
import pc from "picocolors";
|
|
13
|
-
import { execFileSync } from "node:child_process";
|
|
14
|
-
|
|
15
|
-
//#region src/cli/agent.ts
|
|
16
|
-
const DEFAULT_TTC_BASE_URL = "https://api.thetokencompany.com";
|
|
17
|
-
const DEFAULT_TTC_MODEL = "bear-1.2";
|
|
18
|
-
const DEFAULT_TTC_AGGRESSIVENESS = .3;
|
|
19
|
-
const INDEX_PAGE_BASENAMES = new Set([
|
|
20
|
-
"index",
|
|
21
|
-
"page",
|
|
22
|
-
"+page"
|
|
23
|
-
]);
|
|
24
|
-
function parseBooleanFlag(raw) {
|
|
25
|
-
if (raw === "true") return true;
|
|
26
|
-
if (raw === "false") return false;
|
|
27
|
-
throw new Error(`Invalid boolean value: ${raw}. Use true or false.`);
|
|
28
|
-
}
|
|
29
|
-
function parseIntegerFlag(raw, name) {
|
|
30
|
-
const parsed = Number.parseInt(raw, 10);
|
|
31
|
-
if (!Number.isFinite(parsed)) throw new Error(`Invalid ${name}: ${raw}.`);
|
|
32
|
-
return parsed;
|
|
33
|
-
}
|
|
34
|
-
function parseFloatFlag(raw, name) {
|
|
35
|
-
const parsed = Number.parseFloat(raw);
|
|
36
|
-
if (!Number.isFinite(parsed)) throw new Error(`Invalid ${name}: ${raw}.`);
|
|
37
|
-
return parsed;
|
|
38
|
-
}
|
|
39
|
-
function parseAgentCompactArgs(argv) {
|
|
40
|
-
const parsed = { pages: [] };
|
|
41
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
42
|
-
const arg = argv[index];
|
|
43
|
-
if (arg === "--help" || arg === "-h") {
|
|
44
|
-
parsed.help = true;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (arg === "--all") {
|
|
48
|
-
parsed.all = true;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (arg === "--dry-run") {
|
|
52
|
-
parsed.dryRun = true;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (arg === "--changed") {
|
|
56
|
-
parsed.changed = true;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (arg === "--stale") {
|
|
60
|
-
parsed.stale = true;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (arg === "--include-missing") {
|
|
64
|
-
parsed.includeMissing = true;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (arg === "--protect-json") {
|
|
68
|
-
const nextValue = argv[index + 1];
|
|
69
|
-
if (nextValue && !nextValue.startsWith("--")) {
|
|
70
|
-
parsed.protectJson = parseBooleanFlag(nextValue);
|
|
71
|
-
index += 1;
|
|
72
|
-
} else parsed.protectJson = true;
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
if (!arg.startsWith("--")) {
|
|
76
|
-
parsed.pages.push(arg);
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
const [rawKey, inlineValue] = arg.slice(2).split("=", 2);
|
|
80
|
-
const key = rawKey.trim();
|
|
81
|
-
const hasInlineValue = inlineValue !== void 0;
|
|
82
|
-
const nextValue = !hasInlineValue ? argv[index + 1] : void 0;
|
|
83
|
-
const consumeNextValue = () => {
|
|
84
|
-
if (!nextValue || nextValue.startsWith("--")) throw new Error(`Missing value for --${key}.`);
|
|
85
|
-
index += 1;
|
|
86
|
-
return nextValue;
|
|
87
|
-
};
|
|
88
|
-
const value = hasInlineValue ? inlineValue : consumeNextValue();
|
|
89
|
-
switch (key) {
|
|
90
|
-
case "page":
|
|
91
|
-
parsed.pages.push(value);
|
|
92
|
-
break;
|
|
93
|
-
case "config":
|
|
94
|
-
parsed.configPath = value;
|
|
95
|
-
break;
|
|
96
|
-
case "api-key":
|
|
97
|
-
parsed.apiKey = value;
|
|
98
|
-
break;
|
|
99
|
-
case "base-url":
|
|
100
|
-
parsed.baseUrl = value;
|
|
101
|
-
break;
|
|
102
|
-
case "api-key-env":
|
|
103
|
-
parsed.apiKeyEnv = value;
|
|
104
|
-
break;
|
|
105
|
-
case "model":
|
|
106
|
-
parsed.model = value;
|
|
107
|
-
break;
|
|
108
|
-
case "aggressiveness":
|
|
109
|
-
parsed.aggressiveness = parseFloatFlag(value, "aggressiveness");
|
|
110
|
-
break;
|
|
111
|
-
case "max-output-tokens":
|
|
112
|
-
parsed.maxOutputTokens = parseIntegerFlag(value, "max-output-tokens");
|
|
113
|
-
break;
|
|
114
|
-
case "min-output-tokens":
|
|
115
|
-
parsed.minOutputTokens = parseIntegerFlag(value, "min-output-tokens");
|
|
116
|
-
break;
|
|
117
|
-
case "protect-json":
|
|
118
|
-
parsed.protectJson = parseBooleanFlag(value);
|
|
119
|
-
break;
|
|
120
|
-
default: throw new Error(`Unknown agent compact flag: --${key}.`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return parsed;
|
|
124
|
-
}
|
|
125
|
-
function normalizePathSegment(value) {
|
|
126
|
-
return value.replace(/^\/+|\/+$/g, "");
|
|
127
|
-
}
|
|
128
|
-
function normalizeUrlPath(value) {
|
|
129
|
-
const normalized = value.replace(/\/+/g, "/");
|
|
130
|
-
if (normalized === "/") return normalized;
|
|
131
|
-
return normalized.replace(/\/+$/, "");
|
|
132
|
-
}
|
|
133
|
-
function normalizeRequestedPage(entry, rawValue) {
|
|
134
|
-
const trimmed = rawValue.trim();
|
|
135
|
-
if (!trimmed || trimmed === ".") return `/${normalizePathSegment(entry) || "docs"}`;
|
|
136
|
-
if (/^https?:\/\//i.test(trimmed)) try {
|
|
137
|
-
return normalizeUrlPath(new URL(trimmed).pathname.replace(/\.md$/i, ""));
|
|
138
|
-
} catch {
|
|
139
|
-
return trimmed;
|
|
140
|
-
}
|
|
141
|
-
const withoutMarkdownSuffix = trimmed.replace(/\.md$/i, "");
|
|
142
|
-
const normalizedEntry = `/${normalizePathSegment(entry) || "docs"}`;
|
|
143
|
-
const normalized = normalizeUrlPath(withoutMarkdownSuffix.startsWith("/") ? withoutMarkdownSuffix : `/${withoutMarkdownSuffix}`);
|
|
144
|
-
if (normalized === normalizedEntry || normalized.startsWith(`${normalizedEntry}/`)) return normalized;
|
|
145
|
-
const slug = normalizePathSegment(withoutMarkdownSuffix);
|
|
146
|
-
return slug ? normalizeUrlPath(`${normalizedEntry}/${slug}`) : normalizedEntry;
|
|
147
|
-
}
|
|
148
|
-
function scanDocsPageTargets(rootDir, contentDir, entry) {
|
|
149
|
-
const contentDirAbs = path.resolve(rootDir, contentDir);
|
|
150
|
-
const targets = [];
|
|
151
|
-
const visit = (dir, slugParts) => {
|
|
152
|
-
if (!existsSync(dir)) return;
|
|
153
|
-
for (const name of readdirSync(dir).sort()) {
|
|
154
|
-
const fullPath = path.join(dir, name);
|
|
155
|
-
if (statSync(fullPath).isDirectory()) {
|
|
156
|
-
visit(fullPath, [...slugParts, name]);
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (name === "agent.md") continue;
|
|
160
|
-
if (!name.endsWith(".md") && !name.endsWith(".mdx") && !name.endsWith(".svx")) continue;
|
|
161
|
-
const baseName = name.replace(/\.(md|mdx|svx)$/i, "");
|
|
162
|
-
if (!INDEX_PAGE_BASENAMES.has(baseName)) continue;
|
|
163
|
-
const slug = slugParts.join("/");
|
|
164
|
-
const url = slug ? `/${normalizePathSegment(entry)}/${slug}` : `/${normalizePathSegment(entry)}`;
|
|
165
|
-
const agentPath = path.join(dir, "agent.md");
|
|
166
|
-
targets.push({
|
|
167
|
-
slug,
|
|
168
|
-
url,
|
|
169
|
-
pagePath: fullPath,
|
|
170
|
-
pageDir: dir,
|
|
171
|
-
agentPath,
|
|
172
|
-
hasAgentFile: existsSync(agentPath)
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
visit(contentDirAbs, []);
|
|
177
|
-
return targets;
|
|
178
|
-
}
|
|
179
|
-
function resolveCompressionEndpoint(rawBaseUrl) {
|
|
180
|
-
const baseUrl = rawBaseUrl ?? process.env.TOKEN_COMPANY_BASE_URL ?? process.env.THE_TOKEN_COMPANY_BASE_URL ?? DEFAULT_TTC_BASE_URL;
|
|
181
|
-
if (/\/v1\/compress\/?$/i.test(baseUrl)) return baseUrl.replace(/\/+$/, "");
|
|
182
|
-
return `${baseUrl.replace(/\/+$/, "")}/v1/compress`;
|
|
183
|
-
}
|
|
184
|
-
function runGitCommand(rootDir, args) {
|
|
185
|
-
return execFileSync("git", args, {
|
|
186
|
-
cwd: rootDir,
|
|
187
|
-
encoding: "utf-8"
|
|
188
|
-
}).trim();
|
|
189
|
-
}
|
|
190
|
-
function normalizeGitRelativePath(value) {
|
|
191
|
-
return value.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/^\/+/, "");
|
|
192
|
-
}
|
|
193
|
-
function listGitChangedFiles(rootDir, contentDir) {
|
|
194
|
-
let gitRoot;
|
|
195
|
-
try {
|
|
196
|
-
gitRoot = runGitCommand(rootDir, ["rev-parse", "--show-toplevel"]);
|
|
197
|
-
} catch {
|
|
198
|
-
throw new Error("Use --changed inside a git repository.");
|
|
199
|
-
}
|
|
200
|
-
const contentDirAbs = path.resolve(rootDir, contentDir);
|
|
201
|
-
const relativeContentDir = normalizeGitRelativePath(path.relative(gitRoot, contentDirAbs));
|
|
202
|
-
if (relativeContentDir.startsWith("../")) throw new Error("Configured contentDir must live inside the current git repository for --changed.");
|
|
203
|
-
const pathspec = relativeContentDir || ".";
|
|
204
|
-
const changedFiles = /* @__PURE__ */ new Set();
|
|
205
|
-
const commands = [
|
|
206
|
-
[
|
|
207
|
-
"diff",
|
|
208
|
-
"--name-only",
|
|
209
|
-
"--",
|
|
210
|
-
pathspec
|
|
211
|
-
],
|
|
212
|
-
[
|
|
213
|
-
"diff",
|
|
214
|
-
"--name-only",
|
|
215
|
-
"--cached",
|
|
216
|
-
"--",
|
|
217
|
-
pathspec
|
|
218
|
-
],
|
|
219
|
-
[
|
|
220
|
-
"ls-files",
|
|
221
|
-
"--others",
|
|
222
|
-
"--exclude-standard",
|
|
223
|
-
"--",
|
|
224
|
-
pathspec
|
|
225
|
-
]
|
|
226
|
-
];
|
|
227
|
-
for (const args of commands) {
|
|
228
|
-
const output = runGitCommand(gitRoot, args);
|
|
229
|
-
if (!output) continue;
|
|
230
|
-
for (const line of output.split("\n")) {
|
|
231
|
-
const normalized = normalizeGitRelativePath(line.trim());
|
|
232
|
-
if (!normalized) continue;
|
|
233
|
-
const absolutePath = path.resolve(gitRoot, normalized);
|
|
234
|
-
const relativeToRoot = normalizeGitRelativePath(path.relative(rootDir, absolutePath));
|
|
235
|
-
if (relativeToRoot && !relativeToRoot.startsWith("../")) changedFiles.add(relativeToRoot);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return changedFiles;
|
|
239
|
-
}
|
|
240
|
-
function resolveCompressionApiKey(explicitApiKey, explicitApiKeyEnv) {
|
|
241
|
-
const candidateKeys = [
|
|
242
|
-
explicitApiKeyEnv,
|
|
243
|
-
"TOKEN_COMPANY_API_KEY",
|
|
244
|
-
"THE_TOKEN_COMPANY_API_KEY",
|
|
245
|
-
"TTC_API_KEY"
|
|
246
|
-
].filter((value) => typeof value === "string" && value.length > 0);
|
|
247
|
-
const apiKey = explicitApiKey ?? candidateKeys.map((key) => process.env[key]).find(Boolean);
|
|
248
|
-
if (!apiKey) throw new Error("Missing Token Company API key. Pass --api-key, set agent.compact.apiKey/apiKeyEnv, or set TOKEN_COMPANY_API_KEY.");
|
|
249
|
-
return apiKey;
|
|
250
|
-
}
|
|
251
|
-
function readAgentCompactConfig(content) {
|
|
252
|
-
const compactBlock = extractNestedObjectLiteral(content, ["agent", "compact"]);
|
|
253
|
-
if (!compactBlock) return {};
|
|
254
|
-
const configuredApiKey = readStringProperty(compactBlock, "apiKey");
|
|
255
|
-
return {
|
|
256
|
-
apiKey: configuredApiKey,
|
|
257
|
-
apiKeyEnv: readStringProperty(compactBlock, "apiKeyEnv") ?? (!configuredApiKey ? readEnvReferenceProperty(compactBlock, "apiKey") : void 0),
|
|
258
|
-
baseUrl: readStringProperty(compactBlock, "baseUrl"),
|
|
259
|
-
model: readStringProperty(compactBlock, "model"),
|
|
260
|
-
aggressiveness: readNumberProperty(compactBlock, "aggressiveness"),
|
|
261
|
-
maxOutputTokens: readNumberProperty(compactBlock, "maxOutputTokens"),
|
|
262
|
-
minOutputTokens: readNumberProperty(compactBlock, "minOutputTokens"),
|
|
263
|
-
protectJson: readBooleanProperty(compactBlock, "protectJson")
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
function readAgentCompactConfigFromModule(config) {
|
|
267
|
-
const compact = config.agent?.compact;
|
|
268
|
-
if (!compact) return {};
|
|
269
|
-
return {
|
|
270
|
-
apiKey: compact.apiKey,
|
|
271
|
-
apiKeyEnv: compact.apiKeyEnv,
|
|
272
|
-
baseUrl: compact.baseUrl,
|
|
273
|
-
model: compact.model,
|
|
274
|
-
aggressiveness: compact.aggressiveness,
|
|
275
|
-
maxOutputTokens: compact.maxOutputTokens,
|
|
276
|
-
minOutputTokens: compact.minOutputTokens,
|
|
277
|
-
protectJson: compact.protectJson
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
function mergeAgentCompactOptions(defaults, overrides) {
|
|
281
|
-
return {
|
|
282
|
-
...defaults,
|
|
283
|
-
...Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== void 0))
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
function normalizeTokenBudget(value) {
|
|
287
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
288
|
-
return Math.max(1, Math.ceil(value));
|
|
289
|
-
}
|
|
290
|
-
function readPageTokenBudget(pagePath) {
|
|
291
|
-
const { data } = matter(readFileSync(pagePath, "utf-8"));
|
|
292
|
-
return normalizeTokenBudget(data.agent?.tokenBudget);
|
|
293
|
-
}
|
|
294
|
-
function buildCompactionSettingsHash(options) {
|
|
295
|
-
return hashGeneratedAgentContent(JSON.stringify({
|
|
296
|
-
model: options.model ?? DEFAULT_TTC_MODEL,
|
|
297
|
-
aggressiveness: options.aggressiveness ?? DEFAULT_TTC_AGGRESSIVENESS,
|
|
298
|
-
maxOutputTokens: options.maxOutputTokens ?? null,
|
|
299
|
-
minOutputTokens: options.minOutputTokens ?? null,
|
|
300
|
-
protectJson: options.protectJson ?? null
|
|
301
|
-
}));
|
|
302
|
-
}
|
|
303
|
-
function buildPageOptions(defaults, pagePath) {
|
|
304
|
-
const tokenBudget = readPageTokenBudget(pagePath);
|
|
305
|
-
const pageOptions = mergeAgentCompactOptions(defaults, { maxOutputTokens: tokenBudget });
|
|
306
|
-
if (pageOptions.minOutputTokens !== void 0 && pageOptions.maxOutputTokens !== void 0 && pageOptions.minOutputTokens > pageOptions.maxOutputTokens) pageOptions.minOutputTokens = pageOptions.maxOutputTokens;
|
|
307
|
-
return {
|
|
308
|
-
pageOptions,
|
|
309
|
-
tokenBudget
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
function buildResolvedPageSourceDocument(page) {
|
|
313
|
-
return renderDocsMarkdownDocument({
|
|
314
|
-
...page,
|
|
315
|
-
agentRawContent: void 0
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
function buildAgentSourceDocument(page) {
|
|
319
|
-
if (typeof page.agentRawContent === "string") return page.agentRawContent;
|
|
320
|
-
return renderDocsMarkdownDocument(page);
|
|
321
|
-
}
|
|
322
|
-
function readCurrentAgentDocument(target) {
|
|
323
|
-
if (!target.hasAgentFile || !existsSync(target.agentPath)) return void 0;
|
|
324
|
-
return parseGeneratedAgentDocument(readFileSync(target.agentPath, "utf-8"));
|
|
325
|
-
}
|
|
326
|
-
function shouldCompactChangedAgentFile(target) {
|
|
327
|
-
const currentDocument = readCurrentAgentDocument(target);
|
|
328
|
-
if (!currentDocument) return false;
|
|
329
|
-
if (!currentDocument.provenance) return true;
|
|
330
|
-
if (currentDocument.provenance.sourceKind !== "agent-md") return false;
|
|
331
|
-
return hashGeneratedAgentContent(currentDocument.content) !== currentDocument.provenance.outputHash;
|
|
332
|
-
}
|
|
333
|
-
function resolveSourceKindForCompaction(target, currentDocument) {
|
|
334
|
-
if (!target.hasAgentFile) return "resolved-page";
|
|
335
|
-
if (currentDocument?.provenance?.sourceKind === "resolved-page") return "resolved-page";
|
|
336
|
-
return "agent-md";
|
|
337
|
-
}
|
|
338
|
-
function buildSourceDocumentForCompaction(page, sourceKind) {
|
|
339
|
-
return sourceKind === "resolved-page" ? buildResolvedPageSourceDocument(page) : buildAgentSourceDocument(page);
|
|
340
|
-
}
|
|
341
|
-
function buildGeneratedAgentProvenance(sourceKind, sourceDocument, output, pageOptions) {
|
|
342
|
-
return {
|
|
343
|
-
version: GENERATED_AGENT_PROVENANCE_VERSION,
|
|
344
|
-
sourceKind,
|
|
345
|
-
sourceHash: hashGeneratedAgentContent(sourceDocument),
|
|
346
|
-
settingsHash: buildCompactionSettingsHash(pageOptions),
|
|
347
|
-
outputHash: hashGeneratedAgentContent(output),
|
|
348
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
function inspectAgentCompactionState(page, target, defaults) {
|
|
352
|
-
const { pageOptions, tokenBudget } = buildPageOptions(defaults, target.pagePath);
|
|
353
|
-
const currentDocument = readCurrentAgentDocument(target);
|
|
354
|
-
if (!currentDocument) return {
|
|
355
|
-
status: "missing",
|
|
356
|
-
sourceKind: "resolved-page",
|
|
357
|
-
pageOptions,
|
|
358
|
-
sourceDocument: buildResolvedPageSourceDocument(page),
|
|
359
|
-
tokenBudget
|
|
360
|
-
};
|
|
361
|
-
const sourceKind = resolveSourceKindForCompaction(target, currentDocument);
|
|
362
|
-
const sourceDocument = buildSourceDocumentForCompaction(page, sourceKind);
|
|
363
|
-
if (!currentDocument.provenance) return {
|
|
364
|
-
status: "unknown",
|
|
365
|
-
sourceKind,
|
|
366
|
-
pageOptions,
|
|
367
|
-
sourceDocument,
|
|
368
|
-
tokenBudget
|
|
369
|
-
};
|
|
370
|
-
const outputModified = hashGeneratedAgentContent(currentDocument.content) !== currentDocument.provenance.outputHash;
|
|
371
|
-
if (currentDocument.provenance.sourceKind === "agent-md") return {
|
|
372
|
-
status: outputModified ? "modified" : "unknown",
|
|
373
|
-
sourceKind,
|
|
374
|
-
pageOptions,
|
|
375
|
-
sourceDocument,
|
|
376
|
-
provenance: currentDocument.provenance,
|
|
377
|
-
tokenBudget
|
|
378
|
-
};
|
|
379
|
-
const sourceChanged = hashGeneratedAgentContent(sourceDocument) !== currentDocument.provenance.sourceHash;
|
|
380
|
-
const settingsChanged = buildCompactionSettingsHash(pageOptions) !== currentDocument.provenance.settingsHash;
|
|
381
|
-
return {
|
|
382
|
-
status: outputModified && (sourceChanged || settingsChanged) ? "stale-modified" : outputModified ? "modified" : sourceChanged || settingsChanged ? "stale" : "fresh",
|
|
383
|
-
sourceKind,
|
|
384
|
-
pageOptions,
|
|
385
|
-
sourceDocument,
|
|
386
|
-
provenance: currentDocument.provenance,
|
|
387
|
-
tokenBudget
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
function protectForCompression(input) {
|
|
391
|
-
const segments = [];
|
|
392
|
-
const stash = (value) => {
|
|
393
|
-
const token = `__TTC_SAFE_${segments.length}__`;
|
|
394
|
-
segments.push(value);
|
|
395
|
-
return token;
|
|
396
|
-
};
|
|
397
|
-
let result = input;
|
|
398
|
-
result = result.replace(/```[\s\S]*?```/g, stash);
|
|
399
|
-
result = result.replace(/\[[^\]]+\]\([^)]+\)/g, stash);
|
|
400
|
-
result = result.replace(/`[^`\n]+`/g, stash);
|
|
401
|
-
result = result.replace(/^(URL|Description|Related):[^\n]*$/gm, stash);
|
|
402
|
-
result = result.replace(/https?:\/\/[^\s)]+/g, stash);
|
|
403
|
-
for (let index = 0; index < segments.length; index += 1) result = result.replace(`__TTC_SAFE_${index}__`, `<ttc_safe>${segments[index]}</ttc_safe>`);
|
|
404
|
-
return result;
|
|
405
|
-
}
|
|
406
|
-
function sanitizeCompressedOutput(output) {
|
|
407
|
-
return output.replace(/<\/?ttc_safe>/g, "");
|
|
408
|
-
}
|
|
409
|
-
async function compressDocument(input, options) {
|
|
410
|
-
const apiKey = resolveCompressionApiKey(options.apiKey, options.apiKeyEnv);
|
|
411
|
-
const endpoint = resolveCompressionEndpoint(options.baseUrl);
|
|
412
|
-
const aggressiveness = options.aggressiveness ?? DEFAULT_TTC_AGGRESSIVENESS;
|
|
413
|
-
if (aggressiveness < 0 || aggressiveness > 1) throw new Error("Aggressiveness must be between 0.0 and 1.0.");
|
|
414
|
-
const payload = {
|
|
415
|
-
model: options.model ?? DEFAULT_TTC_MODEL,
|
|
416
|
-
input: protectForCompression(input),
|
|
417
|
-
compression_settings: {
|
|
418
|
-
aggressiveness,
|
|
419
|
-
...options.maxOutputTokens !== void 0 ? { max_output_tokens: options.maxOutputTokens } : {},
|
|
420
|
-
...options.minOutputTokens !== void 0 ? { min_output_tokens: options.minOutputTokens } : {},
|
|
421
|
-
...options.protectJson !== void 0 ? { protect_json: options.protectJson } : {}
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
const response = await fetch(endpoint, {
|
|
425
|
-
method: "POST",
|
|
426
|
-
headers: {
|
|
427
|
-
Authorization: `Bearer ${apiKey}`,
|
|
428
|
-
"Content-Type": "application/json"
|
|
429
|
-
},
|
|
430
|
-
body: JSON.stringify(payload)
|
|
431
|
-
});
|
|
432
|
-
if (!response.ok) {
|
|
433
|
-
const body = await response.text();
|
|
434
|
-
throw new Error(`Token Company request failed (${response.status}): ${body || response.statusText}`);
|
|
435
|
-
}
|
|
436
|
-
const result = await response.json();
|
|
437
|
-
const sanitizedOutput = typeof result.output === "string" ? sanitizeCompressedOutput(result.output) : result.output;
|
|
438
|
-
if (typeof sanitizedOutput !== "string" || sanitizedOutput.trim().length === 0) throw new Error("Token Company response did not include a compressed output.");
|
|
439
|
-
return {
|
|
440
|
-
...result,
|
|
441
|
-
output: sanitizedOutput
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
function resolveSelectedPages(pages, targets, entry, requested, includeAll) {
|
|
445
|
-
const compactableBySlug = new Map(targets.map((target) => [target.slug, target]));
|
|
446
|
-
if (includeAll) return targets.map((target) => {
|
|
447
|
-
const page = pages.find((candidate) => candidate.slug === target.slug);
|
|
448
|
-
return page ? {
|
|
449
|
-
page,
|
|
450
|
-
target
|
|
451
|
-
} : null;
|
|
452
|
-
}).filter((value) => value !== null);
|
|
453
|
-
const resolved = [];
|
|
454
|
-
const seen = /* @__PURE__ */ new Set();
|
|
455
|
-
for (const rawRequest of requested) {
|
|
456
|
-
const page = findDocsMarkdownPage(entry, pages, normalizeRequestedPage(entry, rawRequest));
|
|
457
|
-
if (!page) throw new Error(`Could not find a docs page for "${rawRequest}".`);
|
|
458
|
-
const target = compactableBySlug.get(page.slug);
|
|
459
|
-
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.`);
|
|
460
|
-
if (seen.has(target.slug)) continue;
|
|
461
|
-
seen.add(target.slug);
|
|
462
|
-
resolved.push({
|
|
463
|
-
page,
|
|
464
|
-
target
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
return resolved;
|
|
468
|
-
}
|
|
469
|
-
function filterChangedPages(rootDir, contentDir, selectedPages) {
|
|
470
|
-
const changedFiles = listGitChangedFiles(rootDir, contentDir);
|
|
471
|
-
return selectedPages.filter(({ target }) => {
|
|
472
|
-
const relativePagePath = normalizeGitRelativePath(path.relative(rootDir, target.pagePath));
|
|
473
|
-
if (changedFiles.has(relativePagePath)) return true;
|
|
474
|
-
const relativeAgentPath = normalizeGitRelativePath(path.relative(rootDir, target.agentPath));
|
|
475
|
-
return changedFiles.has(relativeAgentPath) && shouldCompactChangedAgentFile(target);
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
async function compactAgentDocs(options = {}) {
|
|
479
|
-
const rootDir = process.cwd();
|
|
480
|
-
const loadedEnv = loadProjectEnv(rootDir);
|
|
481
|
-
for (const [key, value] of Object.entries(loadedEnv)) if (process.env[key] === void 0) process.env[key] = value;
|
|
482
|
-
const loadedConfigModule = await loadDocsConfigModule(rootDir, options.configPath);
|
|
483
|
-
const configContent = readFileSync(loadedConfigModule?.path ?? resolveDocsConfigPath(rootDir, options.configPath), "utf-8");
|
|
484
|
-
const resolvedOptions = mergeAgentCompactOptions(mergeAgentCompactOptions(readAgentCompactConfig(configContent), loadedConfigModule?.config ? readAgentCompactConfigFromModule(loadedConfigModule.config) : {}), options);
|
|
485
|
-
const entry = normalizePathSegment(loadedConfigModule?.config.entry ?? readTopLevelStringProperty(configContent, "entry") ?? "docs") || "docs";
|
|
486
|
-
const contentDir = typeof loadedConfigModule?.config.contentDir === "string" ? loadedConfigModule.config.contentDir : resolveDocsContentDir(rootDir, configContent, entry);
|
|
487
|
-
const siteTitle = typeof loadedConfigModule?.config.nav?.title === "string" ? loadedConfigModule.config.nav.title : readNavTitle(configContent) ?? "Documentation";
|
|
488
|
-
if (resolvedOptions.all && resolvedOptions.pages && resolvedOptions.pages.length > 0) throw new Error("Use either --all or specific page arguments, not both.");
|
|
489
|
-
if (resolvedOptions.includeMissing && !resolvedOptions.stale) throw new Error("Use --include-missing together with --stale.");
|
|
490
|
-
const requestedPages = resolvedOptions.pages?.filter((value) => value.trim().length > 0) ?? [];
|
|
491
|
-
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.");
|
|
492
|
-
const pages = await createFilesystemDocsMcpSource({
|
|
493
|
-
rootDir,
|
|
494
|
-
entry,
|
|
495
|
-
contentDir,
|
|
496
|
-
siteTitle
|
|
497
|
-
}).getPages();
|
|
498
|
-
if (pages.length === 0) throw new Error(`No docs content was found under ${contentDir}.`);
|
|
499
|
-
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);
|
|
500
|
-
const filteredPages = resolvedOptions.changed ? filterChangedPages(rootDir, contentDir, selectedPages) : selectedPages;
|
|
501
|
-
if (filteredPages.length === 0) {
|
|
502
|
-
if (resolvedOptions.changed) {
|
|
503
|
-
console.log(pc.green("No changed docs pages needed compaction."));
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
throw new Error("No compactable docs pages matched the request.");
|
|
507
|
-
}
|
|
508
|
-
let created = 0;
|
|
509
|
-
let overwritten = 0;
|
|
510
|
-
let processed = 0;
|
|
511
|
-
let skippedFresh = 0;
|
|
512
|
-
let skippedModified = 0;
|
|
513
|
-
let skippedUnknown = 0;
|
|
514
|
-
let skippedMissing = 0;
|
|
515
|
-
const requestedExplicitPages = requestedPages.length > 0;
|
|
516
|
-
for (const { page, target } of filteredPages) {
|
|
517
|
-
const state = inspectAgentCompactionState(page, target, resolvedOptions);
|
|
518
|
-
if (resolvedOptions.stale) {
|
|
519
|
-
if (state.status === "fresh") {
|
|
520
|
-
skippedFresh += 1;
|
|
521
|
-
continue;
|
|
522
|
-
}
|
|
523
|
-
if (state.status === "modified" || state.status === "stale-modified") {
|
|
524
|
-
skippedModified += 1;
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
if (state.status === "unknown") {
|
|
528
|
-
skippedUnknown += 1;
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
if (state.status === "missing") {
|
|
532
|
-
if (!(resolvedOptions.includeMissing === true && (requestedExplicitPages || state.tokenBudget !== void 0))) {
|
|
533
|
-
skippedMissing += 1;
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
const compressed = await compressDocument(state.sourceDocument, state.pageOptions);
|
|
539
|
-
const nextContent = compressed.output.trimEnd();
|
|
540
|
-
const generatedDocument = serializeGeneratedAgentDocument(nextContent, buildGeneratedAgentProvenance(state.sourceKind, state.sourceDocument, nextContent, state.pageOptions));
|
|
541
|
-
console.log(pc.dim(`Compacting ${page.url} (${compressed.original_input_tokens ?? "?"} -> ${compressed.output_tokens ?? "?"} tokens)...`));
|
|
542
|
-
if (resolvedOptions.dryRun) continue;
|
|
543
|
-
mkdirSync(target.pageDir, { recursive: true });
|
|
544
|
-
writeFileSync(target.agentPath, generatedDocument, "utf-8");
|
|
545
|
-
if (target.hasAgentFile) overwritten += 1;
|
|
546
|
-
else created += 1;
|
|
547
|
-
processed += 1;
|
|
548
|
-
}
|
|
549
|
-
if (resolvedOptions.dryRun) processed = filteredPages.length - skippedFresh - skippedModified - skippedUnknown - skippedMissing;
|
|
550
|
-
if (resolvedOptions.stale && processed === 0) {
|
|
551
|
-
console.log(pc.green("No stale generated agent.md files needed updates."));
|
|
552
|
-
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"}.`));
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
const summaryPrefix = resolvedOptions.dryRun ? "Dry run complete" : "Compaction complete";
|
|
556
|
-
console.log(pc.green(`${summaryPrefix}: ${processed} page${processed === 1 ? "" : "s"} processed` + (resolvedOptions.dryRun ? "." : ` (${created} created, ${overwritten} overwritten).`)));
|
|
557
|
-
if (resolvedOptions.stale) console.log(pc.dim(`Skipped ${skippedFresh} fresh, ${skippedModified} modified, ${skippedUnknown} unknown, and ${skippedMissing} missing page${skippedFresh + skippedModified + skippedUnknown + skippedMissing === 1 ? "" : "s"}.`));
|
|
558
|
-
}
|
|
559
|
-
function printAgentCompactHelp() {
|
|
560
|
-
console.log(`
|
|
561
|
-
${pc.bold("docs agent compact")} — Generate sibling ${pc.cyan("agent.md")} files by compacting resolved docs pages.
|
|
562
|
-
|
|
563
|
-
${pc.dim("Usage:")}
|
|
564
|
-
npx @farming-labs/docs@latest ${pc.cyan("agent compact")} ${pc.dim("[page ...]")}
|
|
565
|
-
|
|
566
|
-
${pc.dim("Examples:")}
|
|
567
|
-
${pc.cyan("npx @farming-labs/docs@latest agent compact installation configuration")}
|
|
568
|
-
${pc.cyan("npx @farming-labs/docs@latest agent compact /docs/installation")}
|
|
569
|
-
${pc.cyan("npx @farming-labs/docs@latest agent compact --page installation --page configuration")}
|
|
570
|
-
${pc.cyan("npx @farming-labs/docs@latest agent compact --all")}
|
|
571
|
-
${pc.cyan("npx @farming-labs/docs@latest agent compact --changed")}
|
|
572
|
-
${pc.cyan("npx @farming-labs/docs@latest agent compact --stale")}
|
|
573
|
-
${pc.cyan("npx @farming-labs/docs@latest agent compact --stale --include-missing")}
|
|
574
|
-
|
|
575
|
-
${pc.dim("Per-page override:")}
|
|
576
|
-
Add ${pc.cyan("agent.tokenBudget")} to a page frontmatter block to override the compact output target for that page.
|
|
577
|
-
|
|
578
|
-
${pc.dim("Options:")}
|
|
579
|
-
${pc.cyan("--all")} Compact every folder-based docs page under the configured contentDir
|
|
580
|
-
${pc.cyan("--page <slug|path>")} Add a page explicitly (repeatable); positional page args work too
|
|
581
|
-
${pc.cyan("--changed")} Compact only docs pages changed in the current git working tree
|
|
582
|
-
${pc.cyan("--stale")} Re-compact only stale generated agent.md files
|
|
583
|
-
${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")}
|
|
584
|
-
${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
|
|
585
|
-
${pc.cyan("--api-key <key>")} Token Company API key (or set ${pc.dim("TOKEN_COMPANY_API_KEY")})
|
|
586
|
-
${pc.cyan("--api-key-env <name>")} Custom env var name for the Token Company API key
|
|
587
|
-
${pc.cyan("--base-url <url>")} Override the Token Company API base URL (useful for tests)
|
|
588
|
-
${pc.cyan("--model <name>")} Compression model (${pc.dim("bear-1.2")} by default)
|
|
589
|
-
${pc.cyan("--aggressiveness <0-1>")} Compression intensity (${pc.dim("0.3")} by default)
|
|
590
|
-
${pc.cyan("--max-output-tokens <n>")} Pass through to Token Company compression settings
|
|
591
|
-
${pc.cyan("--min-output-tokens <n>")} Pass through to Token Company compression settings
|
|
592
|
-
${pc.cyan("--protect-json <bool>")} Preserve JSON objects during compression
|
|
593
|
-
${pc.cyan("--dry-run")} Resolve pages and call the compressor without writing files
|
|
594
|
-
`);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
//#endregion
|
|
598
|
-
//#region src/cli/agents.ts
|
|
599
|
-
const AGENTS_GENERATED_MARKER = "<!-- Generated by @farming-labs/docs agents generate. -->";
|
|
600
|
-
function parseInlineFlag(arg) {
|
|
601
|
-
const [rawKey, value] = arg.slice(2).split("=", 2);
|
|
602
|
-
return {
|
|
603
|
-
key: rawKey.trim(),
|
|
604
|
-
value
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
function parseAgentsGenerateArgs(argv) {
|
|
608
|
-
const parsed = {};
|
|
609
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
610
|
-
const arg = argv[index];
|
|
611
|
-
if (arg === "--help" || arg === "-h") {
|
|
612
|
-
parsed.help = true;
|
|
613
|
-
continue;
|
|
614
|
-
}
|
|
615
|
-
if (arg === "--force") {
|
|
616
|
-
parsed.force = true;
|
|
617
|
-
continue;
|
|
618
|
-
}
|
|
619
|
-
if (arg === "--check") {
|
|
620
|
-
parsed.check = true;
|
|
621
|
-
continue;
|
|
622
|
-
}
|
|
623
|
-
if (arg.startsWith("--config=")) {
|
|
624
|
-
const value = parseInlineFlag(arg).value;
|
|
625
|
-
if (!value) throw new Error("Missing value for --config.");
|
|
626
|
-
parsed.configPath = value;
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
if (arg === "--config") {
|
|
630
|
-
const value = argv[index + 1];
|
|
631
|
-
if (!value || value.startsWith("--")) throw new Error("Missing value for --config.");
|
|
632
|
-
parsed.configPath = value;
|
|
633
|
-
index += 1;
|
|
634
|
-
continue;
|
|
635
|
-
}
|
|
636
|
-
if (arg.startsWith("--path=")) {
|
|
637
|
-
const value = parseInlineFlag(arg).value;
|
|
638
|
-
if (!value) throw new Error("Missing value for --path.");
|
|
639
|
-
parsed.path = value;
|
|
640
|
-
continue;
|
|
641
|
-
}
|
|
642
|
-
if (arg === "--path") {
|
|
643
|
-
const value = argv[index + 1];
|
|
644
|
-
if (!value || value.startsWith("--")) throw new Error("Missing value for --path.");
|
|
645
|
-
parsed.path = value;
|
|
646
|
-
index += 1;
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
if (!arg.startsWith("--") && !parsed.path) {
|
|
650
|
-
parsed.path = arg;
|
|
651
|
-
continue;
|
|
652
|
-
}
|
|
653
|
-
throw new Error(`Unknown agents generate flag: ${arg}.`);
|
|
654
|
-
}
|
|
655
|
-
return parsed;
|
|
656
|
-
}
|
|
657
|
-
function readTopLevelBooleanProperty(content, key) {
|
|
658
|
-
const match = content.match(new RegExp(`\\b${key}\\b\\s*:\\s*(true|false)`));
|
|
659
|
-
return match ? match[1] === "true" : void 0;
|
|
660
|
-
}
|
|
661
|
-
function readLlmsBaseUrlFromConfig(content, config) {
|
|
662
|
-
if (config?.llmsTxt && typeof config.llmsTxt === "object") return config.llmsTxt.baseUrl;
|
|
663
|
-
const block = extractNestedObjectLiteral(content, ["llmsTxt"]);
|
|
664
|
-
return block ? readStringProperty(block, "baseUrl") : void 0;
|
|
665
|
-
}
|
|
666
|
-
function readSitemapBaseUrlFromConfig(content, config) {
|
|
667
|
-
if (config?.sitemap && typeof config.sitemap === "object") return config.sitemap.baseUrl;
|
|
668
|
-
const block = extractNestedObjectLiteral(content, ["sitemap"]);
|
|
669
|
-
return block ? readStringProperty(block, "baseUrl") : void 0;
|
|
670
|
-
}
|
|
671
|
-
function readRobotsConfigFromStatic(content) {
|
|
672
|
-
const topLevelBoolean = readTopLevelBooleanProperty(content, "robots");
|
|
673
|
-
if (typeof topLevelBoolean === "boolean") return topLevelBoolean;
|
|
674
|
-
const block = extractNestedObjectLiteral(content, ["robots"]);
|
|
675
|
-
if (!block) return void 0;
|
|
676
|
-
return { enabled: readBooleanProperty(block, "enabled") ?? true };
|
|
677
|
-
}
|
|
678
|
-
function resolvePublicDir(rootDir) {
|
|
679
|
-
if (detectFramework(rootDir) === "sveltekit") return path.join(rootDir, "static");
|
|
680
|
-
return path.join(rootDir, "public");
|
|
681
|
-
}
|
|
682
|
-
function resolveAgentsPath(rootDir, options) {
|
|
683
|
-
if (options.path) return path.isAbsolute(options.path) ? options.path : path.resolve(rootDir, options.path);
|
|
684
|
-
return path.join(rootDir, "AGENTS.md");
|
|
685
|
-
}
|
|
686
|
-
function publicFilePath(rootDir, route) {
|
|
687
|
-
return path.join(resolvePublicDir(rootDir), route.replace(/^\/+/, ""));
|
|
688
|
-
}
|
|
689
|
-
function normalizeGeneratedContent(content) {
|
|
690
|
-
return `${AGENTS_GENERATED_MARKER}\n${content.trimEnd()}\n`;
|
|
691
|
-
}
|
|
692
|
-
function normalizeCustomContent(content) {
|
|
693
|
-
return content.endsWith("\n") ? content : `${content}\n`;
|
|
694
|
-
}
|
|
695
|
-
function isManagedAgentsFile(content) {
|
|
696
|
-
return content.includes(AGENTS_GENERATED_MARKER);
|
|
697
|
-
}
|
|
698
|
-
function writeIfNeeded(filePath, content, options) {
|
|
699
|
-
const current = existsSync(filePath) ? readFileSync(filePath, "utf-8") : void 0;
|
|
700
|
-
if (current === content) return "current";
|
|
701
|
-
if (current !== void 0 && !options.force && !isManagedAgentsFile(current)) return "kept";
|
|
702
|
-
if (options.check) return "changed";
|
|
703
|
-
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
704
|
-
writeFileSync(filePath, content, "utf-8");
|
|
705
|
-
return "changed";
|
|
706
|
-
}
|
|
707
|
-
function resolveOpenApiDiscovery(config) {
|
|
708
|
-
const apiReference = resolveApiReferenceConfig(config?.apiReference);
|
|
709
|
-
if (!apiReference.enabled) return { enabled: false };
|
|
710
|
-
return {
|
|
711
|
-
enabled: true,
|
|
712
|
-
url: DEFAULT_OPENAPI_SCHEMA_ROUTE,
|
|
713
|
-
source: apiReference.specUrl ? "configured" : "generated",
|
|
714
|
-
specUrl: apiReference.specUrl,
|
|
715
|
-
apiReferencePath: `/${apiReference.path}`
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
async function generateAgents(options = {}) {
|
|
719
|
-
const rootDir = process.cwd();
|
|
720
|
-
const loadedConfigModule = await loadDocsConfigModule(rootDir, options.configPath);
|
|
721
|
-
const configContent = readFileSync(loadedConfigModule?.path ?? resolveDocsConfigPath(rootDir, options.configPath), "utf-8");
|
|
722
|
-
const config = loadedConfigModule?.config;
|
|
723
|
-
const entry = config?.entry ?? readTopLevelStringProperty(configContent, "entry") ?? "docs";
|
|
724
|
-
const siteTitle = typeof config?.nav?.title === "string" ? config.nav.title : readNavTitle(configContent) ?? "Documentation";
|
|
725
|
-
const siteDescription = typeof config?.metadata?.description === "string" ? config.metadata.description : void 0;
|
|
726
|
-
const baseUrl = readLlmsBaseUrlFromConfig(configContent, config) ?? readSitemapBaseUrlFromConfig(configContent, config);
|
|
727
|
-
const llmsTxt = config?.llmsTxt;
|
|
728
|
-
const llmsEnabled = llmsTxt === false ? false : typeof llmsTxt === "object" ? llmsTxt.enabled ?? true : true;
|
|
729
|
-
const llmsConfig = typeof llmsTxt === "object" ? llmsTxt : void 0;
|
|
730
|
-
const robotsInput = config?.robots ?? readRobotsConfigFromStatic(configContent) ?? true;
|
|
731
|
-
const rootAgentsPath = resolveAgentsPath(rootDir, options);
|
|
732
|
-
const generatedContent = normalizeGeneratedContent(renderDocsAgentsDocument({
|
|
733
|
-
origin: baseUrl ?? "http://localhost",
|
|
734
|
-
entry,
|
|
735
|
-
search: config?.search,
|
|
736
|
-
mcp: resolveDocsMcpConfig(config?.mcp),
|
|
737
|
-
feedback: resolveDocsAgentFeedbackConfig(config?.feedback),
|
|
738
|
-
llms: {
|
|
739
|
-
enabled: llmsEnabled,
|
|
740
|
-
baseUrl,
|
|
741
|
-
siteTitle,
|
|
742
|
-
siteDescription,
|
|
743
|
-
maxChars: llmsConfig?.maxChars,
|
|
744
|
-
sections: llmsConfig?.sections
|
|
745
|
-
},
|
|
746
|
-
sitemap: config?.sitemap,
|
|
747
|
-
robots: robotsInput,
|
|
748
|
-
openapi: resolveOpenApiDiscovery(config),
|
|
749
|
-
markdown: { acceptHeader: false }
|
|
750
|
-
}));
|
|
751
|
-
const existingRoot = existsSync(rootAgentsPath) ? readFileSync(rootAgentsPath, "utf-8") : null;
|
|
752
|
-
const sourceContent = existingRoot !== null && !options.force && !isManagedAgentsFile(existingRoot) ? normalizeCustomContent(existingRoot) : generatedContent;
|
|
753
|
-
const writes = [];
|
|
754
|
-
const kept = [];
|
|
755
|
-
const rootStatus = writeIfNeeded(rootAgentsPath, sourceContent, options);
|
|
756
|
-
if (rootStatus === "changed") writes.push(rootAgentsPath);
|
|
757
|
-
if (rootStatus === "kept") kept.push(rootAgentsPath);
|
|
758
|
-
for (const route of [
|
|
759
|
-
DEFAULT_AGENTS_MD_ROUTE,
|
|
760
|
-
DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE,
|
|
761
|
-
DEFAULT_AGENT_MD_ROUTE,
|
|
762
|
-
DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE
|
|
763
|
-
]) {
|
|
764
|
-
const filePath = publicFilePath(rootDir, route);
|
|
765
|
-
const status = writeIfNeeded(filePath, sourceContent, options);
|
|
766
|
-
if (status === "changed") writes.push(filePath);
|
|
767
|
-
if (status === "kept") kept.push(filePath);
|
|
768
|
-
}
|
|
769
|
-
if (options.check && writes.length > 0) throw new Error("AGENTS.md output is stale. Run `docs agents generate` to update it.");
|
|
770
|
-
const relativeRoot = path.relative(rootDir, rootAgentsPath).replace(/\\/g, "/");
|
|
771
|
-
console.log(writes.length > 0 ? pc.green(`Generated agent instructions at ${relativeRoot}.`) : pc.green(`Agent instructions are current at ${relativeRoot}.`));
|
|
772
|
-
for (const filePath of writes) {
|
|
773
|
-
if (filePath === rootAgentsPath) continue;
|
|
774
|
-
console.log(pc.dim(path.relative(rootDir, filePath).replace(/\\/g, "/")));
|
|
775
|
-
}
|
|
776
|
-
for (const filePath of kept) console.log(pc.yellow(`Kept user-owned ${path.relative(rootDir, filePath).replace(/\\/g, "/")}.`));
|
|
777
|
-
}
|
|
778
|
-
function printAgentsGenerateHelp() {
|
|
779
|
-
console.log(`
|
|
780
|
-
${pc.bold("docs agents generate")} — Generate AGENTS.md instructions for coding agents.
|
|
781
|
-
|
|
782
|
-
${pc.dim("Usage:")}
|
|
783
|
-
pnpm exec docs ${pc.cyan("agents generate")} ${pc.dim("[path]")}
|
|
784
|
-
|
|
785
|
-
${pc.dim("Options:")}
|
|
786
|
-
${pc.cyan("--path <path>")} Write the root instructions file; defaults to ${pc.dim("AGENTS.md")}
|
|
787
|
-
${pc.cyan("--force")} Replace existing AGENTS.md/static files
|
|
788
|
-
${pc.cyan("--check")} Fail if generated output would change
|
|
789
|
-
${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
|
|
790
|
-
${pc.cyan("-h, --help")} Show this help message
|
|
791
|
-
`);
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
//#endregion
|
|
795
|
-
export { readPageTokenBudget as a, generateAgents, printAgentCompactHelp as i, inspectAgentCompactionState as n, scanDocsPageTargets as o, parseAgentsGenerateArgs, printAgentsGenerateHelp, parseAgentCompactArgs as r, compactAgentDocs as t };
|