From 66fd144f1ab48f2bf3010441ea77056a77362f76 Mon Sep 17 00:00:00 2001 From: Roger Qiu Date: Sun, 22 Feb 2026 07:11:51 +0000 Subject: [PATCH 1/2] feat: added AGENTS.md to default markdown --- AGENTS.md | 44 ++++++++++----- README.md | 2 + src/domains/markdown.ts | 11 ++-- tests/bin/lint.test.ts | 18 ++++++ tests/domains/index.test.ts | 110 ++++++++++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 444a864..1b0b865 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,20 +1,38 @@ # AGENTS -- Read the repo profile selector at [`.matrixai/repo-profile.yml`](.matrixai/repo-profile.yml). -- Enforce the universal hotset [`.matrixai/matrixai-standards/standards/HOTSET.md`](.matrixai/matrixai-standards/standards/HOTSET.md). -- Enforce the profile doc under [`.matrixai/matrixai-standards/standards/profiles/`](.matrixai/matrixai-standards/standards/profiles) matching `profile:` in `.matrixai/repo-profile.yml` (e.g., `library-js`, `application-js`, `worker-js-cloudflare`, `docusaurus-js-cloudflare`). -- Profile index (for discovery): [`.matrixai/matrixai-standards/standards/profiles/README.md`](.matrixai/matrixai-standards/standards/profiles/README.md) -- Tooling contract reference: [`.matrixai/matrixai-standards/standards/coding/tooling/tooling-contract.md`](.matrixai/matrixai-standards/standards/coding/tooling/tooling-contract.md) (commands, artifacts, expectations per profile). -- Prefer ASCII punctuation/symbols when an equivalent exists (see [`.matrixai/matrixai-standards/standards/HOTSET.md`](.matrixai/matrixai-standards/standards/HOTSET.md) [MXS-GEN-006]). -- Ensure edits comply with [`.editorconfig`](.editorconfig) (LF line endings, 2-space indent, final newline, trim trailing whitespace). -- Line-reference policy (applies to all agent-generated repository content: Markdown, docs, templates, and code comments): - - Never emit `path:line` (e.g. `foo.ts:1`, `README.md:126`) into repository files. - - Do NOT put `:number` inside Markdown link destinations: `[x](path:123)` is banned. + +- Read the repo profile selector at + [`.matrixai/repo-profile.yml`](.matrixai/repo-profile.yml). +- Enforce the universal hotset + [`.matrixai/matrixai-standards/standards/HOTSET.md`](.matrixai/matrixai-standards/standards/HOTSET.md). +- Enforce the profile doc under + [`.matrixai/matrixai-standards/standards/profiles/`](.matrixai/matrixai-standards/standards/profiles) + matching `profile:` in `.matrixai/repo-profile.yml` (e.g., `library-js`, + `application-js`, `worker-js-cloudflare`, `docusaurus-js-cloudflare`). +- Profile index (for discovery): + [`.matrixai/matrixai-standards/standards/profiles/README.md`](.matrixai/matrixai-standards/standards/profiles/README.md) +- Tooling contract reference: + [`.matrixai/matrixai-standards/standards/coding/tooling/tooling-contract.md`](.matrixai/matrixai-standards/standards/coding/tooling/tooling-contract.md) + (commands, artifacts, expectations per profile). +- Prefer ASCII punctuation/symbols when an equivalent exists (see + [`.matrixai/matrixai-standards/standards/HOTSET.md`](.matrixai/matrixai-standards/standards/HOTSET.md) + [MXS-GEN-006]). +- Ensure edits comply with [`.editorconfig`](.editorconfig) (LF line endings, + 2-space indent, final newline, trim trailing whitespace). +- Line-reference policy (applies to all agent-generated repository content: + Markdown, docs, templates, and code comments): + - Never emit `path:line` (e.g. `foo.ts:1`, `README.md:126`) into repository + files. + - Do NOT put `:number` inside Markdown link destinations: `[x](path:123)` is + banned. - If a line reference is needed, use either: - `[x](path#heading-anchor)` (if possible), or - `[x](path) (line 123)` (preferred, portable), or - - `[x](path#L123)` only when explicitly targeting a renderer that supports `#L` anchors. - - If you would have emitted `:1`, drop it entirely: use `path` with no line info. -- Apply repo-local golden commands and overrides here (use `npm run lintfix` during active development; use `npm run lint` for non-mutating CI checks): + - `[x](path#L123)` only when explicitly targeting a renderer that supports + `#L` anchors. + - If you would have emitted `:1`, drop it entirely: use `path` with no line + info. +- Apply repo-local golden commands and overrides here (use `npm run lintfix` + during active development; use `npm run lint` for non-mutating CI checks): - build: npm run build - test: npm test - lintfix: npm run lintfix diff --git a/README.md b/README.md index a80ab38..d44779d 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ Domain selection behavior: - Directories are used as roots. - File paths and glob patterns are reduced to search roots, then `*.md` / `*.mdx` files are discovered under those roots. + - Root-level `README.md` and `AGENTS.md` are always auto-included when + present. #### Targeted workflows diff --git a/src/domains/markdown.ts b/src/domains/markdown.ts index 731f517..93da3dc 100644 --- a/src/domains/markdown.ts +++ b/src/domains/markdown.ts @@ -12,8 +12,10 @@ import { const platform = os.platform(); const MARKDOWN_FILE_EXTENSIONS = ['.md', '.mdx'] as const; +const DEFAULT_MARKDOWN_ROOT_FILES = ['README.md', 'AGENTS.md'] as const; const DEFAULT_MARKDOWN_SEARCH_ROOTS = [ './README.md', + './AGENTS.md', './pages', './blog', './docs', @@ -28,11 +30,10 @@ function collectMarkdownFilesFromScope( ); const matchedRelativeFiles = relativizeFiles(matchedFiles); - if ( - !matchedRelativeFiles.includes('README.md') && - fs.existsSync('README.md') - ) { - matchedRelativeFiles.unshift('README.md'); + for (const rootFile of [...DEFAULT_MARKDOWN_ROOT_FILES].reverse()) { + if (!matchedRelativeFiles.includes(rootFile) && fs.existsSync(rootFile)) { + matchedRelativeFiles.unshift(rootFile); + } } return matchedRelativeFiles; diff --git a/tests/bin/lint.test.ts b/tests/bin/lint.test.ts index 1ea9a35..54d3a31 100644 --- a/tests/bin/lint.test.ts +++ b/tests/bin/lint.test.ts @@ -105,6 +105,16 @@ describe('matrixai-lint CLI domain semantics', () => { '# guide\n', 'utf8', ); + await fs.promises.writeFile( + path.join(dataDir, 'README.md'), + '# fixture\n', + 'utf8', + ); + await fs.promises.writeFile( + path.join(dataDir, 'AGENTS.md'), + '# agents\n', + 'utf8', + ); await expect( main(['node', 'matrixai-lint', '--markdown', 'standards']), @@ -119,6 +129,14 @@ describe('matrixai-lint CLI domain semantics', () => { c.args.some((arg) => /prettier\.cjs$/.test(arg)), ); expect(prettierCalls.length).toBeGreaterThan(0); + + const normalizedPrettierArgs = prettierCalls + .flatMap((call) => call.args) + .map((arg) => arg.split(path.sep).join(path.posix.sep)); + + expect(normalizedPrettierArgs).toEqual( + expect.arrayContaining(['README.md', 'AGENTS.md', 'standards/guide.md']), + ); }); test('explicit shell request + missing shellcheck fails', async () => { diff --git a/tests/domains/index.test.ts b/tests/domains/index.test.ts index 7582163..4658a65 100644 --- a/tests/domains/index.test.ts +++ b/tests/domains/index.test.ts @@ -423,6 +423,116 @@ describe('domain engine', () => { await fs.promises.rm(tmpRoot, { recursive: true, force: true }); } }); + + test('markdown detection auto-includes root README.md and AGENTS.md', async () => { + const tmpRoot = await fs.promises.mkdtemp( + path.join(tmpDir, 'domain-markdown-default-roots-'), + ); + + const previousCwd = process.cwd(); + + try { + process.chdir(tmpRoot); + + await fs.promises.writeFile( + path.join(tmpRoot, 'README.md'), + '# readme\n', + 'utf8', + ); + await fs.promises.writeFile( + path.join(tmpRoot, 'AGENTS.md'), + '# agents\n', + 'utf8', + ); + + const registry = createBuiltInDomainRegistry({ + prettierConfigPath: path.join(tmpRoot, 'prettier.config.js'), + }); + + const decisions = await evaluateLintDomains({ + registry, + selectedDomains: new Set(['markdown']), + explicitlyRequestedDomains: new Set(['markdown']), + selectionSources: new Map([['markdown', 'domain-flag']]), + executionOrder: ['eslint', 'shell', 'markdown'], + context: { + fix: false, + logger: testLogger, + isConfigValid: true, + }, + }); + + const markdownDecision = decisions.find( + (decision) => decision.domain === 'markdown', + ); + const matchedFiles = ( + markdownDecision?.detection?.matchedFiles ?? [] + ).map((p) => p.split(path.sep).join(path.posix.sep)); + + expect(markdownDecision?.plannedAction).toBe('run'); + expect(matchedFiles).toEqual( + expect.arrayContaining(['README.md', 'AGENTS.md']), + ); + expect(matchedFiles.filter((file) => file === 'README.md')).toHaveLength( + 1, + ); + expect(matchedFiles.filter((file) => file === 'AGENTS.md')).toHaveLength( + 1, + ); + } finally { + process.chdir(previousCwd); + await fs.promises.rm(tmpRoot, { recursive: true, force: true }); + } + }); + + test('markdown detection auto-includes AGENTS.md when README.md is absent', async () => { + const tmpRoot = await fs.promises.mkdtemp( + path.join(tmpDir, 'domain-markdown-agents-only-'), + ); + + const previousCwd = process.cwd(); + + try { + process.chdir(tmpRoot); + + await fs.promises.writeFile( + path.join(tmpRoot, 'AGENTS.md'), + '# agents\n', + 'utf8', + ); + + const registry = createBuiltInDomainRegistry({ + prettierConfigPath: path.join(tmpRoot, 'prettier.config.js'), + }); + + const decisions = await evaluateLintDomains({ + registry, + selectedDomains: new Set(['markdown']), + explicitlyRequestedDomains: new Set(['markdown']), + selectionSources: new Map([['markdown', 'domain-flag']]), + executionOrder: ['eslint', 'shell', 'markdown'], + context: { + fix: false, + logger: testLogger, + isConfigValid: true, + }, + }); + + const markdownDecision = decisions.find( + (decision) => decision.domain === 'markdown', + ); + const matchedFiles = ( + markdownDecision?.detection?.matchedFiles ?? [] + ).map((p) => p.split(path.sep).join(path.posix.sep)); + + expect(markdownDecision?.plannedAction).toBe('run'); + expect(matchedFiles).toContain('AGENTS.md'); + expect(matchedFiles).not.toContain('README.md'); + } finally { + process.chdir(previousCwd); + await fs.promises.rm(tmpRoot, { recursive: true, force: true }); + } + }); }); describe('domain selection', () => { From 2d89c7c933153d6dae45c1b0e816159200ab7106 Mon Sep 17 00:00:00 2001 From: Roger Qiu Date: Sun, 22 Feb 2026 07:12:14 +0000 Subject: [PATCH 2/2] 0.4.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97077d1..487ad0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@matrixai/lint", - "version": "0.4.1", + "version": "0.4.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@matrixai/lint", - "version": "0.4.1", + "version": "0.4.2", "license": "Apache-2.0", "dependencies": { "@eslint/compat": "^1.2.5", diff --git a/package.json b/package.json index ed06a90..a06c196 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@matrixai/lint", - "version": "0.4.1", + "version": "0.4.2", "author": "Roger Qiu", "description": "Org wide custom eslint rules", "license": "Apache-2.0",