docs-ready 0.3.0 → 0.4.0
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/chunk-QI2AROM3.js +153 -0
- package/dist/chunk-QI2AROM3.js.map +1 -0
- package/dist/{generate-LDVEG2WV.js → generate-N54SEQ7E.js} +23 -167
- package/dist/generate-N54SEQ7E.js.map +1 -0
- package/dist/guard-EKGLQRED.js +368 -0
- package/dist/guard-EKGLQRED.js.map +1 -0
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/generate-LDVEG2WV.js.map +0 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/config.ts
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import YAML from "yaml";
|
|
7
|
+
var DEFAULTS = {
|
|
8
|
+
docs: {
|
|
9
|
+
dir: "./docs",
|
|
10
|
+
include: ["**/*.md", "**/*.mdx"],
|
|
11
|
+
exclude: ["**/node_modules/**", "**/_*"]
|
|
12
|
+
},
|
|
13
|
+
generate: {
|
|
14
|
+
llms_txt: true,
|
|
15
|
+
llms_full_txt: true,
|
|
16
|
+
ai_context: true,
|
|
17
|
+
output_dir: "./build"
|
|
18
|
+
},
|
|
19
|
+
guard: {
|
|
20
|
+
npm_packages: [],
|
|
21
|
+
github_releases: [],
|
|
22
|
+
endpoints: [],
|
|
23
|
+
readme_scans: [],
|
|
24
|
+
workflow: {
|
|
25
|
+
enabled: true,
|
|
26
|
+
schedule: "0 9 */3 * *",
|
|
27
|
+
create_issues: true,
|
|
28
|
+
labels: ["ai-context-review", "documentation"]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
deploy: {
|
|
32
|
+
platform: "none",
|
|
33
|
+
cors: { enabled: true, origins: ["*"] }
|
|
34
|
+
},
|
|
35
|
+
validate: {
|
|
36
|
+
max_tokens: 15e4,
|
|
37
|
+
check_links: true,
|
|
38
|
+
check_coverage: true,
|
|
39
|
+
coverage_threshold: 0.95
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var CONFIG_FILES = [
|
|
43
|
+
{ name: ".docs-ready.yaml", parser: "yaml" },
|
|
44
|
+
{ name: ".docs-ready.yml", parser: "yaml" },
|
|
45
|
+
{ name: ".docs-ready.json", parser: "json" },
|
|
46
|
+
{ name: ".docs-ready.toml", parser: "toml" }
|
|
47
|
+
];
|
|
48
|
+
async function loadConfig(dir, configPath) {
|
|
49
|
+
let raw;
|
|
50
|
+
if (configPath) {
|
|
51
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
52
|
+
raw = parseConfig(content, configPath);
|
|
53
|
+
} else {
|
|
54
|
+
for (const { name, parser } of CONFIG_FILES) {
|
|
55
|
+
const fullPath = path.join(dir, name);
|
|
56
|
+
try {
|
|
57
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
58
|
+
if (parser === "toml") {
|
|
59
|
+
throw new Error("TOML support requires a TOML parser. Use .yaml or .json instead.");
|
|
60
|
+
}
|
|
61
|
+
raw = parseConfig(content, fullPath);
|
|
62
|
+
break;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (err.code === "ENOENT") continue;
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!raw) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"No config file found. Run `docs-ready init` to create one, or create .docs-ready.yaml manually."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
validate(raw);
|
|
75
|
+
return applyDefaults(raw);
|
|
76
|
+
}
|
|
77
|
+
function parseConfig(content, filePath) {
|
|
78
|
+
if (filePath.endsWith(".json")) {
|
|
79
|
+
return JSON.parse(content);
|
|
80
|
+
}
|
|
81
|
+
const parsed = YAML.parse(content);
|
|
82
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
83
|
+
throw new Error(`Failed to parse config file: ${filePath}`);
|
|
84
|
+
}
|
|
85
|
+
return parsed;
|
|
86
|
+
}
|
|
87
|
+
function validate(raw) {
|
|
88
|
+
const missing = [];
|
|
89
|
+
if (!raw.title || typeof raw.title !== "string") missing.push("title");
|
|
90
|
+
if (!raw.description || typeof raw.description !== "string") missing.push("description");
|
|
91
|
+
if (!raw.url || typeof raw.url !== "string") missing.push("url");
|
|
92
|
+
if (missing.length > 0) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Missing required config fields: ${missing.join(", ")}. Add them to your .docs-ready.yaml file.`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function applyDefaults(raw) {
|
|
99
|
+
const docs = raw.docs;
|
|
100
|
+
const generate = raw.generate;
|
|
101
|
+
const guard = raw.guard;
|
|
102
|
+
const deploy = raw.deploy;
|
|
103
|
+
const validateConf = raw.validate;
|
|
104
|
+
const guardWorkflow = guard?.workflow ?? {};
|
|
105
|
+
return {
|
|
106
|
+
title: raw.title,
|
|
107
|
+
description: raw.description,
|
|
108
|
+
url: raw.url,
|
|
109
|
+
docs: {
|
|
110
|
+
dir: docs?.dir ?? DEFAULTS.docs.dir,
|
|
111
|
+
include: docs?.include ?? DEFAULTS.docs.include,
|
|
112
|
+
exclude: docs?.exclude ?? DEFAULTS.docs.exclude
|
|
113
|
+
},
|
|
114
|
+
generate: {
|
|
115
|
+
llms_txt: generate?.llms_txt ?? DEFAULTS.generate.llms_txt,
|
|
116
|
+
llms_full_txt: generate?.llms_full_txt ?? DEFAULTS.generate.llms_full_txt,
|
|
117
|
+
ai_context: generate?.ai_context ?? DEFAULTS.generate.ai_context,
|
|
118
|
+
output_dir: generate?.output_dir ?? DEFAULTS.generate.output_dir,
|
|
119
|
+
sections: generate?.sections,
|
|
120
|
+
ai_context_config: generate?.ai_context_config
|
|
121
|
+
},
|
|
122
|
+
guard: {
|
|
123
|
+
npm_packages: guard?.npm_packages ?? DEFAULTS.guard.npm_packages,
|
|
124
|
+
github_releases: guard?.github_releases ?? DEFAULTS.guard.github_releases,
|
|
125
|
+
endpoints: guard?.endpoints ?? DEFAULTS.guard.endpoints,
|
|
126
|
+
readme_scans: guard?.readme_scans ?? DEFAULTS.guard.readme_scans,
|
|
127
|
+
workflow: {
|
|
128
|
+
enabled: guardWorkflow.enabled ?? DEFAULTS.guard.workflow.enabled,
|
|
129
|
+
schedule: guardWorkflow.schedule ?? DEFAULTS.guard.workflow.schedule,
|
|
130
|
+
create_issues: guardWorkflow.create_issues ?? DEFAULTS.guard.workflow.create_issues,
|
|
131
|
+
labels: guardWorkflow.labels ?? DEFAULTS.guard.workflow.labels
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
deploy: {
|
|
135
|
+
platform: deploy?.platform ?? DEFAULTS.deploy.platform,
|
|
136
|
+
cors: {
|
|
137
|
+
enabled: deploy?.cors?.enabled ?? DEFAULTS.deploy.cors.enabled,
|
|
138
|
+
origins: deploy?.cors?.origins ?? DEFAULTS.deploy.cors.origins
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
validate: {
|
|
142
|
+
max_tokens: validateConf?.max_tokens ?? DEFAULTS.validate.max_tokens,
|
|
143
|
+
check_links: validateConf?.check_links ?? DEFAULTS.validate.check_links,
|
|
144
|
+
check_coverage: validateConf?.check_coverage ?? DEFAULTS.validate.check_coverage,
|
|
145
|
+
coverage_threshold: validateConf?.coverage_threshold ?? DEFAULTS.validate.coverage_threshold
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export {
|
|
151
|
+
loadConfig
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=chunk-QI2AROM3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/config.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport YAML from \"yaml\";\n\nexport interface DocsReadyConfig {\n title: string;\n description: string;\n url: string;\n docs: {\n dir: string;\n include: string[];\n exclude: string[];\n };\n generate: {\n llms_txt: boolean;\n llms_full_txt: boolean;\n ai_context: boolean;\n output_dir: string;\n sections?: Array<{ title: string; patterns: string[] }>;\n ai_context_config?: {\n key_pages?: Array<{ path: string; section: string }>;\n extra_sections?: Array<{ title: string; source?: string; auto_extract?: boolean }>;\n };\n };\n guard: {\n npm_packages: Array<{ name: string; label: string }>;\n github_releases: Array<{ repo: string; label: string }>;\n endpoints: Array<{ url: string; label: string; expected_status?: number[] }>;\n readme_scans: Array<{ repo: string; keywords: string[] }>;\n workflow: {\n enabled: boolean;\n schedule: string;\n create_issues: boolean;\n labels: string[];\n };\n };\n deploy: {\n platform: \"vercel\" | \"netlify\" | \"cloudflare\" | \"none\";\n cors: { enabled: boolean; origins: string[] };\n };\n validate: {\n max_tokens: number;\n check_links: boolean;\n check_coverage: boolean;\n coverage_threshold: number;\n };\n}\n\nconst DEFAULTS: Omit<DocsReadyConfig, \"title\" | \"description\" | \"url\"> = {\n docs: {\n dir: \"./docs\",\n include: [\"**/*.md\", \"**/*.mdx\"],\n exclude: [\"**/node_modules/**\", \"**/_*\"],\n },\n generate: {\n llms_txt: true,\n llms_full_txt: true,\n ai_context: true,\n output_dir: \"./build\",\n },\n guard: {\n npm_packages: [],\n github_releases: [],\n endpoints: [],\n readme_scans: [],\n workflow: {\n enabled: true,\n schedule: \"0 9 */3 * *\",\n create_issues: true,\n labels: [\"ai-context-review\", \"documentation\"],\n },\n },\n deploy: {\n platform: \"none\",\n cors: { enabled: true, origins: [\"*\"] },\n },\n validate: {\n max_tokens: 150000,\n check_links: true,\n check_coverage: true,\n coverage_threshold: 0.95,\n },\n};\n\nconst CONFIG_FILES = [\n { name: \".docs-ready.yaml\", parser: \"yaml\" as const },\n { name: \".docs-ready.yml\", parser: \"yaml\" as const },\n { name: \".docs-ready.json\", parser: \"json\" as const },\n { name: \".docs-ready.toml\", parser: \"toml\" as const },\n];\n\nexport async function loadConfig(dir: string, configPath?: string): Promise<DocsReadyConfig> {\n let raw: Record<string, unknown> | undefined;\n\n if (configPath) {\n const content = await fs.readFile(configPath, \"utf-8\");\n raw = parseConfig(content, configPath);\n } else {\n for (const { name, parser } of CONFIG_FILES) {\n const fullPath = path.join(dir, name);\n try {\n const content = await fs.readFile(fullPath, \"utf-8\");\n if (parser === \"toml\") {\n throw new Error(\"TOML support requires a TOML parser. Use .yaml or .json instead.\");\n }\n raw = parseConfig(content, fullPath);\n break;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") continue;\n throw err;\n }\n }\n }\n\n if (!raw) {\n throw new Error(\n \"No config file found. Run `docs-ready init` to create one, or create .docs-ready.yaml manually.\"\n );\n }\n\n validate(raw);\n return applyDefaults(raw);\n}\n\nfunction parseConfig(content: string, filePath: string): Record<string, unknown> {\n if (filePath.endsWith(\".json\")) {\n return JSON.parse(content);\n }\n const parsed = YAML.parse(content);\n if (parsed === null || typeof parsed !== \"object\") {\n throw new Error(`Failed to parse config file: ${filePath}`);\n }\n return parsed;\n}\n\nfunction validate(raw: Record<string, unknown>): void {\n const missing: string[] = [];\n if (!raw.title || typeof raw.title !== \"string\") missing.push(\"title\");\n if (!raw.description || typeof raw.description !== \"string\") missing.push(\"description\");\n if (!raw.url || typeof raw.url !== \"string\") missing.push(\"url\");\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required config fields: ${missing.join(\", \")}. ` +\n `Add them to your .docs-ready.yaml file.`\n );\n }\n}\n\nfunction applyDefaults(raw: Record<string, unknown>): DocsReadyConfig {\n const docs = raw.docs as Record<string, unknown> | undefined;\n const generate = raw.generate as Record<string, unknown> | undefined;\n const guard = raw.guard as Record<string, unknown> | undefined;\n const deploy = raw.deploy as Record<string, unknown> | undefined;\n const validateConf = raw.validate as Record<string, unknown> | undefined;\n const guardWorkflow = (guard?.workflow as Record<string, unknown>) ?? {};\n\n return {\n title: raw.title as string,\n description: raw.description as string,\n url: raw.url as string,\n docs: {\n dir: (docs?.dir as string) ?? DEFAULTS.docs.dir,\n include: (docs?.include as string[]) ?? DEFAULTS.docs.include,\n exclude: (docs?.exclude as string[]) ?? DEFAULTS.docs.exclude,\n },\n generate: {\n llms_txt: (generate?.llms_txt as boolean) ?? DEFAULTS.generate.llms_txt,\n llms_full_txt: (generate?.llms_full_txt as boolean) ?? DEFAULTS.generate.llms_full_txt,\n ai_context: (generate?.ai_context as boolean) ?? DEFAULTS.generate.ai_context,\n output_dir: (generate?.output_dir as string) ?? DEFAULTS.generate.output_dir,\n sections: generate?.sections as DocsReadyConfig[\"generate\"][\"sections\"],\n ai_context_config: generate?.ai_context_config as DocsReadyConfig[\"generate\"][\"ai_context_config\"],\n },\n guard: {\n npm_packages: (guard?.npm_packages as DocsReadyConfig[\"guard\"][\"npm_packages\"]) ?? DEFAULTS.guard.npm_packages,\n github_releases: (guard?.github_releases as DocsReadyConfig[\"guard\"][\"github_releases\"]) ?? DEFAULTS.guard.github_releases,\n endpoints: (guard?.endpoints as DocsReadyConfig[\"guard\"][\"endpoints\"]) ?? DEFAULTS.guard.endpoints,\n readme_scans: (guard?.readme_scans as DocsReadyConfig[\"guard\"][\"readme_scans\"]) ?? DEFAULTS.guard.readme_scans,\n workflow: {\n enabled: (guardWorkflow.enabled as boolean) ?? DEFAULTS.guard.workflow.enabled,\n schedule: (guardWorkflow.schedule as string) ?? DEFAULTS.guard.workflow.schedule,\n create_issues: (guardWorkflow.create_issues as boolean) ?? DEFAULTS.guard.workflow.create_issues,\n labels: (guardWorkflow.labels as string[]) ?? DEFAULTS.guard.workflow.labels,\n },\n },\n deploy: {\n platform: (deploy?.platform as DocsReadyConfig[\"deploy\"][\"platform\"]) ?? DEFAULTS.deploy.platform,\n cors: {\n enabled: ((deploy?.cors as Record<string, unknown>)?.enabled as boolean) ?? DEFAULTS.deploy.cors.enabled,\n origins: ((deploy?.cors as Record<string, unknown>)?.origins as string[]) ?? DEFAULTS.deploy.cors.origins,\n },\n },\n validate: {\n max_tokens: (validateConf?.max_tokens as number) ?? DEFAULTS.validate.max_tokens,\n check_links: (validateConf?.check_links as boolean) ?? DEFAULTS.validate.check_links,\n check_coverage: (validateConf?.check_coverage as boolean) ?? DEFAULTS.validate.check_coverage,\n coverage_threshold: (validateConf?.coverage_threshold as number) ?? DEFAULTS.validate.coverage_threshold,\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,167 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadConfig
|
|
4
|
+
} from "./chunk-QI2AROM3.js";
|
|
2
5
|
import {
|
|
3
6
|
log,
|
|
4
7
|
spinner
|
|
5
8
|
} from "./chunk-7YN54Y4Y.js";
|
|
6
9
|
|
|
7
10
|
// src/cli/commands/generate.ts
|
|
8
|
-
import
|
|
9
|
-
import
|
|
11
|
+
import fs2 from "fs/promises";
|
|
12
|
+
import path4 from "path";
|
|
10
13
|
|
|
11
|
-
// src/core/
|
|
14
|
+
// src/core/scanner.ts
|
|
12
15
|
import fs from "fs/promises";
|
|
13
16
|
import path from "path";
|
|
14
|
-
import YAML from "yaml";
|
|
15
|
-
var DEFAULTS = {
|
|
16
|
-
docs: {
|
|
17
|
-
dir: "./docs",
|
|
18
|
-
include: ["**/*.md", "**/*.mdx"],
|
|
19
|
-
exclude: ["**/node_modules/**", "**/_*"]
|
|
20
|
-
},
|
|
21
|
-
generate: {
|
|
22
|
-
llms_txt: true,
|
|
23
|
-
llms_full_txt: true,
|
|
24
|
-
ai_context: true,
|
|
25
|
-
output_dir: "./build"
|
|
26
|
-
},
|
|
27
|
-
guard: {
|
|
28
|
-
npm_packages: [],
|
|
29
|
-
github_releases: [],
|
|
30
|
-
endpoints: [],
|
|
31
|
-
readme_scans: [],
|
|
32
|
-
workflow: {
|
|
33
|
-
enabled: true,
|
|
34
|
-
schedule: "0 9 */3 * *",
|
|
35
|
-
create_issues: true,
|
|
36
|
-
labels: ["ai-context-review", "documentation"]
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
deploy: {
|
|
40
|
-
platform: "none",
|
|
41
|
-
cors: { enabled: true, origins: ["*"] }
|
|
42
|
-
},
|
|
43
|
-
validate: {
|
|
44
|
-
max_tokens: 15e4,
|
|
45
|
-
check_links: true,
|
|
46
|
-
check_coverage: true,
|
|
47
|
-
coverage_threshold: 0.95
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
var CONFIG_FILES = [
|
|
51
|
-
{ name: ".docs-ready.yaml", parser: "yaml" },
|
|
52
|
-
{ name: ".docs-ready.yml", parser: "yaml" },
|
|
53
|
-
{ name: ".docs-ready.json", parser: "json" },
|
|
54
|
-
{ name: ".docs-ready.toml", parser: "toml" }
|
|
55
|
-
];
|
|
56
|
-
async function loadConfig(dir, configPath) {
|
|
57
|
-
let raw;
|
|
58
|
-
if (configPath) {
|
|
59
|
-
const content = await fs.readFile(configPath, "utf-8");
|
|
60
|
-
raw = parseConfig(content, configPath);
|
|
61
|
-
} else {
|
|
62
|
-
for (const { name, parser } of CONFIG_FILES) {
|
|
63
|
-
const fullPath = path.join(dir, name);
|
|
64
|
-
try {
|
|
65
|
-
const content = await fs.readFile(fullPath, "utf-8");
|
|
66
|
-
if (parser === "toml") {
|
|
67
|
-
throw new Error("TOML support requires a TOML parser. Use .yaml or .json instead.");
|
|
68
|
-
}
|
|
69
|
-
raw = parseConfig(content, fullPath);
|
|
70
|
-
break;
|
|
71
|
-
} catch (err) {
|
|
72
|
-
if (err.code === "ENOENT") continue;
|
|
73
|
-
throw err;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (!raw) {
|
|
78
|
-
throw new Error(
|
|
79
|
-
"No config file found. Run `docs-ready init` to create one, or create .docs-ready.yaml manually."
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
validate(raw);
|
|
83
|
-
return applyDefaults(raw);
|
|
84
|
-
}
|
|
85
|
-
function parseConfig(content, filePath) {
|
|
86
|
-
if (filePath.endsWith(".json")) {
|
|
87
|
-
return JSON.parse(content);
|
|
88
|
-
}
|
|
89
|
-
const parsed = YAML.parse(content);
|
|
90
|
-
if (parsed === null || typeof parsed !== "object") {
|
|
91
|
-
throw new Error(`Failed to parse config file: ${filePath}`);
|
|
92
|
-
}
|
|
93
|
-
return parsed;
|
|
94
|
-
}
|
|
95
|
-
function validate(raw) {
|
|
96
|
-
const missing = [];
|
|
97
|
-
if (!raw.title || typeof raw.title !== "string") missing.push("title");
|
|
98
|
-
if (!raw.description || typeof raw.description !== "string") missing.push("description");
|
|
99
|
-
if (!raw.url || typeof raw.url !== "string") missing.push("url");
|
|
100
|
-
if (missing.length > 0) {
|
|
101
|
-
throw new Error(
|
|
102
|
-
`Missing required config fields: ${missing.join(", ")}. Add them to your .docs-ready.yaml file.`
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function applyDefaults(raw) {
|
|
107
|
-
const docs = raw.docs;
|
|
108
|
-
const generate = raw.generate;
|
|
109
|
-
const guard = raw.guard;
|
|
110
|
-
const deploy = raw.deploy;
|
|
111
|
-
const validateConf = raw.validate;
|
|
112
|
-
const guardWorkflow = guard?.workflow ?? {};
|
|
113
|
-
return {
|
|
114
|
-
title: raw.title,
|
|
115
|
-
description: raw.description,
|
|
116
|
-
url: raw.url,
|
|
117
|
-
docs: {
|
|
118
|
-
dir: docs?.dir ?? DEFAULTS.docs.dir,
|
|
119
|
-
include: docs?.include ?? DEFAULTS.docs.include,
|
|
120
|
-
exclude: docs?.exclude ?? DEFAULTS.docs.exclude
|
|
121
|
-
},
|
|
122
|
-
generate: {
|
|
123
|
-
llms_txt: generate?.llms_txt ?? DEFAULTS.generate.llms_txt,
|
|
124
|
-
llms_full_txt: generate?.llms_full_txt ?? DEFAULTS.generate.llms_full_txt,
|
|
125
|
-
ai_context: generate?.ai_context ?? DEFAULTS.generate.ai_context,
|
|
126
|
-
output_dir: generate?.output_dir ?? DEFAULTS.generate.output_dir,
|
|
127
|
-
sections: generate?.sections,
|
|
128
|
-
ai_context_config: generate?.ai_context_config
|
|
129
|
-
},
|
|
130
|
-
guard: {
|
|
131
|
-
npm_packages: guard?.npm_packages ?? DEFAULTS.guard.npm_packages,
|
|
132
|
-
github_releases: guard?.github_releases ?? DEFAULTS.guard.github_releases,
|
|
133
|
-
endpoints: guard?.endpoints ?? DEFAULTS.guard.endpoints,
|
|
134
|
-
readme_scans: guard?.readme_scans ?? DEFAULTS.guard.readme_scans,
|
|
135
|
-
workflow: {
|
|
136
|
-
enabled: guardWorkflow.enabled ?? DEFAULTS.guard.workflow.enabled,
|
|
137
|
-
schedule: guardWorkflow.schedule ?? DEFAULTS.guard.workflow.schedule,
|
|
138
|
-
create_issues: guardWorkflow.create_issues ?? DEFAULTS.guard.workflow.create_issues,
|
|
139
|
-
labels: guardWorkflow.labels ?? DEFAULTS.guard.workflow.labels
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
deploy: {
|
|
143
|
-
platform: deploy?.platform ?? DEFAULTS.deploy.platform,
|
|
144
|
-
cors: {
|
|
145
|
-
enabled: deploy?.cors?.enabled ?? DEFAULTS.deploy.cors.enabled,
|
|
146
|
-
origins: deploy?.cors?.origins ?? DEFAULTS.deploy.cors.origins
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
validate: {
|
|
150
|
-
max_tokens: validateConf?.max_tokens ?? DEFAULTS.validate.max_tokens,
|
|
151
|
-
check_links: validateConf?.check_links ?? DEFAULTS.validate.check_links,
|
|
152
|
-
check_coverage: validateConf?.check_coverage ?? DEFAULTS.validate.check_coverage,
|
|
153
|
-
coverage_threshold: validateConf?.coverage_threshold ?? DEFAULTS.validate.coverage_threshold
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// src/core/scanner.ts
|
|
159
|
-
import fs2 from "fs/promises";
|
|
160
|
-
import path2 from "path";
|
|
161
17
|
import matter from "gray-matter";
|
|
162
18
|
import { glob } from "glob";
|
|
163
19
|
async function scanDocs(docsDir, options) {
|
|
164
|
-
const resolvedDir =
|
|
20
|
+
const resolvedDir = path.resolve(docsDir);
|
|
165
21
|
const allFiles = [];
|
|
166
22
|
for (const pattern of options.include) {
|
|
167
23
|
const matches = await glob(pattern, {
|
|
@@ -175,8 +31,8 @@ async function scanDocs(docsDir, options) {
|
|
|
175
31
|
const uniqueFiles = [...new Set(allFiles)];
|
|
176
32
|
const pages = [];
|
|
177
33
|
for (const relativePath of uniqueFiles) {
|
|
178
|
-
const filePath =
|
|
179
|
-
const raw = await
|
|
34
|
+
const filePath = path.join(resolvedDir, relativePath);
|
|
35
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
180
36
|
const { data: frontmatter, content } = matter(raw);
|
|
181
37
|
const title = resolveTitle(frontmatter, content, relativePath);
|
|
182
38
|
const description = frontmatter.description ?? null;
|
|
@@ -205,12 +61,12 @@ function resolveTitle(frontmatter, content, relativePath) {
|
|
|
205
61
|
if (h1Match) {
|
|
206
62
|
return h1Match[1].trim();
|
|
207
63
|
}
|
|
208
|
-
const basename =
|
|
64
|
+
const basename = path.basename(relativePath, path.extname(relativePath));
|
|
209
65
|
return basename.charAt(0).toUpperCase() + basename.slice(1).replace(/[-_]/g, " ");
|
|
210
66
|
}
|
|
211
67
|
|
|
212
68
|
// src/generate/llms-txt.ts
|
|
213
|
-
import
|
|
69
|
+
import path3 from "path";
|
|
214
70
|
|
|
215
71
|
// node_modules/balanced-match/dist/esm/index.js
|
|
216
72
|
var balanced = (a, b, str) => {
|
|
@@ -1264,11 +1120,11 @@ var qmarksTestNoExtDot = ([$0]) => {
|
|
|
1264
1120
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
1265
1121
|
};
|
|
1266
1122
|
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
1267
|
-
var
|
|
1123
|
+
var path2 = {
|
|
1268
1124
|
win32: { sep: "\\" },
|
|
1269
1125
|
posix: { sep: "/" }
|
|
1270
1126
|
};
|
|
1271
|
-
var sep = defaultPlatform === "win32" ?
|
|
1127
|
+
var sep = defaultPlatform === "win32" ? path2.win32.sep : path2.posix.sep;
|
|
1272
1128
|
minimatch.sep = sep;
|
|
1273
1129
|
var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
|
|
1274
1130
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -2089,7 +1945,7 @@ function extractFirstParagraph(content) {
|
|
|
2089
1945
|
function groupByDirectory(pages) {
|
|
2090
1946
|
const groups = /* @__PURE__ */ new Map();
|
|
2091
1947
|
for (const page of pages) {
|
|
2092
|
-
const dir =
|
|
1948
|
+
const dir = path3.dirname(page.relativePath);
|
|
2093
1949
|
const topDir = dir === "." ? "." : dir.split("/")[0];
|
|
2094
1950
|
if (!groups.has(topDir)) {
|
|
2095
1951
|
groups.set(topDir, []);
|
|
@@ -2147,7 +2003,7 @@ async function generateCommand(options = {}) {
|
|
|
2147
2003
|
const config = await loadConfig(cwd);
|
|
2148
2004
|
const spin = spinner("Scanning documentation...");
|
|
2149
2005
|
spin.start();
|
|
2150
|
-
const docsDir =
|
|
2006
|
+
const docsDir = path4.resolve(cwd, config.docs.dir);
|
|
2151
2007
|
const pages = await scanDocs(docsDir, {
|
|
2152
2008
|
include: config.docs.include,
|
|
2153
2009
|
exclude: config.docs.exclude
|
|
@@ -2158,7 +2014,7 @@ async function generateCommand(options = {}) {
|
|
|
2158
2014
|
return;
|
|
2159
2015
|
}
|
|
2160
2016
|
log.info(`Found ${pages.length} documentation pages`);
|
|
2161
|
-
const outputDir =
|
|
2017
|
+
const outputDir = path4.resolve(cwd, config.generate.output_dir);
|
|
2162
2018
|
if (config.generate.llms_txt && options.only !== "llms-full") {
|
|
2163
2019
|
const llmsTxt = generateLlmsTxt(pages, {
|
|
2164
2020
|
title: config.title,
|
|
@@ -2170,8 +2026,8 @@ async function generateCommand(options = {}) {
|
|
|
2170
2026
|
log.info("[dry-run] Would write llms.txt");
|
|
2171
2027
|
log.dim(` ${llmsTxt.length} chars, ${formatTokens(estimateTokens(llmsTxt))}`);
|
|
2172
2028
|
} else {
|
|
2173
|
-
await
|
|
2174
|
-
await
|
|
2029
|
+
await fs2.mkdir(outputDir, { recursive: true });
|
|
2030
|
+
await fs2.writeFile(path4.join(outputDir, "llms.txt"), llmsTxt, "utf-8");
|
|
2175
2031
|
log.success(`Generated llms.txt (${formatTokens(estimateTokens(llmsTxt))})`);
|
|
2176
2032
|
}
|
|
2177
2033
|
}
|
|
@@ -2181,8 +2037,8 @@ async function generateCommand(options = {}) {
|
|
|
2181
2037
|
log.info("[dry-run] Would write llms-full.txt");
|
|
2182
2038
|
log.dim(` ${llmsFullTxt.length} chars, ${formatTokens(estimateTokens(llmsFullTxt))}`);
|
|
2183
2039
|
} else {
|
|
2184
|
-
await
|
|
2185
|
-
await
|
|
2040
|
+
await fs2.mkdir(outputDir, { recursive: true });
|
|
2041
|
+
await fs2.writeFile(path4.join(outputDir, "llms-full.txt"), llmsFullTxt, "utf-8");
|
|
2186
2042
|
log.success(`Generated llms-full.txt (${formatTokens(estimateTokens(llmsFullTxt))})`);
|
|
2187
2043
|
}
|
|
2188
2044
|
}
|
|
@@ -2203,8 +2059,8 @@ async function generateCommand(options = {}) {
|
|
|
2203
2059
|
log.info("[dry-run] Would write ai-context.md");
|
|
2204
2060
|
log.dim(` ${aiContext.length} chars, ${formatTokens(estimateTokens(aiContext))}`);
|
|
2205
2061
|
} else {
|
|
2206
|
-
await
|
|
2207
|
-
await
|
|
2062
|
+
await fs2.mkdir(outputDir, { recursive: true });
|
|
2063
|
+
await fs2.writeFile(path4.join(outputDir, "ai-context.md"), aiContext, "utf-8");
|
|
2208
2064
|
log.success(`Generated ai-context.md (${formatTokens(estimateTokens(aiContext))})`);
|
|
2209
2065
|
}
|
|
2210
2066
|
}
|
|
@@ -2212,4 +2068,4 @@ async function generateCommand(options = {}) {
|
|
|
2212
2068
|
export {
|
|
2213
2069
|
generateCommand
|
|
2214
2070
|
};
|
|
2215
|
-
//# sourceMappingURL=generate-
|
|
2071
|
+
//# sourceMappingURL=generate-N54SEQ7E.js.map
|