diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3bf50e06..51d7745c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,9 @@ jobs: - name: Checkout uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Keep local AI tooling out of Git + run: python3 tools/check_local_tooling.py + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable with: diff --git a/.gitignore b/.gitignore index 9932abf4..7d04a0d8 100644 --- a/.gitignore +++ b/.gitignore @@ -162,5 +162,24 @@ rustchan-e2e-*/ /.cargo/credentials /.cargo/credentials.toml -# Personal working instructions. +# Local AI tooling: keep instructions, configuration, and sessions on this machine. +# CI checks this block even if an ignored file is accidentally force-added. +AGENTS.md +CLAUDE.md +GEMINI.md +.agents/ +.codex/ +.claude/ +.cursor/ +.continue/ +.windsurf/ +.aider* +/.mcp.json +/mcp.json +/.cursorrules +/.windsurfrules +/.github/copilot-instructions.md +/.github/instructions/ +/.github/prompts/ /rules.md +# End local AI tooling. diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 60fd1a81..00000000 --- a/AGENTS.md +++ /dev/null @@ -1,4 +0,0 @@ -# Repository instructions - -- Never create or push a branch whose name contains `codex`, regardless of case. -- Use plain, descriptive branch names. diff --git a/tools/check_local_tooling.py b/tools/check_local_tooling.py new file mode 100644 index 00000000..c4c5c233 --- /dev/null +++ b/tools/check_local_tooling.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Reject tracked files covered by the local-tooling block in .gitignore.""" + +from pathlib import Path +import subprocess +import sys +import tempfile + + +def main(): + lines = Path(".gitignore").read_text().splitlines() + start = lines.index( + "# Local AI tooling: keep instructions, configuration, and sessions on this machine." + ) + end = lines.index("# End local AI tooling.", start) + patterns = "\n".join(lines[start + 1 : end]) + "\n" + with tempfile.NamedTemporaryFile(mode="w+", encoding="utf-8") as excludes: + excludes.write(patterns) + excludes.flush() + result = subprocess.run( + [ + "git", "ls-files", "--cached", "--ignored", "-z", + f"--exclude-from={excludes.name}", + ], + check=True, + capture_output=True, + ) + paths = result.stdout.decode("utf-8", errors="replace").split("\0") + tracked = [path for path in paths if path] + if tracked: + print("Local AI tooling must remain untracked:", file=sys.stderr) + for path in tracked: + print(f" {path!r}", file=sys.stderr) + return 1 + print("No local AI tooling is tracked.") + return 0 + + +if __name__ == "__main__": + sys.exit(main())