Prefer AGENTS.md over CLAUDE.md for project instructions (#439)

* Prefer AGENTS.md over CLAUDE.md for project instructions

* fix: preserve CLAUDE.md fallback behavior

* fix: isolate onboarding tests and preserve legacy init

* fix: restore full fsOperations exports in test mock and align compact cwd

* Fix onboarding test isolation and init migration guidance

* Tighten init prompt coverage and onboarding copy

* Handle nested project instruction paths consistently

* Fix NEW_INIT feature gate for Bun build

---------

Co-authored-by: 赵小落 <zhaoxiaoluo@zhaoxiaoluodeMac-mini.local>
Co-authored-by: zhaomo01 <zhaomo01@baidu.com>
This commit is contained in:
ZhaoXiaoLuo
2026-04-12 21:31:33 +08:00
committed by GitHub
parent 2e0e14d713
commit b3f3dc4e66
18 changed files with 521 additions and 105 deletions

View File

@@ -0,0 +1,105 @@
import { describe, expect, test } from 'bun:test'
import { join } from 'node:path'
import {
findProjectInstructionFilePathInAncestors,
FALLBACK_PROJECT_INSTRUCTION_FILE,
getProjectInstructionFilePath,
getProjectInstructionFilePaths,
hasProjectInstructionFile,
isProjectInstructionFileName,
PRIMARY_PROJECT_INSTRUCTION_FILE,
} from './projectInstructions.js'
describe('projectInstructions', () => {
test('prefers AGENTS.md over CLAUDE.md for root project instructions', () => {
const dir = '/repo'
const existingPaths = new Set([
join(dir, PRIMARY_PROJECT_INSTRUCTION_FILE),
join(dir, FALLBACK_PROJECT_INSTRUCTION_FILE),
])
const filePath = getProjectInstructionFilePath(
dir,
path => existingPaths.has(path),
)
expect(filePath).toBe(join(dir, PRIMARY_PROJECT_INSTRUCTION_FILE))
})
test('falls back to CLAUDE.md when AGENTS.md is absent', () => {
const dir = '/repo'
const existingPaths = new Set([join(dir, FALLBACK_PROJECT_INSTRUCTION_FILE)])
const filePath = getProjectInstructionFilePath(
dir,
path => existingPaths.has(path),
)
expect(filePath).toBe(join(dir, FALLBACK_PROJECT_INSTRUCTION_FILE))
})
test('returns both candidate root instruction paths', () => {
const dir = '/repo'
expect(getProjectInstructionFilePaths(dir)).toEqual([
join(dir, PRIMARY_PROJECT_INSTRUCTION_FILE),
join(dir, FALLBACK_PROJECT_INSTRUCTION_FILE),
])
})
test('detects whether a repo instruction file exists', () => {
const dir = '/repo'
const existingPaths = new Set([join(dir, PRIMARY_PROJECT_INSTRUCTION_FILE)])
expect(hasProjectInstructionFile(dir, path => existingPaths.has(path))).toBe(
true,
)
expect(hasProjectInstructionFile(dir, () => false)).toBe(false)
})
test('recognizes AGENTS.md as a root instruction filename', () => {
expect(isProjectInstructionFileName(PRIMARY_PROJECT_INSTRUCTION_FILE)).toBe(
true,
)
expect(isProjectInstructionFileName(FALLBACK_PROJECT_INSTRUCTION_FILE)).toBe(
true,
)
expect(isProjectInstructionFileName('README.md')).toBe(false)
})
test('finds repo instructions in ancestor directories', () => {
const repoDir = '/repo'
const nestedDir = join(repoDir, 'packages', 'app')
const existingPaths = new Set([join(repoDir, PRIMARY_PROJECT_INSTRUCTION_FILE)])
expect(
findProjectInstructionFilePathInAncestors(
nestedDir,
path => existingPaths.has(path),
),
).toBe(join(repoDir, PRIMARY_PROJECT_INSTRUCTION_FILE))
})
test('prefers the closest ancestor project instruction file', () => {
const repoDir = '/repo'
const nestedProjectDir = join(repoDir, 'packages', 'app')
const existingPaths = new Set([
join(repoDir, PRIMARY_PROJECT_INSTRUCTION_FILE),
join(nestedProjectDir, FALLBACK_PROJECT_INSTRUCTION_FILE),
])
expect(
findProjectInstructionFilePathInAncestors(
join(nestedProjectDir, 'src'),
path => existingPaths.has(path),
),
).toBe(join(nestedProjectDir, FALLBACK_PROJECT_INSTRUCTION_FILE))
})
test('returns null when no ancestor repo instruction file exists', () => {
expect(
findProjectInstructionFilePathInAncestors('/repo/packages/app', () => false),
).toBeNull()
})
})