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,368 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadConfig
4
+ } from "./chunk-QI2AROM3.js";
5
+ import {
6
+ log,
7
+ spinner
8
+ } from "./chunk-7YN54Y4Y.js";
9
+
10
+ // src/utils/http.ts
11
+ async function fetchWithTimeout(url, options = {}) {
12
+ const { timeout = 1e4, headers = {} } = options;
13
+ const controller = new AbortController();
14
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
15
+ try {
16
+ const response = await fetch(url, {
17
+ signal: controller.signal,
18
+ headers
19
+ });
20
+ return response;
21
+ } finally {
22
+ clearTimeout(timeoutId);
23
+ }
24
+ }
25
+ function githubHeaders() {
26
+ const headers = {
27
+ Accept: "application/vnd.github.v3+json"
28
+ };
29
+ const token = process.env.GITHUB_TOKEN;
30
+ if (token) {
31
+ headers.Authorization = `Bearer ${token}`;
32
+ }
33
+ return headers;
34
+ }
35
+
36
+ // src/guard/monitors/npm.ts
37
+ async function checkNpmPackage(config) {
38
+ const url = `https://registry.npmjs.org/${encodeURIComponent(config.name).replace("%40", "@")}/latest`;
39
+ try {
40
+ const response = await fetchWithTimeout(url, { timeout: 1e4 });
41
+ if (response.status === 404) {
42
+ return {
43
+ monitor: "npm",
44
+ label: config.label,
45
+ status: "fail",
46
+ message: `Package ${config.name} not found on npm`
47
+ };
48
+ }
49
+ if (!response.ok) {
50
+ return {
51
+ monitor: "npm",
52
+ label: config.label,
53
+ status: "warn",
54
+ message: `npm registry returned ${response.status}`
55
+ };
56
+ }
57
+ const data = await response.json();
58
+ return {
59
+ monitor: "npm",
60
+ label: config.label,
61
+ status: "pass",
62
+ message: `Latest version: ${data.version}`,
63
+ details: { latestVersion: data.version, package: config.name }
64
+ };
65
+ } catch (error) {
66
+ return {
67
+ monitor: "npm",
68
+ label: config.label,
69
+ status: "warn",
70
+ message: `Failed to check npm: ${error.message}`
71
+ };
72
+ }
73
+ }
74
+
75
+ // src/guard/monitors/github.ts
76
+ async function checkGitHubRelease(config) {
77
+ const headers = githubHeaders();
78
+ try {
79
+ const releaseUrl = `https://api.github.com/repos/${config.repo}/releases/latest`;
80
+ const response = await fetchWithTimeout(releaseUrl, { timeout: 1e4, headers });
81
+ if (response.status === 403) {
82
+ return {
83
+ monitor: "github",
84
+ label: config.label,
85
+ status: "warn",
86
+ message: "GitHub API rate limit exceeded. Set GITHUB_TOKEN for higher limits."
87
+ };
88
+ }
89
+ if (response.ok) {
90
+ const data = await response.json();
91
+ return {
92
+ monitor: "github",
93
+ label: config.label,
94
+ status: "pass",
95
+ message: `Latest release: ${data.tag_name} (${data.published_at.split("T")[0]})`,
96
+ details: { tag: data.tag_name, publishedAt: data.published_at, repo: config.repo }
97
+ };
98
+ }
99
+ if (response.status === 404) {
100
+ return await checkTags(config, headers);
101
+ }
102
+ return {
103
+ monitor: "github",
104
+ label: config.label,
105
+ status: "warn",
106
+ message: `GitHub API returned ${response.status}`
107
+ };
108
+ } catch (error) {
109
+ return {
110
+ monitor: "github",
111
+ label: config.label,
112
+ status: "warn",
113
+ message: `Failed to check GitHub: ${error.message}`
114
+ };
115
+ }
116
+ }
117
+ async function checkTags(config, headers) {
118
+ try {
119
+ const tagsUrl = `https://api.github.com/repos/${config.repo}/tags?per_page=1`;
120
+ const response = await fetchWithTimeout(tagsUrl, { timeout: 1e4, headers });
121
+ if (!response.ok) {
122
+ return {
123
+ monitor: "github",
124
+ label: config.label,
125
+ status: "warn",
126
+ message: `No releases or tags found for ${config.repo}`
127
+ };
128
+ }
129
+ const tags = await response.json();
130
+ if (tags.length === 0) {
131
+ return {
132
+ monitor: "github",
133
+ label: config.label,
134
+ status: "warn",
135
+ message: `No releases or tags found for ${config.repo}`
136
+ };
137
+ }
138
+ return {
139
+ monitor: "github",
140
+ label: config.label,
141
+ status: "pass",
142
+ message: `Latest tag: ${tags[0].name}`,
143
+ details: { tag: tags[0].name, repo: config.repo }
144
+ };
145
+ } catch {
146
+ return {
147
+ monitor: "github",
148
+ label: config.label,
149
+ status: "warn",
150
+ message: `Failed to check tags for ${config.repo}`
151
+ };
152
+ }
153
+ }
154
+
155
+ // src/guard/monitors/endpoints.ts
156
+ async function checkEndpoint(config) {
157
+ const expectedStatuses = config.expected_status ?? [200, 301, 302];
158
+ const startTime = Date.now();
159
+ try {
160
+ const response = await fetchWithTimeout(config.url, { timeout: 1e4 });
161
+ const elapsed = Date.now() - startTime;
162
+ if (expectedStatuses.includes(response.status)) {
163
+ return {
164
+ monitor: "endpoint",
165
+ label: config.label,
166
+ status: "pass",
167
+ message: `${config.url} returned ${response.status} (${elapsed}ms)`,
168
+ details: { url: config.url, statusCode: response.status, responseTime: elapsed }
169
+ };
170
+ }
171
+ return {
172
+ monitor: "endpoint",
173
+ label: config.label,
174
+ status: "fail",
175
+ message: `${config.url} returned ${response.status} (expected ${expectedStatuses.join("|")})`,
176
+ details: { url: config.url, statusCode: response.status, responseTime: elapsed }
177
+ };
178
+ } catch (error) {
179
+ const err = error;
180
+ if (err.name === "AbortError") {
181
+ return {
182
+ monitor: "endpoint",
183
+ label: config.label,
184
+ status: "fail",
185
+ message: `${config.url} timed out after 10s`
186
+ };
187
+ }
188
+ return {
189
+ monitor: "endpoint",
190
+ label: config.label,
191
+ status: "fail",
192
+ message: `${config.url} failed: ${err.message}`
193
+ };
194
+ }
195
+ }
196
+
197
+ // src/guard/monitors/readme-keywords.ts
198
+ async function scanReadme(config) {
199
+ const url = `https://raw.githubusercontent.com/${config.repo}/main/README.md`;
200
+ try {
201
+ const response = await fetchWithTimeout(url, { timeout: 1e4, headers: githubHeaders() });
202
+ if (response.status === 404) {
203
+ const fallbackUrl = `https://raw.githubusercontent.com/${config.repo}/master/README.md`;
204
+ const fallback = await fetchWithTimeout(fallbackUrl, { timeout: 1e4, headers: githubHeaders() });
205
+ if (!fallback.ok) {
206
+ return {
207
+ monitor: "readme",
208
+ label: config.repo,
209
+ status: "warn",
210
+ message: `No README found for ${config.repo}`
211
+ };
212
+ }
213
+ return await analyzeReadme(await fallback.text(), config);
214
+ }
215
+ if (!response.ok) {
216
+ return {
217
+ monitor: "readme",
218
+ label: config.repo,
219
+ status: "warn",
220
+ message: `Failed to fetch README: ${response.status}`
221
+ };
222
+ }
223
+ return await analyzeReadme(await response.text(), config);
224
+ } catch (error) {
225
+ return {
226
+ monitor: "readme",
227
+ label: config.repo,
228
+ status: "warn",
229
+ message: `Failed to scan README: ${error.message}`
230
+ };
231
+ }
232
+ }
233
+ async function analyzeReadme(content, config) {
234
+ const found = [];
235
+ for (const keyword of config.keywords) {
236
+ const regex = new RegExp(keyword, "gi");
237
+ let match;
238
+ while ((match = regex.exec(content)) !== null) {
239
+ const start = Math.max(0, match.index - 25);
240
+ const end = Math.min(content.length, match.index + keyword.length + 25);
241
+ const context = content.slice(start, end).replace(/\n/g, " ");
242
+ found.push({ keyword, context });
243
+ }
244
+ }
245
+ if (found.length > 0) {
246
+ return {
247
+ monitor: "readme",
248
+ label: config.repo,
249
+ status: "warn",
250
+ message: `Found ${found.length} keyword(s): ${found.map((f) => f.keyword).join(", ")}`,
251
+ details: { matches: found, repo: config.repo }
252
+ };
253
+ }
254
+ return {
255
+ monitor: "readme",
256
+ label: config.repo,
257
+ status: "pass",
258
+ message: `No concerning keywords found in README`
259
+ };
260
+ }
261
+
262
+ // src/guard/runner.ts
263
+ async function runGuard(config) {
264
+ const results = [];
265
+ for (const pkg of config.guard.npm_packages) {
266
+ results.push(await checkNpmPackage(pkg));
267
+ }
268
+ for (const release of config.guard.github_releases) {
269
+ results.push(await checkGitHubRelease(release));
270
+ }
271
+ for (const endpoint of config.guard.endpoints) {
272
+ results.push(await checkEndpoint(endpoint));
273
+ }
274
+ for (const scan of config.guard.readme_scans) {
275
+ results.push(await scanReadme(scan));
276
+ }
277
+ return results;
278
+ }
279
+ function getExitCode(results) {
280
+ return results.some((r) => r.status === "fail") ? 1 : 0;
281
+ }
282
+
283
+ // src/guard/reporters/console.ts
284
+ import chalk from "chalk";
285
+ var ICONS = {
286
+ pass: chalk.green("\u2714"),
287
+ warn: chalk.yellow("\u26A0"),
288
+ fail: chalk.red("\u2716")
289
+ };
290
+ function formatConsole(results) {
291
+ const lines = [];
292
+ for (const result of results) {
293
+ const icon = ICONS[result.status];
294
+ lines.push(`${icon} [${result.monitor}] ${result.label}: ${result.message}`);
295
+ }
296
+ const passed = results.filter((r) => r.status === "pass").length;
297
+ const warned = results.filter((r) => r.status === "warn").length;
298
+ const failed = results.filter((r) => r.status === "fail").length;
299
+ lines.push("");
300
+ lines.push(`Results: ${passed} passed, ${warned} warnings, ${failed} failed`);
301
+ return lines.join("\n");
302
+ }
303
+
304
+ // src/guard/reporters/json.ts
305
+ function formatJson(results) {
306
+ return JSON.stringify(results, null, 2);
307
+ }
308
+
309
+ // src/guard/reporters/markdown.ts
310
+ var STATUS_EMOJI = {
311
+ pass: "\u2705",
312
+ warn: "\u26A0\uFE0F",
313
+ fail: "\u274C"
314
+ };
315
+ function formatMarkdown(results) {
316
+ const lines = [];
317
+ lines.push("# docs-ready Guard Report");
318
+ lines.push("");
319
+ lines.push("| Status | Monitor | Label | Message |");
320
+ lines.push("|--------|---------|-------|---------|");
321
+ for (const result of results) {
322
+ const emoji = STATUS_EMOJI[result.status];
323
+ lines.push(`| ${emoji} ${result.status} | ${result.monitor} | ${result.label} | ${result.message} |`);
324
+ }
325
+ const failed = results.filter((r) => r.status === "fail").length;
326
+ lines.push("");
327
+ if (failed > 0) {
328
+ lines.push(`**${failed} check(s) failed.** Please review and update AI-facing documentation.`);
329
+ } else {
330
+ lines.push("All checks passed.");
331
+ }
332
+ return lines.join("\n");
333
+ }
334
+
335
+ // src/cli/commands/guard.ts
336
+ async function guardCommand(options = {}) {
337
+ const cwd = process.cwd();
338
+ const config = await loadConfig(cwd);
339
+ const totalChecks = config.guard.npm_packages.length + config.guard.github_releases.length + config.guard.endpoints.length + config.guard.readme_scans.length;
340
+ if (totalChecks === 0) {
341
+ log.warn("No guard checks configured. Add monitors to .docs-ready.yaml");
342
+ return;
343
+ }
344
+ const spin = spinner(`Running ${totalChecks} guard checks...`);
345
+ spin.start();
346
+ const results = await runGuard(config);
347
+ spin.stop();
348
+ const format = options.output ?? "console";
349
+ switch (format) {
350
+ case "json":
351
+ console.log(formatJson(results));
352
+ break;
353
+ case "markdown":
354
+ console.log(formatMarkdown(results));
355
+ break;
356
+ default:
357
+ console.log(formatConsole(results));
358
+ break;
359
+ }
360
+ const exitCode = getExitCode(results);
361
+ if (exitCode !== 0) {
362
+ process.exitCode = exitCode;
363
+ }
364
+ }
365
+ export {
366
+ guardCommand
367
+ };
368
+ //# sourceMappingURL=guard-EKGLQRED.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/http.ts","../src/guard/monitors/npm.ts","../src/guard/monitors/github.ts","../src/guard/monitors/endpoints.ts","../src/guard/monitors/readme-keywords.ts","../src/guard/runner.ts","../src/guard/reporters/console.ts","../src/guard/reporters/json.ts","../src/guard/reporters/markdown.ts","../src/cli/commands/guard.ts"],"sourcesContent":["export interface FetchOptions {\n timeout?: number;\n headers?: Record<string, string>;\n}\n\nexport async function fetchWithTimeout(\n url: string,\n options: FetchOptions = {}\n): Promise<Response> {\n const { timeout = 10000, headers = {} } = options;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n signal: controller.signal,\n headers,\n });\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\nexport function githubHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: \"application/vnd.github.v3+json\",\n };\n const token = process.env.GITHUB_TOKEN;\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n return headers;\n}\n","import { fetchWithTimeout } from \"../../utils/http.js\";\nimport type { GuardResult } from \"../types.js\";\n\nexport interface NpmPackageConfig {\n name: string;\n label: string;\n}\n\nexport async function checkNpmPackage(config: NpmPackageConfig): Promise<GuardResult> {\n const url = `https://registry.npmjs.org/${encodeURIComponent(config.name).replace(\"%40\", \"@\")}/latest`;\n\n try {\n const response = await fetchWithTimeout(url, { timeout: 10000 });\n\n if (response.status === 404) {\n return {\n monitor: \"npm\",\n label: config.label,\n status: \"fail\",\n message: `Package ${config.name} not found on npm`,\n };\n }\n\n if (!response.ok) {\n return {\n monitor: \"npm\",\n label: config.label,\n status: \"warn\",\n message: `npm registry returned ${response.status}`,\n };\n }\n\n const data = await response.json() as { version: string };\n return {\n monitor: \"npm\",\n label: config.label,\n status: \"pass\",\n message: `Latest version: ${data.version}`,\n details: { latestVersion: data.version, package: config.name },\n };\n } catch (error) {\n return {\n monitor: \"npm\",\n label: config.label,\n status: \"warn\",\n message: `Failed to check npm: ${(error as Error).message}`,\n };\n }\n}\n","import { fetchWithTimeout } from \"../../utils/http.js\";\nimport { githubHeaders } from \"../../utils/http.js\";\nimport type { GuardResult } from \"../types.js\";\n\nexport interface GitHubReleaseConfig {\n repo: string;\n label: string;\n}\n\nexport async function checkGitHubRelease(config: GitHubReleaseConfig): Promise<GuardResult> {\n const headers = githubHeaders();\n\n // Try releases API first\n try {\n const releaseUrl = `https://api.github.com/repos/${config.repo}/releases/latest`;\n const response = await fetchWithTimeout(releaseUrl, { timeout: 10000, headers });\n\n if (response.status === 403) {\n return {\n monitor: \"github\",\n label: config.label,\n status: \"warn\",\n message: \"GitHub API rate limit exceeded. Set GITHUB_TOKEN for higher limits.\",\n };\n }\n\n if (response.ok) {\n const data = await response.json() as { tag_name: string; published_at: string };\n return {\n monitor: \"github\",\n label: config.label,\n status: \"pass\",\n message: `Latest release: ${data.tag_name} (${data.published_at.split(\"T\")[0]})`,\n details: { tag: data.tag_name, publishedAt: data.published_at, repo: config.repo },\n };\n }\n\n // No releases — fall back to tags API\n if (response.status === 404) {\n return await checkTags(config, headers);\n }\n\n return {\n monitor: \"github\",\n label: config.label,\n status: \"warn\",\n message: `GitHub API returned ${response.status}`,\n };\n } catch (error) {\n return {\n monitor: \"github\",\n label: config.label,\n status: \"warn\",\n message: `Failed to check GitHub: ${(error as Error).message}`,\n };\n }\n}\n\nasync function checkTags(\n config: GitHubReleaseConfig,\n headers: Record<string, string>\n): Promise<GuardResult> {\n try {\n const tagsUrl = `https://api.github.com/repos/${config.repo}/tags?per_page=1`;\n const response = await fetchWithTimeout(tagsUrl, { timeout: 10000, headers });\n\n if (!response.ok) {\n return {\n monitor: \"github\",\n label: config.label,\n status: \"warn\",\n message: `No releases or tags found for ${config.repo}`,\n };\n }\n\n const tags = await response.json() as Array<{ name: string }>;\n if (tags.length === 0) {\n return {\n monitor: \"github\",\n label: config.label,\n status: \"warn\",\n message: `No releases or tags found for ${config.repo}`,\n };\n }\n\n return {\n monitor: \"github\",\n label: config.label,\n status: \"pass\",\n message: `Latest tag: ${tags[0].name}`,\n details: { tag: tags[0].name, repo: config.repo },\n };\n } catch {\n return {\n monitor: \"github\",\n label: config.label,\n status: \"warn\",\n message: `Failed to check tags for ${config.repo}`,\n };\n }\n}\n","import { fetchWithTimeout } from \"../../utils/http.js\";\nimport type { GuardResult } from \"../types.js\";\n\nexport interface EndpointConfig {\n url: string;\n label: string;\n expected_status?: number[];\n}\n\nexport async function checkEndpoint(config: EndpointConfig): Promise<GuardResult> {\n const expectedStatuses = config.expected_status ?? [200, 301, 302];\n const startTime = Date.now();\n\n try {\n const response = await fetchWithTimeout(config.url, { timeout: 10000 });\n const elapsed = Date.now() - startTime;\n\n if (expectedStatuses.includes(response.status)) {\n return {\n monitor: \"endpoint\",\n label: config.label,\n status: \"pass\",\n message: `${config.url} returned ${response.status} (${elapsed}ms)`,\n details: { url: config.url, statusCode: response.status, responseTime: elapsed },\n };\n }\n\n return {\n monitor: \"endpoint\",\n label: config.label,\n status: \"fail\",\n message: `${config.url} returned ${response.status} (expected ${expectedStatuses.join(\"|\")})`,\n details: { url: config.url, statusCode: response.status, responseTime: elapsed },\n };\n } catch (error) {\n const err = error as Error;\n if (err.name === \"AbortError\") {\n return {\n monitor: \"endpoint\",\n label: config.label,\n status: \"fail\",\n message: `${config.url} timed out after 10s`,\n };\n }\n return {\n monitor: \"endpoint\",\n label: config.label,\n status: \"fail\",\n message: `${config.url} failed: ${err.message}`,\n };\n }\n}\n","import { fetchWithTimeout, githubHeaders } from \"../../utils/http.js\";\nimport type { GuardResult } from \"../types.js\";\n\nexport interface ReadmeScanConfig {\n repo: string;\n keywords: string[];\n}\n\nexport async function scanReadme(config: ReadmeScanConfig): Promise<GuardResult> {\n const url = `https://raw.githubusercontent.com/${config.repo}/main/README.md`;\n\n try {\n const response = await fetchWithTimeout(url, { timeout: 10000, headers: githubHeaders() });\n\n if (response.status === 404) {\n // Try master branch\n const fallbackUrl = `https://raw.githubusercontent.com/${config.repo}/master/README.md`;\n const fallback = await fetchWithTimeout(fallbackUrl, { timeout: 10000, headers: githubHeaders() });\n if (!fallback.ok) {\n return {\n monitor: \"readme\",\n label: config.repo,\n status: \"warn\",\n message: `No README found for ${config.repo}`,\n };\n }\n return await analyzeReadme(await fallback.text(), config);\n }\n\n if (!response.ok) {\n return {\n monitor: \"readme\",\n label: config.repo,\n status: \"warn\",\n message: `Failed to fetch README: ${response.status}`,\n };\n }\n\n return await analyzeReadme(await response.text(), config);\n } catch (error) {\n return {\n monitor: \"readme\",\n label: config.repo,\n status: \"warn\",\n message: `Failed to scan README: ${(error as Error).message}`,\n };\n }\n}\n\nasync function analyzeReadme(content: string, config: ReadmeScanConfig): Promise<GuardResult> {\n const found: Array<{ keyword: string; context: string }> = [];\n\n for (const keyword of config.keywords) {\n const regex = new RegExp(keyword, \"gi\");\n let match;\n while ((match = regex.exec(content)) !== null) {\n const start = Math.max(0, match.index - 25);\n const end = Math.min(content.length, match.index + keyword.length + 25);\n const context = content.slice(start, end).replace(/\\n/g, \" \");\n found.push({ keyword, context });\n }\n }\n\n if (found.length > 0) {\n return {\n monitor: \"readme\",\n label: config.repo,\n status: \"warn\",\n message: `Found ${found.length} keyword(s): ${found.map((f) => f.keyword).join(\", \")}`,\n details: { matches: found, repo: config.repo },\n };\n }\n\n return {\n monitor: \"readme\",\n label: config.repo,\n status: \"pass\",\n message: `No concerning keywords found in README`,\n };\n}\n","import type { GuardResult } from \"./types.js\";\nimport { checkNpmPackage } from \"./monitors/npm.js\";\nimport { checkGitHubRelease } from \"./monitors/github.js\";\nimport { checkEndpoint } from \"./monitors/endpoints.js\";\nimport { scanReadme } from \"./monitors/readme-keywords.js\";\nimport type { DocsReadyConfig } from \"../core/config.js\";\n\nexport async function runGuard(config: DocsReadyConfig): Promise<GuardResult[]> {\n const results: GuardResult[] = [];\n\n // npm monitors\n for (const pkg of config.guard.npm_packages) {\n results.push(await checkNpmPackage(pkg));\n }\n\n // GitHub release monitors\n for (const release of config.guard.github_releases) {\n results.push(await checkGitHubRelease(release));\n }\n\n // Endpoint monitors\n for (const endpoint of config.guard.endpoints) {\n results.push(await checkEndpoint(endpoint));\n }\n\n // README scans\n for (const scan of config.guard.readme_scans) {\n results.push(await scanReadme(scan));\n }\n\n return results;\n}\n\nexport function getExitCode(results: GuardResult[]): number {\n return results.some((r) => r.status === \"fail\") ? 1 : 0;\n}\n","import chalk from \"chalk\";\nimport type { GuardResult } from \"../types.js\";\n\nconst ICONS: Record<string, string> = {\n pass: chalk.green(\"✔\"),\n warn: chalk.yellow(\"⚠\"),\n fail: chalk.red(\"✖\"),\n};\n\nexport function formatConsole(results: GuardResult[]): string {\n const lines: string[] = [];\n\n for (const result of results) {\n const icon = ICONS[result.status];\n lines.push(`${icon} [${result.monitor}] ${result.label}: ${result.message}`);\n }\n\n const passed = results.filter((r) => r.status === \"pass\").length;\n const warned = results.filter((r) => r.status === \"warn\").length;\n const failed = results.filter((r) => r.status === \"fail\").length;\n lines.push(\"\");\n lines.push(`Results: ${passed} passed, ${warned} warnings, ${failed} failed`);\n\n return lines.join(\"\\n\");\n}\n","import type { GuardResult } from \"../types.js\";\n\nexport function formatJson(results: GuardResult[]): string {\n return JSON.stringify(results, null, 2);\n}\n","import type { GuardResult } from \"../types.js\";\n\nconst STATUS_EMOJI: Record<string, string> = {\n pass: \"✅\",\n warn: \"⚠️\",\n fail: \"❌\",\n};\n\nexport function formatMarkdown(results: GuardResult[]): string {\n const lines: string[] = [];\n\n lines.push(\"# docs-ready Guard Report\");\n lines.push(\"\");\n lines.push(\"| Status | Monitor | Label | Message |\");\n lines.push(\"|--------|---------|-------|---------|\");\n\n for (const result of results) {\n const emoji = STATUS_EMOJI[result.status];\n lines.push(`| ${emoji} ${result.status} | ${result.monitor} | ${result.label} | ${result.message} |`);\n }\n\n const failed = results.filter((r) => r.status === \"fail\").length;\n lines.push(\"\");\n if (failed > 0) {\n lines.push(`**${failed} check(s) failed.** Please review and update AI-facing documentation.`);\n } else {\n lines.push(\"All checks passed.\");\n }\n\n return lines.join(\"\\n\");\n}\n","import { loadConfig } from \"../../core/config.js\";\nimport { runGuard, getExitCode } from \"../../guard/runner.js\";\nimport { formatConsole } from \"../../guard/reporters/console.js\";\nimport { formatJson } from \"../../guard/reporters/json.js\";\nimport { formatMarkdown } from \"../../guard/reporters/markdown.js\";\nimport { log, spinner } from \"../../utils/logger.js\";\n\ninterface GuardOptions {\n output?: string;\n}\n\nexport async function guardCommand(options: GuardOptions = {}): Promise<void> {\n const cwd = process.cwd();\n const config = await loadConfig(cwd);\n\n const totalChecks =\n config.guard.npm_packages.length +\n config.guard.github_releases.length +\n config.guard.endpoints.length +\n config.guard.readme_scans.length;\n\n if (totalChecks === 0) {\n log.warn(\"No guard checks configured. Add monitors to .docs-ready.yaml\");\n return;\n }\n\n const spin = spinner(`Running ${totalChecks} guard checks...`);\n spin.start();\n\n const results = await runGuard(config);\n spin.stop();\n\n // Output\n const format = options.output ?? \"console\";\n switch (format) {\n case \"json\":\n console.log(formatJson(results));\n break;\n case \"markdown\":\n console.log(formatMarkdown(results));\n break;\n default:\n console.log(formatConsole(results));\n break;\n }\n\n // Exit code\n const exitCode = getExitCode(results);\n if (exitCode !== 0) {\n process.exitCode = exitCode;\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -208,11 +208,12 @@ async function main() {
208
208
  await initCommand();
