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.
@@ -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":";;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,UAAU;AA8CjB,IAAM,WAAmE;AAAA,EACvE,MAAM;AAAA,IACJ,KAAK;AAAA,IACL,SAAS,CAAC,WAAW,UAAU;AAAA,IAC/B,SAAS,CAAC,sBAAsB,OAAO;AAAA,EACzC;AAAA,EACA,UAAU;AAAA,IACR,UAAU;AAAA,IACV,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AAAA,EACA,OAAO;AAAA,IACL,cAAc,CAAC;AAAA,IACf,iBAAiB,CAAC;AAAA,IAClB,WAAW,CAAC;AAAA,IACZ,cAAc,CAAC;AAAA,IACf,UAAU;AAAA,MACR,SAAS;AAAA,MACT,UAAU;AAAA,MACV,eAAe;AAAA,MACf,QAAQ,CAAC,qBAAqB,eAAe;AAAA,IAC/C;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC,GAAG,EAAE;AAAA,EACxC;AAAA,EACA,UAAU;AAAA,IACR,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,EACtB;AACF;AAEA,IAAM,eAAe;AAAA,EACnB,EAAE,MAAM,oBAAoB,QAAQ,OAAgB;AAAA,EACpD,EAAE,MAAM,mBAAmB,QAAQ,OAAgB;AAAA,EACnD,EAAE,MAAM,oBAAoB,QAAQ,OAAgB;AAAA,EACpD,EAAE,MAAM,oBAAoB,QAAQ,OAAgB;AACtD;AAEA,eAAsB,WAAW,KAAa,YAA+C;AAC3F,MAAI;AAEJ,MAAI,YAAY;AACd,UAAM,UAAU,MAAM,GAAG,SAAS,YAAY,OAAO;AACrD,UAAM,YAAY,SAAS,UAAU;AAAA,EACvC,OAAO;AACL,eAAW,EAAE,MAAM,OAAO,KAAK,cAAc;AAC3C,YAAM,WAAW,KAAK,KAAK,KAAK,IAAI;AACpC,UAAI;AACF,cAAM,UAAU,MAAM,GAAG,SAAS,UAAU,OAAO;AACnD,YAAI,WAAW,QAAQ;AACrB,gBAAM,IAAI,MAAM,kEAAkE;AAAA,QACpF;AACA,cAAM,YAAY,SAAS,QAAQ;AACnC;AAAA,MACF,SAAS,KAAK;AACZ,YAAK,IAA8B,SAAS,SAAU;AACtD,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,WAAS,GAAG;AACZ,SAAO,cAAc,GAAG;AAC1B;AAEA,SAAS,YAAY,SAAiB,UAA2C;AAC/E,MAAI,SAAS,SAAS,OAAO,GAAG;AAC9B,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B;AACA,QAAM,SAAS,KAAK,MAAM,OAAO;AACjC,MAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;AACjD,UAAM,IAAI,MAAM,gCAAgC,QAAQ,EAAE;AAAA,EAC5D;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAoC;AACpD,QAAM,UAAoB,CAAC;AAC3B,MAAI,CAAC,IAAI,SAAS,OAAO,IAAI,UAAU,SAAU,SAAQ,KAAK,OAAO;AACrE,MAAI,CAAC,IAAI,eAAe,OAAO,IAAI,gBAAgB,SAAU,SAAQ,KAAK,aAAa;AACvF,MAAI,CAAC,IAAI,OAAO,OAAO,IAAI,QAAQ,SAAU,SAAQ,KAAK,KAAK;AAE/D,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,mCAAmC,QAAQ,KAAK,IAAI,CAAC;AAAA,IAEvD;AAAA,EACF;AACF;AAEA,SAAS,cAAc,KAA+C;AACpE,QAAM,OAAO,IAAI;AACjB,QAAM,WAAW,IAAI;AACrB,QAAM,QAAQ,IAAI;AAClB,QAAM,SAAS,IAAI;AACnB,QAAM,eAAe,IAAI;AACzB,QAAM,gBAAiB,OAAO,YAAwC,CAAC;AAEvE,SAAO;AAAA,IACL,OAAO,IAAI;AAAA,IACX,aAAa,IAAI;AAAA,IACjB,KAAK,IAAI;AAAA,IACT,MAAM;AAAA,MACJ,KAAM,MAAM,OAAkB,SAAS,KAAK;AAAA,MAC5C,SAAU,MAAM,WAAwB,SAAS,KAAK;AAAA,MACtD,SAAU,MAAM,WAAwB,SAAS,KAAK;AAAA,IACxD;AAAA,IACA,UAAU;AAAA,MACR,UAAW,UAAU,YAAwB,SAAS,SAAS;AAAA,MAC/D,eAAgB,UAAU,iBAA6B,SAAS,SAAS;AAAA,MACzE,YAAa,UAAU,cAA0B,SAAS,SAAS;AAAA,MACnE,YAAa,UAAU,cAAyB,SAAS,SAAS;AAAA,MAClE,UAAU,UAAU;AAAA,MACpB,mBAAmB,UAAU;AAAA,IAC/B;AAAA,IACA,OAAO;AAAA,MACL,cAAe,OAAO,gBAA6D,SAAS,MAAM;AAAA,MAClG,iBAAkB,OAAO,mBAAmE,SAAS,MAAM;AAAA,MAC3G,WAAY,OAAO,aAAuD,SAAS,MAAM;AAAA,MACzF,cAAe,OAAO,gBAA6D,SAAS,MAAM;AAAA,MAClG,UAAU;AAAA,QACR,SAAU,cAAc,WAAuB,SAAS,MAAM,SAAS;AAAA,QACvE,UAAW,cAAc,YAAuB,SAAS,MAAM,SAAS;AAAA,QACxE,eAAgB,cAAc,iBAA6B,SAAS,MAAM,SAAS;AAAA,QACnF,QAAS,cAAc,UAAuB,SAAS,MAAM,SAAS;AAAA,MACxE;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,UAAW,QAAQ,YAAsD,SAAS,OAAO;AAAA,MACzF,MAAM;AAAA,QACJ,SAAW,QAAQ,MAAkC,WAAuB,SAAS,OAAO,KAAK;AAAA,QACjG,SAAW,QAAQ,MAAkC,WAAwB,SAAS,OAAO,KAAK;AAAA,MACpG;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,YAAa,cAAc,cAAyB,SAAS,SAAS;AAAA,MACtE,aAAc,cAAc,eAA2B,SAAS,SAAS;AAAA,MACzE,gBAAiB,cAAc,kBAA8B,SAAS,SAAS;AAAA,MAC/E,oBAAqB,cAAc,sBAAiC,SAAS,SAAS;AAAA,IACxF;AAAA,EACF;AACF;","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 fs3 from "fs/promises";
9
- import path5 from "path";
11
+ import fs2 from "fs/promises";
12
+ import path4 from "path";
10
13
 
