nuxt-agent-md 0.2.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/README.md +19 -25
- package/dist/cli.js +43 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# nuxt-agent-md
|
|
2
2
|
|
|
3
|
-
Generate `AGENTS.md` with Nuxt documentation for AI coding agents.
|
|
3
|
+
Generate `AGENTS.md` and `CLAUDE.md` with Nuxt documentation for AI coding agents (Cursor, Copilot, Claude Code, etc.).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Gives your AI assistant instant access to accurate Nuxt API references, reducing hallucinations and improving code quality.
|
|
6
6
|
|
|
7
7
|
## Usage
|
|
8
8
|
|
|
@@ -18,9 +18,16 @@ nuxt-agent-md
|
|
|
18
18
|
## What it does
|
|
19
19
|
|
|
20
20
|
1. Detects your Nuxt version from `package.json`
|
|
21
|
-
2. Downloads the corresponding `@nuxt/docs` documentation
|
|
22
|
-
3. Generates a minified index of all documentation files
|
|
23
|
-
4. Creates/updates `AGENTS.md` with the index
|
|
21
|
+
2. Downloads the corresponding `@nuxt/docs` documentation (~1.5 MB)
|
|
22
|
+
3. Generates a minified index (~20 KB) of all documentation files
|
|
23
|
+
4. Creates/updates `AGENTS.md` with the index
|
|
24
|
+
5. Creates `CLAUDE.md` that references `@AGENTS.md`
|
|
25
|
+
|
|
26
|
+
The index format is pipe-delimited for minimal token usage:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
CATEGORY|path/to/file.md|keyword1,keyword2,keyword3
|
|
30
|
+
```
|
|
24
31
|
|
|
25
32
|
## Options
|
|
26
33
|
|
|
@@ -48,36 +55,23 @@ nuxt-agent-md --no-minify
|
|
|
48
55
|
|
|
49
56
|
# Custom output paths
|
|
50
57
|
nuxt-agent-md -d .docs -o CLAUDE.md
|
|
51
|
-
|
|
52
|
-
# Preview changes without writing
|
|
53
|
-
nuxt-agent-md --dry-run
|
|
54
58
|
```
|
|
55
59
|
|
|
56
60
|
## Output
|
|
57
61
|
|
|
58
62
|
The tool generates:
|
|
59
63
|
|
|
60
|
-
1. `.nuxt-docs/` - Directory containing
|
|
61
|
-
2. `AGENTS.md` - File with
|
|
62
|
-
|
|
63
|
-
The index format is pipe-delimited for minimal token usage:
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
CATEGORY|path/to/file.md|keyword1,keyword2,keyword3
|
|
67
|
-
```
|
|
64
|
+
1. `.nuxt-docs/` - Directory containing markdown documentation (auto-added to `.gitignore`)
|
|
65
|
+
2. `AGENTS.md` - File with documentation index (wrapped in `<!-- BEGIN:nuxt-agent-rules -->` markers)
|
|
66
|
+
3. `CLAUDE.md` - File that references `@AGENTS.md` for Claude Code/Cursor compatibility
|
|
68
67
|
|
|
69
|
-
## Why
|
|
68
|
+
## Why?
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
AI coding agents work better when they have access to accurate documentation rather than relying on training data that may be outdated. By providing a compact index in `AGENTS.md`, the agent can quickly find and read the relevant documentation files for any Nuxt API.
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|----------|--------------|
|
|
75
|
-
| Baseline (no docs) | 53% |
|
|
76
|
-
| Skills | 53% |
|
|
77
|
-
| Skills with explicit instructions | 79% |
|
|
78
|
-
| **AGENTS.md with docs index** | **100%** |
|
|
72
|
+
## Credits
|
|
79
73
|
|
|
80
|
-
|
|
74
|
+
Inspired by [Vercel's research](https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals) showing that documentation indexes in `AGENTS.md` significantly improve agent accuracy.
|
|
81
75
|
|
|
82
76
|
## License
|
|
83
77
|
|
package/dist/cli.js
CHANGED
|
@@ -38,8 +38,10 @@ function mapToDocsVersion(nuxtVersion) {
|
|
|
38
38
|
|
|
39
39
|
// src/download.ts
|
|
40
40
|
import { spawnSync } from "node:child_process";
|
|
41
|
-
import { existsSync as existsSync2, mkdirSync, rmSync, renameSync } from "node:fs";
|
|
41
|
+
import { existsSync as existsSync2, mkdirSync, rmSync, renameSync, readdirSync } from "node:fs";
|
|
42
42
|
import { join as join2 } from "node:path";
|
|
43
|
+
var CLEANUP_FILES = ["LICENSE", "README.md", "package.json", ".navigation.yml"];
|
|
44
|
+
var CLEANUP_DIRS = ["bridge", "community", "migration"];
|
|
43
45
|
async function downloadDocs(version, targetDir) {
|
|
44
46
|
const tempDir = ".nuxt-docs-temp";
|
|
45
47
|
if (existsSync2(targetDir))
|
|
@@ -63,6 +65,17 @@ async function downloadDocs(version, targetDir) {
|
|
|
63
65
|
if (extract.status !== 0)
|
|
64
66
|
throw new Error(extract.stderr);
|
|
65
67
|
renameSync(join2(tempDir, "package"), targetDir);
|
|
68
|
+
for (const file of CLEANUP_FILES) {
|
|
69
|
+
const path = join2(targetDir, file);
|
|
70
|
+
if (existsSync2(path))
|
|
71
|
+
rmSync(path);
|
|
72
|
+
}
|
|
73
|
+
for (const item of readdirSync(targetDir)) {
|
|
74
|
+
const dirName = item.replace(/^\d+\./, "");
|
|
75
|
+
if (CLEANUP_DIRS.includes(dirName)) {
|
|
76
|
+
rmSync(join2(targetDir, item), { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
66
79
|
} finally {
|
|
67
80
|
if (existsSync2(tempDir))
|
|
68
81
|
rmSync(tempDir, { recursive: true });
|
|
@@ -70,7 +83,7 @@ async function downloadDocs(version, targetDir) {
|
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
// src/generate.ts
|
|
73
|
-
import { readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
86
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
74
87
|
import { join as join3, relative } from "node:path";
|
|
75
88
|
async function generateIndex(docsDir) {
|
|
76
89
|
const entries = [];
|
|
@@ -81,10 +94,11 @@ async function generateIndex(docsDir) {
|
|
|
81
94
|
full: generateFullIndex(entries, docsDir)
|
|
82
95
|
};
|
|
83
96
|
}
|
|
97
|
+
var EXCLUDED_DIRS = ["bridge", "community"];
|
|
84
98
|
function walkDir(dir, baseDir, entries) {
|
|
85
99
|
let items;
|
|
86
100
|
try {
|
|
87
|
-
items =
|
|
101
|
+
items = readdirSync2(dir);
|
|
88
102
|
} catch {
|
|
89
103
|
return;
|
|
90
104
|
}
|
|
@@ -97,7 +111,8 @@ function walkDir(dir, baseDir, entries) {
|
|
|
97
111
|
continue;
|
|
98
112
|
}
|
|
99
113
|
if (stat.isDirectory()) {
|
|
100
|
-
|
|
114
|
+
const dirName = item.replace(/^\d+\./, "");
|
|
115
|
+
if (!item.startsWith(".") && item !== "node_modules" && !EXCLUDED_DIRS.includes(dirName)) {
|
|
101
116
|
walkDir(fullPath, baseDir, entries);
|
|
102
117
|
}
|
|
103
118
|
} else if (item.endsWith(".md") && !item.startsWith(".")) {
|
|
@@ -186,7 +201,7 @@ function generateMinifiedIndex(entries, docsDir) {
|
|
|
186
201
|
});
|
|
187
202
|
const lines = [];
|
|
188
203
|
for (const entry of sorted) {
|
|
189
|
-
const keywords = entry.keywords.slice(0,
|
|
204
|
+
const keywords = entry.keywords.slice(0, 5).join(",");
|
|
190
205
|
lines.push(`${entry.category}|${docsDir}/${entry.path}|${keywords}`);
|
|
191
206
|
}
|
|
192
207
|
return lines.join(`
|
|
@@ -221,17 +236,23 @@ function generateFullIndex(entries, docsDir) {
|
|
|
221
236
|
}
|
|
222
237
|
|
|
223
238
|
// src/inject.ts
|
|
239
|
+
import { dirname, join as join4 } from "node:path";
|
|
224
240
|
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
225
|
-
var START_MARKER = "<!--
|
|
226
|
-
var END_MARKER = "<!--
|
|
241
|
+
var START_MARKER = "<!-- BEGIN:nuxt-agent-rules -->";
|
|
242
|
+
var END_MARKER = "<!-- END:nuxt-agent-rules -->";
|
|
227
243
|
async function injectAgentsMd(outputPath, index, nuxtVersion, docsDir, minify) {
|
|
228
244
|
let content = "";
|
|
229
245
|
if (existsSync3(outputPath)) {
|
|
230
246
|
content = readFileSync3(outputPath, "utf-8");
|
|
231
247
|
}
|
|
232
248
|
const nuxtSection = generateNuxtSection(index, nuxtVersion, docsDir, minify);
|
|
249
|
+
const oldStartMarker = "<!-- NUXT_DOCS_START -->";
|
|
250
|
+
const oldEndMarker = "<!-- NUXT_DOCS_END -->";
|
|
233
251
|
if (content.includes(START_MARKER) && content.includes(END_MARKER)) {
|
|
234
|
-
const regex = new RegExp(`${START_MARKER}[\\s\\S]*${END_MARKER}`, "m");
|
|
252
|
+
const regex = new RegExp(`${escapeRegex(START_MARKER)}[\\s\\S]*${escapeRegex(END_MARKER)}`, "m");
|
|
253
|
+
content = content.replace(regex, nuxtSection);
|
|
254
|
+
} else if (content.includes(oldStartMarker) && content.includes(oldEndMarker)) {
|
|
255
|
+
const regex = new RegExp(`${escapeRegex(oldStartMarker)}[\\s\\S]*${escapeRegex(oldEndMarker)}`, "m");
|
|
235
256
|
content = content.replace(regex, nuxtSection);
|
|
236
257
|
} else if (content.length > 0) {
|
|
237
258
|
content = content.trimEnd() + `
|
|
@@ -241,6 +262,18 @@ async function injectAgentsMd(outputPath, index, nuxtVersion, docsDir, minify) {
|
|
|
241
262
|
content = generateFullAgentsMd(nuxtSection);
|
|
242
263
|
}
|
|
243
264
|
writeFileSync(outputPath, content);
|
|
265
|
+
generateClaudeMd(outputPath);
|
|
266
|
+
}
|
|
267
|
+
function escapeRegex(str) {
|
|
268
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
269
|
+
}
|
|
270
|
+
function generateClaudeMd(agentsMdPath) {
|
|
271
|
+
const dir = dirname(agentsMdPath);
|
|
272
|
+
const claudeMdPath = join4(dir, "CLAUDE.md");
|
|
273
|
+
const agentsMdFilename = agentsMdPath.split("/").pop() || "AGENTS.md";
|
|
274
|
+
const claudeMdContent = `@${agentsMdFilename}
|
|
275
|
+
`;
|
|
276
|
+
writeFileSync(claudeMdPath, claudeMdContent, "utf-8");
|
|
244
277
|
}
|
|
245
278
|
function generateNuxtSection(index, nuxtVersion, docsDir, minify) {
|
|
246
279
|
const majorVersion = nuxtVersion.split(".")[0];
|
|
@@ -339,7 +372,8 @@ async function generateAgentsMd(options = {}) {
|
|
|
339
372
|
if (updateGitignore(docsDir)) {
|
|
340
373
|
console.log(`Added ${docsDir} to .gitignore`);
|
|
341
374
|
}
|
|
342
|
-
|
|
375
|
+
const claudeMdPath = outputPath.replace(/[^/]+$/, "CLAUDE.md");
|
|
376
|
+
console.log(`Generated ${outputPath} and ${claudeMdPath}`);
|
|
343
377
|
}
|
|
344
378
|
|
|
345
379
|
// src/cli.ts
|