209
209
  });
210
210
  program.command("generate").description("Generate AI-facing documentation files").option("--dry-run", "Show what would be generated without writing files").option("--only <type>", "Generate only: llms-txt, llms-full, or ai-context").action(async (opts) => {
211
- const { generateCommand } = await import("./generate-LDVEG2WV.js");
211
+ const { generateCommand } = await import("./generate-N54SEQ7E.js");
212
212
  await generateCommand({ dryRun: opts.dryRun, only: opts.only });
213
213
  });
214
- program.command("guard").description("Check AI-facing docs for staleness").action(() => {
215
- console.log("Guard command coming in v0.4.0");
214
+ program.command("guard").description("Check AI-facing docs for staleness").option("--output <format>", "Output format: console, json, or markdown", "console").action(async (opts) => {
215
+ const { guardCommand } = await import("./guard-EKGLQRED.js");
216
+ await guardCommand({ output: opts.output });
216
217
  });
217
218
  program.command("validate").description("Lint and validate AI-facing docs").action(() => {
218
219
  console.log("Validate command coming in v0.6.0");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands/init.ts","../src/frameworks/detector.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\nimport { initCommand } from \"./commands/init.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nasync function getVersion(): Promise<string> {\n let dir = __dirname;\n for (let i = 0; i < 5; i++) {\n try {\n const pkg = JSON.parse(await readFile(path.join(dir, \"package.json\"), \"utf-8\"));\n return pkg.version;\n } catch {\n dir = path.dirname(dir);\n }\n }\n return \"0.0.0\";\n}\n\nasync function main(): Promise<void> {\n const version = await getVersion();\n\n const program = new Command();\n\n program\n .name(\"docs-ready\")\n .description(\"Make your docs AI-ready. Keep them that way.\")\n .version(version);\n\n program\n .command(\"init\")\n .description(\"Initialize docs-ready in your project\")\n .action(async () => {\n await initCommand();\n });\n\n program\n .command(\"generate\")\n .description(\"Generate AI-facing documentation files\")\n .option(\"--dry-run\", \"Show what would be generated without writing files\")\n .option(\"--only <type>\", \"Generate only: llms-txt, llms-full, or ai-context\")\n .action(async (opts) => {\n const { generateCommand } = await import(\"./commands/generate.js\");\n await generateCommand({ dryRun: opts.dryRun, only: opts.only });\n });\n\n program\n .command(\"guard\")\n .description(\"Check AI-facing docs for staleness\")\n .action(() => {\n console.log(\"Guard command coming in v0.4.0\");\n });\n\n program\n .command(\"validate\")\n .description(\"Lint and validate AI-facing docs\")\n .action(() => {\n console.log(\"Validate command coming in v0.6.0\");\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\nimport { stdin, stdout } from \"node:process\";\nimport { detectFramework } from \"../../frameworks/detector.js\";\nimport { log, spinner } from \"../../utils/logger.js\";\n\ninterface InitOptions {\n cwd?: string;\n}\n\nexport async function initCommand(options: InitOptions = {}): Promise<void> {\n const cwd = options.cwd ?? process.cwd();\n const configPath = path.join(cwd, \".docs-ready.yaml\");\n\n // Check if config already exists\n try {\n await fs.access(configPath);\n log.warn(\".docs-ready.yaml already exists. Use --force to overwrite.\");\n return;\n } catch {\n // Config doesn't exist, proceed\n }\n\n const spin = spinner(\"Detecting project structure...\");\n spin.start();\n\n // Detect framework\n const framework = await detectFramework(cwd);\n spin.stop();\n\n log.info(`Detected framework: ${framework.name}`);\n log.info(`Docs directory: ${framework.docsDir}`);\n\n // Detect deployment platform\n const platform = await detectPlatform(cwd);\n if (platform !== \"none\") {\n log.info(`Detected platform: ${platform}`);\n }\n\n // Detect docusaurus-plugin-llms\n const hasLlmsPlugin = await detectLlmsPlugin(cwd);\n if (hasLlmsPlugin) {\n log.warn(\"Detected docusaurus-plugin-llms — disabling llms.txt/llms-full.txt generation.\");\n }\n\n // Prompt for project info\n const rl = createInterface({ input: stdin, output: stdout });\n const title = await rl.question(\"Project title: \");\n const description = await rl.question(\"Description: \");\n const url = await rl.question(\"Docs URL (e.g. https://docs.example.com): \");\n rl.close();\n\n // Render config from template (try file first, fall back to inline)\n const templateData: Record<string, string> = {\n title: title || \"My Project\",\n description: description || \"Project documentation\",\n url: url || \"https://docs.example.com\",\n docsDir: framework.docsDir,\n llmsTxt: String(!hasLlmsPlugin),\n llmsFullTxt: String(!hasLlmsPlugin),\n platform,\n };\n\n const template = await loadTemplate();\n const rendered = template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => templateData[key] ?? \"\");\n\n await fs.writeFile(configPath, rendered, \"utf-8\");\n log.success(\"Created .docs-ready.yaml\");\n log.dim(\"Next: run `docs-ready generate` to create AI-facing files.\");\n}\n\nasync function loadTemplate(): Promise<string> {\n // Try to load the template file (works when installed as npm package)\n const candidates = [\n path.join(path.dirname(new URL(import.meta.url).pathname), \"../../templates/config.yaml.tmpl\"),\n path.join(path.dirname(new URL(import.meta.url).pathname), \"../templates/config.yaml.tmpl\"),\n ];\n\n for (const candidate of candidates) {\n try {\n return await fs.readFile(candidate, \"utf-8\");\n } catch {\n // Try next\n }\n }\n\n // Inline fallback\n return `# docs-ready configuration\ntitle: \"{{title}}\"\ndescription: \"{{description}}\"\nurl: \"{{url}}\"\ndocs:\n dir: \"{{docsDir}}\"\n include:\n - \"**/*.md\"\n - \"**/*.mdx\"\n exclude:\n - \"**/node_modules/**\"\n - \"**/_*\"\ngenerate:\n llms_txt: {{llmsTxt}}\n llms_full_txt: {{llmsFullTxt}}\n ai_context: true\n output_dir: \"./build\"\ndeploy:\n platform: \"{{platform}}\"\n`;\n}\n\nasync function detectPlatform(\n cwd: string\n): Promise<\"vercel\" | \"netlify\" | \"cloudflare\" | \"none\"> {\n const checks: Array<{ file: string; platform: \"vercel\" | \"netlify\" | \"cloudflare\" }> = [\n { file: \"vercel.json\", platform: \"vercel\" },\n { file: \"netlify.toml\", platform: \"netlify\" },\n { file: \"_headers\", platform: \"cloudflare\" },\n { file: \"wrangler.toml\", platform: \"cloudflare\" },\n ];\n\n for (const { file, platform } of checks) {\n try {\n await fs.access(path.join(cwd, file));\n return platform;\n } catch {\n // Not found, continue\n }\n }\n\n return \"none\";\n}\n\nasync function detectLlmsPlugin(cwd: string): Promise<boolean> {\n try {\n const pkgPath = path.join(cwd, \"package.json\");\n const content = await fs.readFile(pkgPath, \"utf-8\");\n const pkg = JSON.parse(content);\n const allDeps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n return \"docusaurus-plugin-llms\" in allDeps;\n } catch {\n return false;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport interface FrameworkResult {\n name: \"docusaurus\" | \"vitepress\" | \"mkdocs\" | \"starlight\" | \"generic\";\n configFile: string | null;\n docsDir: string;\n}\n\nconst DETECTORS: Array<{\n name: FrameworkResult[\"name\"];\n files: string[];\n docsDir: string;\n}> = [\n {\n name: \"docusaurus\",\n files: [\n \"docusaurus.config.ts\",\n \"docusaurus.config.js\",\n \"docusaurus.config.mts\",\n \"docusaurus.config.mjs\",\n ],\n docsDir: \"./docs\",\n },\n {\n name: \"vitepress\",\n files: [\n \".vitepress/config.ts\",\n \".vitepress/config.js\",\n \".vitepress/config.mts\",\n \".vitepress/config.mjs\",\n ],\n docsDir: \"./docs\",\n },\n {\n name: \"mkdocs\",\n files: [\"mkdocs.yml\", \"mkdocs.yaml\"],\n docsDir: \"./docs\",\n },\n {\n name: \"starlight\",\n files: [\"astro.config.mjs\", \"astro.config.ts\", \"astro.config.js\"],\n docsDir: \"./src/content/docs\",\n },\n];\n\nexport async function detectFramework(projectDir: string): Promise<FrameworkResult> {\n for (const detector of DETECTORS) {\n for (const file of detector.files) {\n const fullPath = path.join(projectDir, file);\n try {\n await fs.access(fullPath);\n\n if (detector.name === \"starlight\") {\n const content = await fs.readFile(fullPath, \"utf-8\");\n if (!content.includes(\"starlight\")) {\n continue;\n }\n }\n\n return {\n name: detector.name,\n configFile: fullPath,\n docsDir: detector.docsDir,\n };\n } catch {\n // File doesn't exist, try next\n }\n }\n }\n\n return {\n name: \"generic\",\n configFile: null,\n docsDir: \"./docs\",\n };\n}\n"],"mappings":"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","names":["path","fs","path","path","fs","__dirname","path"]}
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands/init.ts","../src/frameworks/detector.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\nimport { initCommand } from \"./commands/init.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nasync function getVersion(): Promise<string> {\n let dir = __dirname;\n for (let i = 0; i < 5; i++) {\n try {\n const pkg = JSON.parse(await readFile(path.join(dir, \"package.json\"), \"utf-8\"));\n return pkg.version;\n } catch {\n dir = path.dirname(dir);\n }\n }\n return \"0.0.0\";\n}\n\nasync function main(): Promise<void> {\n const version = await getVersion();\n\n const program = new Command();\n\n program\n .name(\"docs-ready\")\n .description(\"Make your docs AI-ready. Keep them that way.\")\n .version(version);\n\n program\n .command(\"init\")\n .description(\"Initialize docs-ready in your project\")\n .action(async () => {\n await initCommand();\n });\n\n program\n .command(\"generate\")\n .description(\"Generate AI-facing documentation files\")\n .option(\"--dry-run\", \"Show what would be generated without writing files\")\n .option(\"--only <type>\", \"Generate only: llms-txt, llms-full, or ai-context\")\n .action(async (opts) => {\n const { generateCommand } = await import(\"./commands/generate.js\");\n await generateCommand({ dryRun: opts.dryRun, only: opts.only });\n });\n\n program\n .command(\"guard\")\n .description(\"Check AI-facing docs for staleness\")\n .option(\"--output <format>\", \"Output format: console, json, or markdown\", \"console\")\n .action(async (opts) => {\n const { guardCommand } = await import(\"./commands/guard.js\");\n await guardCommand({ output: opts.output });\n });\n\n program\n .command(\"validate\")\n .description(\"Lint and validate AI-facing docs\")\n .action(() => {\n console.log(\"Validate command coming in v0.6.0\");\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\nimport { stdin, stdout } from \"node:process\";\nimport { detectFramework } from \"../../frameworks/detector.js\";\nimport { log, spinner } from \"../../utils/logger.js\";\n\ninterface InitOptions {\n cwd?: string;\n}\n\nexport async function initCommand(options: InitOptions = {}): Promise<void> {\n const cwd = options.cwd ?? process.cwd();\n const configPath = path.join(cwd, \".docs-ready.yaml\");\n\n // Check if config already exists\n try {\n await fs.access(configPath);\n log.warn(\".docs-ready.yaml already exists. Use --force to overwrite.\");\n return;\n } catch {\n // Config doesn't exist, proceed\n }\n\n const spin = spinner(\"Detecting project structure...\");\n spin.start();\n\n // Detect framework\n const framework = await detectFramework(cwd);\n spin.stop();\n\n log.info(`Detected framework: ${framework.name}`);\n log.info(`Docs directory: ${framework.docsDir}`);\n\n // Detect deployment platform\n const platform = await detectPlatform(cwd);\n if (platform !== \"none\") {\n log.info(`Detected platform: ${platform}`);\n }\n\n // Detect docusaurus-plugin-llms\n const hasLlmsPlugin = await detectLlmsPlugin(cwd);\n if (hasLlmsPlugin) {\n log.warn(\"Detected docusaurus-plugin-llms — disabling llms.txt/llms-full.txt generation.\");\n }\n\n // Prompt for project info\n const rl = createInterface({ input: stdin, output: stdout });\n const title = await rl.question(\"Project title: \");\n const description = await rl.question(\"Description: \");\n const url = await rl.question(\"Docs URL (e.g. https://docs.example.com): \");\n rl.close();\n\n // Render config from template (try file first, fall back to inline)\n const templateData: Record<string, string> = {\n title: title || \"My Project\",\n description: description || \"Project documentation\",\n url: url || \"https://docs.example.com\",\n docsDir: framework.docsDir,\n llmsTxt: String(!hasLlmsPlugin),\n llmsFullTxt: String(!hasLlmsPlugin),\n platform,\n };\n\n const template = await loadTemplate();\n const rendered = template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => templateData[key] ?? \"\");\n\n await fs.writeFile(configPath, rendered, \"utf-8\");\n log.success(\"Created .docs-ready.yaml\");\n log.dim(\"Next: run `docs-ready generate` to create AI-facing files.\");\n}\n\nasync function loadTemplate(): Promise<string> {\n // Try to load the template file (works when installed as npm package)\n const candidates = [\n path.join(path.dirname(new URL(import.meta.url).pathname), \"../../templates/config.yaml.tmpl\"),\n path.join(path.dirname(new URL(import.meta.url).pathname), \"../templates/config.yaml.tmpl\"),\n ];\n\n for (const candidate of candidates) {\n try {\n return await fs.readFile(candidate, \"utf-8\");\n } catch {\n // Try next\n }\n }\n\n // Inline fallback\n return `# docs-ready configuration\ntitle: \"{{title}}\"\ndescription: \"{{description}}\"\nurl: \"{{url}}\"\ndocs:\n dir: \"{{docsDir}}\"\n include:\n - \"**/*.md\"\n - \"**/*.mdx\"\n exclude:\n - \"**/node_modules/**\"\n - \"**/_*\"\ngenerate:\n llms_txt: {{llmsTxt}}\n llms_full_txt: {{llmsFullTxt}}\n ai_context: true\n output_dir: \"./build\"\ndeploy:\n platform: \"{{platform}}\"\n`;\n}\n\nasync function detectPlatform(\n cwd: string\n): Promise<\"vercel\" | \"netlify\" | \"cloudflare\" | \"none\"> {\n const checks: Array<{ file: string; platform: \"vercel\" | \"netlify\" | \"cloudflare\" }> = [\n { file: \"vercel.json\", platform: \"vercel\" },\n { file: \"netlify.toml\", platform: \"netlify\" },\n { file: \"_headers\", platform: \"cloudflare\" },\n { file: \"wrangler.toml\", platform: \"cloudflare\" },\n ];\n\n for (const { file, platform } of checks) {\n try {\n await fs.access(path.join(cwd, file));\n return platform;\n } catch {\n // Not found, continue\n }\n }\n\n return \"none\";\n}\n\nasync function detectLlmsPlugin(cwd: string): Promise<boolean> {\n try {\n const pkgPath = path.join(cwd, \"package.json\");\n const content = await fs.readFile(pkgPath, \"utf-8\");\n const pkg = JSON.parse(content);\n const allDeps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n return \"docusaurus-plugin-llms\" in allDeps;\n } catch {\n return false;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport interface FrameworkResult {\n name: \"docusaurus\" | \"vitepress\" | \"mkdocs\" | \"starlight\" | \"generic\";\n configFile: string | null;\n docsDir: string;\n}\n\nconst DETECTORS: Array<{\n name: FrameworkResult[\"name\"];\n files: string[];\n docsDir: string;\n}> = [\n {\n name: \"docusaurus\",\n files: [\n \"docusaurus.config.ts\",\n \"docusaurus.config.js\",\n \"docusaurus.config.mts\",\n \"docusaurus.config.mjs\",\n ],\n docsDir: \"./docs\",\n },\n {\n name: \"vitepress\",\n files: [\n \".vitepress/config.ts\",\n \".vitepress/config.js\",\n \".vitepress/config.mts\",\n \".vitepress/config.mjs\",\n ],\n docsDir: \"./docs\",\n },\n {\n name: \"mkdocs\",\n files: [\"mkdocs.yml\", \"mkdocs.yaml\"],\n docsDir: \"./docs\",\n },\n {\n name: \"starlight\",\n files: [\"astro.config.mjs\", \"astro.config.ts\", \"astro.config.js\"],\n docsDir: \"./src/content/docs\",\n },\n];\n\nexport async function detectFramework(projectDir: string): Promise<FrameworkResult> {\n for (const detector of DETECTORS) {\n for (const file of detector.files) {\n const fullPath = path.join(projectDir, file);\n try {\n await fs.access(fullPath);\n\n if (detector.name === \"starlight\") {\n const content = await fs.readFile(fullPath, \"utf-8\");\n if (!content.includes(\"starlight\")) {\n continue;\n }\n }\n\n return {\n name: detector.name,\n configFile: fullPath,\n docsDir: detector.docsDir,\n };\n } catch {\n // File doesn't exist, try next\n }\n }\n }\n\n return {\n name: \"generic\",\n configFile: null,\n docsDir: \"./docs\",\n };\n}\n"],"mappings":"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","names":["path","fs","path","path","fs","__dirname","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docs-ready",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Make your docs AI-ready. Keep them that way.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -58,6 +58,7 @@
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/node": "^25.5.2",
61
+ "msw": "^2.12.14",
61
62
  "tsup": "^8.2.0",
62
63
  "typescript": "^5.5.0",
63
64
  "vitest": "^2.0.0"