11
- // src/core/config.ts
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 = path2.resolve(docsDir);
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 = path2.join(resolvedDir, relativePath);
179
- const raw = await fs2.readFile(filePath, "utf-8");
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 = path2.basename(relativePath, path2.extname(relativePath));
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 path4 from "path";
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 path3 = {
1123
+ var path2 = {
1268
1124
  win32: { sep: "\\" },
1269
1125
  posix: { sep: "/" }
1270
1126
  };
1271
- var sep = defaultPlatform === "win32" ? path3.win32.sep : path3.posix.sep;
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 = path4.dirname(page.relativePath);
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 = path5.resolve(cwd, config.docs.dir);
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 = path5.resolve(cwd, config.generate.output_dir);
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 fs3.mkdir(outputDir, { recursive: true });
2174
- await fs3.writeFile(path5.join(outputDir, "llms.txt"), llmsTxt, "utf-8");
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 fs3.mkdir(outputDir, { recursive: true });
2185
- await fs3.writeFile(path5.join(outputDir, "llms-full.txt"), llmsFullTxt, "utf-8");
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 fs3.mkdir(outputDir, { recursive: true });
2207
- await fs3.writeFile(path5.join(outputDir, "ai-context.md"), aiContext, "utf-8");
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-LDVEG2WV.js.map
2071
+ //# sourceMappingURL=generate-N54SEQ7E.js.map