mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
## Summary
Adds a new `agents-md` subcommand to `@next/codemod` that generates a
Next.js documentation index for AI coding agents (Claude, Cursor, GitHub
Copilot, etc.).
## Usage
```bash
# Interactive mode - prompts for version and target file
npx @next/codemod agents-md
# Non-interactive mode
npx @next/codemod agents-md --version 15.1.0 --output CLAUDE.md
# Auto-detect version, specify output
npx @next/codemod agents-md --output AGENTS.md
```
## What it does
1. Downloads the Next.js documentation matching your project's version
to `.next-docs/` (via git sparse-checkout)
2. Generates a compact index of all doc files grouped by directory
3. Injects the index into `CLAUDE.md` or `AGENTS.md` between marker
comments
4. Adds `.next-docs/` to `.gitignore`
The index helps AI agents prefer local documentation over pre-trained
knowledge, ensuring answers match your Next.js version.
## Options
| Option | Description |
|--------|-------------|
| `--version <version>` | Specify Next.js version (auto-detected from
`package.json` if not provided) |
| `--output <file>` | Target file path (e.g., `CLAUDE.md`, `AGENTS.md`)
|
## Output format
The generated index is a single line with pipe separators:
```
<!-- NEXT-AGENTS-MD-START -->[Next.js Docs Index]|root: ./.next-docs|IMPORTANT: Prefer retrieval-led reasoning...|dir:{file1.mdx,file2.mdx}|...<!-- NEXT-AGENTS-MD-END -->
```
## Test plan
- [x] Build passes
- [x] Tested interactive mode
- [x] Tested non-interactive mode with `--version` and `--output`
- [x] Tested auto-detect version with `--output` only
- [x] Verified `.gitignore` is updated
- [x] Verified index is injected correctly (new file and existing file)
## Eval results
Tested against 19 Next.js-specific agent evals with 100% pass rate:
| Eval | Result |
|------|--------|
| agent-000-app-router-migration-simple | ✅✅✅ (89.5s) |
| agent-021-avoid-fetch-in-effect | ✅✅✅ (90.4s) |
| agent-022-prefer-server-actions | ✅✅✅ (67.1s) |
| agent-023-avoid-getserversideprops | ✅✅✅ (102.5s) |
| agent-024-avoid-redundant-usestate | ✅✅✅ (62.9s) |
| agent-025-prefer-next-link | ✅✅✅ (69.5s) |
| agent-026-no-serial-await | ✅✅✅ (96.2s) |
| agent-027-prefer-next-image | ✅✅✅ (68.0s) |
| agent-028-prefer-next-font | ✅✅✅ (64.2s) |
| agent-029-use-cache-directive | ✅✅✅ (76.6s) |
| agent-030-app-router-migration-hard | ✅✅✅ (220.8s) |
| agent-031-proxy-middleware | ✅✅✅ (92.4s) |
| agent-032-use-cache-directive | ✅✅✅ (71.3s) |
| agent-033-forbidden-auth | ✅✅✅ (100.6s) |
| agent-034-async-cookies | ✅✅✅ (68.7s) |
| agent-035-connection-dynamic | ✅✅✅ (65.2s) |
| agent-036-after-response | ✅✅✅ (68.7s) |
| agent-037-updatetag-cache | ✅✅✅ (70.3s) |
| agent-038-refresh-settings | ✅✅✅ (71.8s) |
| **Overall** | **19/19/19 (100%, 100%, 100%)** |
Legend: ✅✅✅ = Build/Lint/Test
105 lines
3.4 KiB
JavaScript
105 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Copyright 2015-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*/
|
|
// Based on https://github.com/reactjs/react-codemod/blob/dd8671c9a470a2c342b221ec903c574cf31e9f57/bin/cli.js
|
|
// @next/codemod optional-name-of-transform optional/path/to/src [...options]
|
|
|
|
import { Command } from 'commander'
|
|
import { runUpgrade } from './upgrade'
|
|
import { runAgentsMd } from './agents-md'
|
|
import { runTransform } from './transform'
|
|
import { BadInput } from './shared'
|
|
|
|
const packageJson = require('../package.json')
|
|
const program = new Command(packageJson.name)
|
|
.description('Codemods for updating Next.js apps.')
|
|
.version(
|
|
packageJson.version,
|
|
'-v, --version',
|
|
'Output the current version of @next/codemod.'
|
|
)
|
|
.argument(
|
|
'[codemod]',
|
|
'Codemod slug to run. See "https://github.com/vercel/next.js/tree/canary/packages/next-codemod".'
|
|
)
|
|
.argument(
|
|
'[source]',
|
|
'Path to source files or directory to transform including glob patterns.'
|
|
)
|
|
.usage('[codemod] [source] [options]')
|
|
.helpOption('-h, --help', 'Display this help message.')
|
|
.option('-f, --force', 'Bypass Git safety checks and forcibly run codemods')
|
|
.option('-d, --dry', 'Dry run (no changes are made to files)')
|
|
.option(
|
|
'-p, --print',
|
|
'Print transformed files to stdout, useful for development'
|
|
)
|
|
.option('--verbose', 'Show more information about the transform process')
|
|
.option(
|
|
'-j, --jscodeshift',
|
|
'(Advanced) Pass options directly to jscodeshift'
|
|
)
|
|
.action(runTransform)
|
|
.allowUnknownOption()
|
|
// This is needed for options for subcommands to be passed correctly.
|
|
// Because by default the options are not positional, which will pass options
|
|
// to the main command "@next/codemod" even if it was passed after subcommands,
|
|
// e.g. "@next/codemod upgrade --verbose" will be treated as "next-codemod --verbose upgrade"
|
|
// By enabling this, it will respect the position of the options and pass it to subcommands.
|
|
// x-ref: https://github.com/tj/commander.js/pull/1427
|
|
.enablePositionalOptions()
|
|
|
|
program
|
|
.command('upgrade')
|
|
.description(
|
|
'Upgrade Next.js apps to desired versions with a single command.'
|
|
)
|
|
.argument(
|
|
'[revision]',
|
|
'Specify the upgrade type ("patch", "minor", "major"), an NPM dist tag (e.g. "latest", "canary", "rc"), or an exact version (e.g. "15.0.0"). Defaults to "minor".'
|
|
)
|
|
.usage('[revision] [options]')
|
|
.option('--verbose', 'Verbose output', false)
|
|
.action(async (revision, options) => {
|
|
try {
|
|
await runUpgrade(revision, options)
|
|
} catch (error) {
|
|
if (!options.verbose && error instanceof BadInput) {
|
|
console.error(error.message)
|
|
} else {
|
|
console.error(error)
|
|
}
|
|
process.exit(1)
|
|
}
|
|
})
|
|
|
|
program
|
|
.command('agents-md')
|
|
.description(
|
|
'Generate Next.js documentation index for AI coding agents (Claude, Cursor, etc.).'
|
|
)
|
|
.option(
|
|
'--version <version>',
|
|
'Next.js version (auto-detected if not provided)'
|
|
)
|
|
.option('--output <file>', 'Target file path (e.g., CLAUDE.md, AGENTS.md)')
|
|
.action(async (options) => {
|
|
try {
|
|
await runAgentsMd(options)
|
|
} catch (error) {
|
|
if (error instanceof BadInput) {
|
|
console.error(error.message)
|
|
} else {
|
|
console.error(error)
|
|
}
|
|
process.exit(1)
|
|
}
|
|
})
|
|
|
|
program.parse(process.argv)
|