Introduce scripts/project_rules/gitignore-claude.template with the fleet pattern that tracks .claude/rules/ while ignoring machine-local Claude state. ensure_gitignore_claude_rules.py applies it during statehub register, register_project.sh, and update_agent_instruction_files fleet regen.
63 lines
No EOL
2 KiB
Python
63 lines
No EOL
2 KiB
Python
"""Ensure repos track .claude/rules/ while ignoring other Claude Code local state."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
SNIPPET_PATH = ROOT / "scripts" / "project_rules" / "gitignore-claude.template"
|
|
MARKER = "# state-hub: track .claude/rules"
|
|
TRACK_RULE = "!.claude/rules/"
|
|
BLANKET_IGNORE_RE = re.compile(r"^\.claude/?\s*$", re.MULTILINE)
|
|
|
|
|
|
def load_snippet() -> str:
|
|
text = SNIPPET_PATH.read_text(encoding="utf-8").strip()
|
|
return f"{MARKER}\n{text}\n"
|
|
|
|
|
|
def snippet_present(text: str) -> bool:
|
|
return TRACK_RULE in text
|
|
|
|
|
|
def ensure_claude_gitignore(gitignore_path: Path) -> bool:
|
|
"""Return True when the file was created or modified."""
|
|
snippet = load_snippet()
|
|
if gitignore_path.exists():
|
|
text = gitignore_path.read_text(encoding="utf-8")
|
|
if snippet_present(text):
|
|
return False
|
|
if BLANKET_IGNORE_RE.search(text):
|
|
updated = BLANKET_IGNORE_RE.sub(snippet.rstrip(), text, count=1)
|
|
if updated != text:
|
|
gitignore_path.write_text(_ensure_trailing_newline(updated), encoding="utf-8")
|
|
return True
|
|
separator = "" if text.endswith("\n") or not text else "\n"
|
|
gitignore_path.write_text(f"{text}{separator}\n{snippet}", encoding="utf-8")
|
|
return True
|
|
|
|
gitignore_path.write_text(snippet, encoding="utf-8")
|
|
return True
|
|
|
|
|
|
def _ensure_trailing_newline(text: str) -> str:
|
|
return text if text.endswith("\n") else f"{text}\n"
|
|
|
|
|
|
def main() -> int:
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="Ensure .gitignore tracks .claude/rules/.")
|
|
parser.add_argument("path", type=Path, help="Repo root or .gitignore file path")
|
|
args = parser.parse_args()
|
|
|
|
target = args.path
|
|
gitignore = target if target.name == ".gitignore" else target / ".gitignore"
|
|
changed = ensure_claude_gitignore(gitignore)
|
|
print(f"{'updated' if changed else 'ok'}: {gitignore}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main()) |