Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/agent-knowledge-supply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@modern-js/app-tools': minor
'@modern-js/create': minor
---

feat: agent knowledge supply — bundle version-matched English docs into the app-tools tarball (`main-doc/docs/en/`) on publish, and generate `AGENTS.md` / `CLAUDE.md` in new projects created by `@modern-js/create` (skip with `--no-agents-md`); also fixes boolean flags swallowing the following positional argument (e.g. `create --sub my-app`)

feat: Agent 知识供给 —— 发布时将版本匹配的英文文档打进 app-tools tarball(`main-doc/docs/en/`),并在 `@modern-js/create` 新建项目时默认生成 `AGENTS.md` / `CLAUDE.md`(`--no-agents-md` 可跳过);同时修复布尔参数吞掉后续位置参数的问题(如 `create --sub my-app`)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"prepare-build": "cross-env NX_DAEMON=false NX_REJECT_UNKNOWN_LOCAL_CACHE=0 nx run-many -t build -p @modern-js/* --exclude=@modern-js/main-doc,@modern-js/module-tools-docs --maxParallel=4",
"prepare": "npm run prepare-build && husky install",
"prepare-build-continue": "cross-env NX_DAEMON=false NX_REJECT_UNKNOWN_LOCAL_CACHE=0 nx run-many -t build -p @modern-js/* --exclude=@modern-js/main-doc,@modern-js/module-tools-docs --nxBail=false --maxParallel=4 || echo 'Build completed with some failures, continuing...'",
"build:required": "cross-env NX_DAEMON=false nx run-many -t build -p @modern-js/builder @modern-js/bff-core @modern-js/utils",
"build:required": "cross-env NX_DAEMON=false nx run-many -t build -p @modern-js/builder @modern-js/bff-core @modern-js/utils @modern-js/i18n-utils",
"lint": "biome check",
"change": "changeset add",
"bump": "changeset version",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ Modern.js docs follow the [llms.txt specification](https://llmstxt.org/), auto-g

Most "what is this API / config" questions can be answered from llms.txt. Just let your agent retrieve it on demand; no need to copy docs into your project.

## Bundled docs & AGENTS.md

Every new Modern.js project is agent-ready out of the box:

- **Bundled docs**: the `@modern-js/app-tools` package ships the full English docs at `node_modules/@modern-js/app-tools/main-doc/docs/en/` — they always match your installed framework version, no network required.
- **AGENTS.md / CLAUDE.md**: `npx @modern-js/create` generates both files by default. `AGENTS.md` instructs agents to read the bundled docs before writing any Modern.js code (so they don't rely on outdated training data); `CLAUDE.md` includes it for Claude Code via the `@AGENTS.md` directive. Pass `--no-agents-md` to skip generating them; they are also skipped in `--sub` (subproject) mode, where agent files belong to the monorepo root.

The Modern.js-managed part of `AGENTS.md` is wrapped in `<!-- BEGIN:modernjs-agent-rules -->` / `<!-- END:modernjs-agent-rules -->` markers. Anything you write outside the markers is yours and will never be touched by Modern.js tooling.

## Skills

Skills are on-demand AI capabilities following the [Agent Skills open standard](https://github.com/vercel-labs/skills). User-facing Skills:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ Modern.js 文档遵循 [llms.txt 规范](https://llmstxt.org/),由 [`@rspress/

大部分「这个 API / 配置是什么」类问题,靠 llms.txt 即可解决。让你的 Agent 按需检索它即可,不必把文档复制进项目。

## 随包文档与 AGENTS.md

每个新建的 Modern.js 项目都开箱即为 agent-ready:

- **随包文档**:`@modern-js/app-tools` 包内置了完整英文文档(`node_modules/@modern-js/app-tools/main-doc/docs/en/`)——与你安装的框架版本严格一致,离线可用。
- **AGENTS.md / CLAUDE.md**:`npx @modern-js/create` 默认生成这两个文件。`AGENTS.md` 指示 agent 在编写任何 Modern.js 代码前先阅读随包文档(避免依赖过时的训练数据);`CLAUDE.md` 通过 `@AGENTS.md` 指令供 Claude Code 引用。可通过 `--no-agents-md` 跳过生成;`--sub`(子项目)模式下也不会生成——monorepo 场景的 agent 文件应由仓库根统一管理。

`AGENTS.md` 中由 Modern.js 管理的部分包裹在 `<!-- BEGIN:modernjs-agent-rules -->` / `<!-- END:modernjs-agent-rules -->` 标记内。标记之外的内容完全归你所有,Modern.js 工具永远不会改动。

## Skills

Skills 是按需触发的 AI 辅助能力,遵循 [Agent Skills 开放标准](https://github.com/vercel-labs/skills)。用户向 Skills:
Expand Down
1 change: 1 addition & 0 deletions packages/solutions/app-tools/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
main-doc
4 changes: 4 additions & 0 deletions packages/solutions/app-tools/.npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ jest.config.js
tsconfig.json
CHANGELOG.md


# docs bundle for AI agents (copy-main-doc.mjs) — re-include everything
!main-doc/
!main-doc/**
2 changes: 1 addition & 1 deletion packages/solutions/app-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"modern-app": "./bin/modern.js"
},
"scripts": {
"prepublishOnly": "only-allow-pnpm",
"prepublishOnly": "only-allow-pnpm && node ./scripts/copy-main-doc.mjs",
"dev": "rslib build --watch",
"build": "rslib build",
"test": "rstest"
Expand Down
46 changes: 46 additions & 0 deletions packages/solutions/app-tools/scripts/copy-main-doc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

// Copies the English docs from packages/document into this package as
// `main-doc/docs/en`, so every project gets version-matched offline docs
// for AI coding agents (see AGENTS.md generated by @modern-js/create).
// Runs on prepublishOnly — the bundle only exists in published tarballs,
// never in the working tree (the directory is gitignored).
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const pkgRoot = path.resolve(__dirname, '..');
const source = path.resolve(pkgRoot, '../../document/docs/en');
const bundleRoot = path.resolve(pkgRoot, 'main-doc');
const target = path.join(bundleRoot, 'docs/en');

// Top-level doc sections excluded from the bundle to keep it small.
// Agents rarely need these while coding (blog/showcase, narrative
// tutorials, framework-plugin authoring); llms.txt covers them online.
// Keep this list in sync with scripts/check-doc-bundle.mjs.
export const EXCLUDED_SECTIONS = ['community', 'tutorials', 'plugin'];

if (!fs.existsSync(source)) {
console.error(`[copy-main-doc] source docs not found: ${source}`);
process.exit(1);
}

fs.rmSync(bundleRoot, { recursive: true, force: true });
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.cpSync(source, target, {
recursive: true,
filter: src => {
const rel = path.relative(source, src);
const top = rel.split(path.sep)[0];
return rel === '' || !EXCLUDED_SECTIONS.includes(top);
},
});

const count = fs
.readdirSync(target, { recursive: true, withFileTypes: true })
.filter(entry => entry.isFile()).length;

if (count === 0) {
console.error('[copy-main-doc] no files copied');
process.exit(1);
}
console.log(`[copy-main-doc] bundled ${count} doc files into main-doc/docs/en`);
4 changes: 3 additions & 1 deletion packages/toolkit/create/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@
"scripts": {
"build": "rslib build",
"dev": "rslib build -w",
"start": "node ./dist/index.js"
"start": "node ./dist/index.js",
"test": "rstest"
},
"devDependencies": {
"@modern-js/i18n-utils": "workspace:*",
"@modern-js/rslib": "workspace:*",
"@rslib/core": "0.23.2",
"@scripts/rstest-config": "workspace:*",
"@types/node": "^20",
"tsx": "^4.22.4",
"typescript": "^5"
Expand Down
7 changes: 7 additions & 0 deletions packages/toolkit/create/rstest.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { withTestPreset } from '@scripts/rstest-config';

export default withTestPreset({
root: __dirname,
testEnvironment: 'node',
globals: true,
});
58 changes: 39 additions & 19 deletions packages/toolkit/create/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function showHelp() {
console.log(i18n.t(localeKeys.help.optionVersion));
console.log(i18n.t(localeKeys.help.optionLang));
console.log(i18n.t(localeKeys.help.optionSub));
console.log(i18n.t(localeKeys.help.optionNoAgentsMd));
console.log('');
console.log(i18n.t(localeKeys.help.examples));
console.log(i18n.t(localeKeys.help.example1));
Expand Down Expand Up @@ -96,6 +97,20 @@ function promptInput(question: string): Promise<string> {
});
}

// flags that consume the next argument as their value
const VALUE_FLAGS = ['--lang', '-l'];
// flags that stand alone
const BOOLEAN_FLAGS = [
'--help',
'-h',
'--version',
'-v',
'--sub',
'-s',
'--no-sub',
'--no-agents-md',
];

function detectSubprojectFlag(): boolean | null {
const args = process.argv.slice(2);
if (args.includes('--sub') || args.includes('-s')) {
Expand All @@ -107,6 +122,10 @@ function detectSubprojectFlag(): boolean | null {
return null;
}

function detectNoAgentsMdFlag(): boolean {
return process.argv.slice(2).includes('--no-agents-md');
}

function isDirectoryEmpty(dirPath: string): boolean {
if (!fs.existsSync(dirPath)) {
return false;
Expand All @@ -124,27 +143,14 @@ async function getProjectName(): Promise<{
useCurrentDir: boolean;
}> {
const args = process.argv.slice(2);
// a positional argument is anything that is not a flag itself and not
// the value of a value-consuming flag (only --lang/-l takes a value;
// boolean flags must not swallow the argument that follows them)
const projectNameArg = args.find(
(arg, index) =>
arg !== '--lang' &&
arg !== '-l' &&
arg !== '--help' &&
arg !== '-h' &&
arg !== '--version' &&
arg !== '-v' &&
arg !== '--sub' &&
arg !== '-s' &&
arg !== '--no-sub' &&
(index === 0 ||
(args[index - 1] !== '--lang' &&
args[index - 1] !== '-l' &&
args[index - 1] !== '--help' &&
args[index - 1] !== '-h' &&
args[index - 1] !== '--version' &&
args[index - 1] !== '-v' &&
args[index - 1] !== '--sub' &&
args[index - 1] !== '-s' &&
args[index - 1] !== '--no-sub')),
!VALUE_FLAGS.includes(arg) &&
!BOOLEAN_FLAGS.includes(arg) &&
!(index > 0 && VALUE_FLAGS.includes(args[index - 1])),
);

if (projectNameArg) {
Expand Down Expand Up @@ -202,11 +208,13 @@ async function main() {

const subprojectFlag = detectSubprojectFlag();
const isSubproject = subprojectFlag === true;
const noAgentsMd = detectNoAgentsMdFlag();

copyTemplate(templateDir, targetDir, {
packageName: projectName,
version,
isSubproject,
noAgentsMd,
});

const targetPackageJson = path.join(targetDir, 'package.json');
Expand Down Expand Up @@ -237,6 +245,9 @@ async function main() {
const reset = '\x1b[0m';

console.log(`${i18n.t(localeKeys.message.success)}\n`);
if (!noAgentsMd && !isSubproject) {
console.log(`${i18n.t(localeKeys.message.agentsMd)}\n`);
}
console.log(i18n.t(localeKeys.message.nextSteps));
if (!useCurrentDir) {
console.log(
Expand All @@ -254,6 +265,7 @@ function copyTemplate(
packageName: string;
version: string;
isSubproject: boolean;
noAgentsMd: boolean;
},
) {
fs.mkdirSync(dest, { recursive: true });
Expand All @@ -263,15 +275,23 @@ function copyTemplate(
'biome.json',
'.npmrc',
'.nvmrc',
// agent files are managed at the monorepo root in subproject setups
'AGENTS.md',
'CLAUDE.md',
];

const agentFiles = ['AGENTS.md', 'CLAUDE.md'];

function copyRecursive(srcDir: string, destDir: string) {
const entries = fs.readdirSync(srcDir, { withFileTypes: true });

for (const entry of entries) {
if (options.isSubproject && excludeInSubproject.includes(entry.name)) {
continue;
}
if (options.noAgentsMd && agentFiles.includes(entry.name)) {
continue;
}

const srcPath = path.join(srcDir, entry.name);
let destPath = path.join(destDir, entry.name);
Expand Down
4 changes: 4 additions & 0 deletions packages/toolkit/create/src/locale/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const EN_LOCALE = {
message: {
welcome: '🚀 Welcome to Modern.js',
success: '✨ Created successfully!',
agentsMd:
'✔ AGENTS.md & CLAUDE.md generated — AI coding agents will pick them up automatically. (--no-agents-md to skip)',
nextSteps: '📋 Next steps:',
step1: 'cd {projectName}',
step2: 'pnpm install',
Expand All @@ -26,6 +28,8 @@ export const EN_LOCALE = {
optionVersion: ' -v, --version Display version information',
optionLang: ' -l, --lang Set the language (zh or en)',
optionSub: ' -s, --sub Mark as a subproject (package in monorepo)',
optionNoAgentsMd:
' --no-agents-md Skip generating AGENTS.md / CLAUDE.md for AI coding agents',
examples: '💡 Examples:',
example1: ' create my-app',
example2: ' create my-app --lang zh',
Expand Down
4 changes: 4 additions & 0 deletions packages/toolkit/create/src/locale/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const ZH_LOCALE = {
message: {
welcome: '🚀 欢迎使用 Modern.js',
success: '✨ 创建成功!',
agentsMd:
'✔ 已生成 AGENTS.md 和 CLAUDE.md —— AI 编码助手会自动读取。(--no-agents-md 可跳过)',
nextSteps: '📋 下一步:',
step1: 'cd {projectName}',
step2: 'pnpm install',
Expand All @@ -25,6 +27,8 @@ export const ZH_LOCALE = {
optionVersion: ' -v, --version 显示版本信息',
optionLang: ' -l, --lang 设置语言 (zh 或 en)',
optionSub: ' -s, --sub 标记为子项目(monorepo 中的子包)',
optionNoAgentsMd:
' --no-agents-md 跳过生成 AGENTS.md / CLAUDE.md(AI 编码助手指引文件)',
examples: '💡 示例:',
example1: ' create my-app',
example2: ' create my-app --lang zh',
Expand Down
30 changes: 30 additions & 0 deletions packages/toolkit/create/template/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!-- BEGIN:modernjs-agent-rules -->

# Modern.js: read the docs before you code

Your training data about Modern.js is likely outdated. This project uses
Modern.js v3 — APIs, config, and conventions may differ from what you know.

Before writing or changing any Modern.js-related code:

1. Read the bundled docs at `node_modules/@modern-js/app-tools/main-doc/docs/en/`
— they exactly match the installed framework version. The files are MDX:
when a page imports local content via `@site-docs-en/*`, resolve it to
`main-doc/docs/en/*` and read that file too — key content often lives in
those components. Imports pointing outside the docs tree (e.g.
`../../src/sandbox/...`) are not bundled — consult the online docs for those.
2. If the bundled docs are missing, fetch https://modernjs.dev/llms.txt
for the full doc index (plain markdown).
3. For multi-step workflows (v2→v3 migration, enabling BFF/SSG/Tailwind):
if the `modernjs-*` skills are already installed, use them; otherwise
suggest the user run `npx skills add web-infra-dev/modern.js` — do not
install skills yourself.

Key facts:

- Dev server: `pnpm dev` (modern dev). Build: `pnpm build` (modern build).
- Config lives in `modern.config.ts` (`defineConfig` from `@modern-js/app-tools`).
- File-system routing under `src/routes/` (`layout.tsx` / `page.tsx` convention).
- Do NOT use `modern new` — it was removed in v3; use the skills above instead.

<!-- END:modernjs-agent-rules -->
1 change: 1 addition & 0 deletions packages/toolkit/create/template/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
Loading
Loading