diff --git a/eslint.config.mjs b/eslint.config.mjs index 9462bf51..d24689c7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,7 +4,7 @@ import eslintConfigPrettier from 'eslint-config-prettier'; export default [ { - ignores: ['node_modules/', 'dist/', 'coverage/', '*.min.js', 'templates/'], + ignores: ['node_modules/', 'dist/', 'coverage/', '.context/', '*.min.js', 'templates/'], }, js.configs.recommended, eslintConfigPrettier, diff --git a/jest.config.js b/jest.config.js index b9bb8fb3..a57b133c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,8 @@ module.exports = { testEnvironment: 'node', testMatch: ['**/test/**/*.test.js', '**/test/**/*.spec.js'], + testPathIgnorePatterns: ['/.context/worktrees/'], + modulePathIgnorePatterns: ['/.context/worktrees/'], collectCoverageFrom: [ '**/*.{js,mjs,cjs}', '!script/**/*', diff --git a/script/update-agents-md.sh b/script/update-agents-md.sh index 1fed4c5b..7f05b4ba 100755 --- a/script/update-agents-md.sh +++ b/script/update-agents-md.sh @@ -46,6 +46,17 @@ workflow_files() { find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) | sort } +dir_has_tracked_files() { + local dir="$1" + + if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + [[ -n "$(git ls-files -- "$dir/" | head -n 1)" ]] + return + fi + + [[ -n "$(find "$dir" -type f -print -quit)" ]] +} + # shellcheck disable=SC2034 # NODE_VER / PM consumed by template via eval NODE_VER=$(jq -r '.engines.node // empty' package.json 2>/dev/null) # shellcheck disable=SC2034 @@ -72,10 +83,11 @@ collect_dirs() { [[ "$d" == "./" || "$d" == "../" ]] && continue name="${d%/}" [[ "$name" == .git || "$name" == .git-* ]] && continue + dir_has_tracked_files "$name" || continue if [[ "$name" == ".claude" ]]; then for sub in "${CLAUDE_SUB_ORDER[@]}"; do - [[ -d ".claude/$sub" ]] && emit out "\`.claude/$sub/\`" "${CLAUDE_SUB_PURPOSE[$sub]}" + [[ -d ".claude/$sub" ]] && dir_has_tracked_files ".claude/$sub" && emit out "\`.claude/$sub/\`" "${CLAUDE_SUB_PURPOSE[$sub]}" done continue fi @@ -90,6 +102,7 @@ collect_dirs() { for d in */; do name="${d%/}" [[ "$REG_DIR_SKIP" == *" $name "* ]] && continue + dir_has_tracked_files "$name" || continue purpose="${REG_DIR_PURPOSE[$name]:-$name}" emit out "\`$name/\`" "$purpose" done diff --git a/test/claude-workflow-contract.test.js b/test/claude-workflow-contract.test.js index 5dd7ac59..b2c7c702 100644 --- a/test/claude-workflow-contract.test.js +++ b/test/claude-workflow-contract.test.js @@ -34,6 +34,39 @@ function runRequiredWorkflowScript(workflows) { } } +function runUpdateAgentsScriptWithUntrackedDirectory() { + const contextDir = path.join(repoPath, '.context'); + fs.mkdirSync(contextDir, { recursive: true }); + const tempRoot = fs.mkdtempSync(path.join(contextDir, 'agents-md-test-')); + + try { + fs.writeFileSync( + path.join(tempRoot, 'AGENTS.md'), + ['# Test Agents', '', '', '', ''].join('\n'), + ); + fs.writeFileSync( + path.join(tempRoot, 'package.json'), + JSON.stringify({ scripts: {}, engines: { node: '24.14.1' } }, null, 2), + ); + fs.mkdirSync(path.join(tempRoot, 'docs'), { recursive: true }); + fs.writeFileSync(path.join(tempRoot, 'docs', 'README.md'), '# Docs\n'); + fs.mkdirSync(path.join(tempRoot, 'next'), { recursive: true }); + + execFileSync('git', ['init'], { cwd: tempRoot, stdio: 'ignore' }); + execFileSync('git', ['add', 'AGENTS.md', 'package.json', 'docs/README.md'], { + cwd: tempRoot, + stdio: 'ignore', + }); + + const scriptPath = path.join(repoPath, 'script', 'update-agents-md.sh'); + execFileSync('bash', [scriptPath], { cwd: tempRoot, encoding: 'utf8' }); + + return fs.readFileSync(path.join(tempRoot, 'AGENTS.md'), 'utf8'); + } finally { + fs.rmSync(tempRoot, { recursive: true, force: true }); + } +} + describe('Claude workflow contracts', () => { const issueWorkflows = ['.github/workflows/claude.yml', 'templates/workflows/claude.yml']; const maintenanceWorkflows = [ @@ -230,6 +263,13 @@ describe('Claude workflow contracts', () => { expect(script).not.toContain('mktemp -d -t agents-md-check'); }); + test('update-agents-md ignores untracked project directories', () => { + const generated = runUpdateAgentsScriptWithUntrackedDirectory(); + + expect(generated).toContain('`docs/`'); + expect(generated).not.toContain('`next/`'); + }); + test('repo-maintenance scans yml and yaml workflows in cross-workflow checks', () => { const script = readWorkflow('script/repo-maintenance.sh'); diff --git a/test/eslint-config.test.js b/test/eslint-config.test.js index 28dcccd4..88bbb8e3 100644 --- a/test/eslint-config.test.js +++ b/test/eslint-config.test.js @@ -13,7 +13,7 @@ function makeTempDir(prefix) { } function runEslint(filePath, extraArgs = []) { - return execFileSync(eslintBin, ['--config', configPath, ...extraArgs, filePath], { + return execFileSync(eslintBin, ['--config', configPath, '--no-ignore', ...extraArgs, filePath], { cwd: repoPath, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], @@ -33,6 +33,12 @@ function expectEslintFailure(filePath, extraArgs = []) { } describe('ESLint configuration runtime behavior', () => { + test('ignores shared context artifacts during full repository lint', () => { + const config = fs.readFileSync(configPath, 'utf8'); + + expect(config).toContain("'.context/'"); + }); + test('allows console usage and ignored underscore arguments', () => { const tempDir = makeTempDir('eslint-valid'); try { diff --git a/test/integration/audit_references.bats b/test/integration/audit_references.bats index 4937338c..83542927 100644 --- a/test/integration/audit_references.bats +++ b/test/integration/audit_references.bats @@ -18,7 +18,7 @@ assert_tsv_row() { run "$REPO_ROOT/script/audit-references.sh" --format tsv assert_success - assert_tsv_row docs script/audit-references.sh .context/refactoring-baseline.md + assert_tsv_row docs script/README.md docs/README.md assert_tsv_row test script/audit-references.sh test/integration/core-scripts.bats assert_tsv_row code/ci templates/workflows/claude.yml templates/workflows/claude-health-check.yml assert_tsv_row test templates/workflows/claude-health-check.yml test/template-workflows.test.js diff --git a/test/jest-config.test.js b/test/jest-config.test.js index df4a8e88..24852d0c 100644 --- a/test/jest-config.test.js +++ b/test/jest-config.test.js @@ -35,6 +35,11 @@ function expectJestFailure(args) { } describe('Jest configuration runtime behavior', () => { + test('ignores persistent worktrees under .context', () => { + expect(baseJestConfig.testPathIgnorePatterns).toContain('/.context/worktrees/'); + expect(baseJestConfig.modulePathIgnorePatterns).toContain('/.context/worktrees/'); + }); + test('runs a matching test file in the Node environment', () => { const tempDir = makeTempDir('jest-runtime'); try {