Skip to content

Commit e0de685

Browse files
committed
update claude settings
AI Prompts: claude-sonnet-4-6: <ide_opened_file>The user opened the file ./CHANGES.md in the IDE. This may or may not be related to the current task.</ide_opened_file> add a version variable to the .claude/settings.json when updated that reflects the package version claude-sonnet-4-6: <ide_selection>The user selected the lines 5 to 12 from ./ai_prompt_auto_commit/data/claude_settings.json: "hooks": [ { "id": "ai-prompt-auto-commit", "type": "command", "command": "input=$(cat); ts=$(date +%Y-%m-%dT%H-%M-%S); model=$(printf '%s' \"$input\" | jq -r '.model // \"claude-sonnet-4-6\"'); root=$(git rev-parse --show-toplevel 2>/dev/null || pwd); mkdir -p \"$root/.prompts\"; printf '%s' \"$input\" | jq -r '.prompt' > \"$root/.prompts/${ts}_${model}.md\"" } ] This may or may not be related to the current task.</ide_selection> should look like "hooks": [ { "id": "ai-prompt-auto-commit", "version": ..., "type": "command", "command": "input=$(cat); ts=$(date +%Y-%m-%dT%H-%M-%S); model=$(printf '%s' \"$input\" | jq -r '.model // \"claude-sonnet-4-6\"'); root=$(git rev-parse --show-toplevel 2>/dev/null || pwd); mkdir -p \"$root/.prompts\"; printf '%s' \"$input\" | jq -r '.prompt' > \"$root/.prompts/${ts}_${model}.md\"" } ] claude-sonnet-4-6: <ide_selection>The user selected the lines 15 to 15 from ./ai_prompt_auto_commit/prepare_repository.py: get_default_claude_settings This may or may not be related to the current task.</ide_selection> fix, use and test get_default_claude_settings claude-sonnet-4-6: <ide_selection>The user selected the lines 64 to 64 from ./ai_prompt_auto_commit/prepare_repository.py: h["version"] = package_version This may or may not be related to the current task.</ide_selection> now test that prepare_repository updates the hook code when it is run again h["version"] = package_version is not enough . the whole hooks content needs updating
1 parent b4fafee commit e0de685

3 files changed

Lines changed: 99 additions & 29 deletions

File tree

CHANGES.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
## v0.0.6
66

7-
- Create .prompts if absent
8-
- `prepare-ai-repository` hook now adds .prompts to the `/.gitignore`
7+
- Create .prompts if absent in `.clauded/settings.json`
8+
- `prepare-ai-repository` hook
9+
- adds .prompts to the `/.gitignore`
10+
- updates the hook `.claude/settings.json`
11+
- adds a version to the hook
912

1013
## v0.0.5
1114

ai_prompt_auto_commit/prepare_repository.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import importlib.metadata
56
import importlib.resources
67
import json
78
import sys
@@ -11,17 +12,26 @@
1112
from .common import PROMPTS_DIRECTORY
1213

1314

14-
def prepare_repository() -> int:
15+
def get_default_claude_settings() -> dict:
16+
"""Return the bundled claude_settings.json with the package version injected into the hook."""
17+
ref = importlib.resources.files("ai_prompt_auto_commit.data").joinpath("claude_settings.json")
18+
settings = json.loads(ref.read_text(encoding="utf-8"))
19+
package_version = importlib.metadata.version("ai-prompt-auto-commit")
20+
settings["hooks"]["UserPromptSubmit"][0]["hooks"][0]["version"] = package_version
21+
return settings
22+
23+
def prepare_repository(
24+
prompts_directory:str = PROMPTS_DIRECTORY,) -> int:
1525
"""Set up .prompts/, and .claude/settings.json in the target repo."""
1626
repo_root = common._repo_root()
1727

1828
# Create PROMPTS_DIRECTORY
19-
prompts_dir = repo_root / PROMPTS_DIRECTORY
29+
prompts_dir = repo_root / prompts_directory
2030
prompts_dir.mkdir(parents=True, exist_ok=True)
2131

2232
# Add .prompts/ to the root .gitignore
2333
root_gitignore = repo_root / ".gitignore"
24-
pattern = f"/{PROMPTS_DIRECTORY}/"
34+
pattern = f"/{prompts_directory}/"
2535
existing = root_gitignore.read_text(encoding="utf-8").splitlines() if root_gitignore.exists() else []
2636
if pattern not in existing:
2737
with root_gitignore.open("a", encoding="utf-8") as fh:
@@ -31,10 +41,10 @@ def prepare_repository() -> int:
3141
print(f"{root_gitignore} already contains '{pattern}'")
3242

3343
# Install the UserPromptSubmit hook into .claude/settings.json
34-
ref = importlib.resources.files("ai_prompt_auto_commit.data").joinpath("claude_settings.json")
35-
bundled = json.loads(ref.read_text(encoding="utf-8"))
44+
bundled = get_default_claude_settings()
3645
hook_def = bundled["hooks"]["UserPromptSubmit"][0]["hooks"][0]
3746
hook_id = hook_def["id"]
47+
package_version = hook_def["version"]
3848

3949
claude_dest = repo_root / ".claude"
4050
dest_file = claude_dest / "settings.json"
@@ -47,18 +57,23 @@ def prepare_repository() -> int:
4757
for matcher in matchers
4858
for h in matcher.get("hooks", [])
4959
)
50-
5160
if already_installed:
52-
print(f"Hook '{hook_id}' already present in {dest_file}")
61+
for matcher in matchers:
62+
hooks_list = matcher.get("hooks", [])
63+
for i, h in enumerate(hooks_list):
64+
if h.get("id") == hook_id:
65+
hooks_list[i] = hook_def
66+
print(f"Updated hook '{hook_id}' to version {package_version} in {dest_file}")
5367
else:
5468
if matchers:
5569
matchers[0].setdefault("hooks", []).append(hook_def)
5670
else:
5771
matchers.append({"hooks": [hook_def]})
58-
claude_dest.mkdir(parents=True, exist_ok=True)
59-
dest_file.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
6072
print(f"Inserted hook '{hook_id}' into {dest_file}")
6173

74+
claude_dest.mkdir(parents=True, exist_ok=True)
75+
dest_file.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
76+
6277
# Ensure prepare-commit-msg and post-commit hooks are installed.
6378
# Derive them from the existing pre-commit hook file by swapping --hook-type.
6479
hooks_dir = repo_root / ".git" / "hooks"

tests/test_prepare_repository.py

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
from __future__ import annotations
44

5+
import importlib.metadata
56
import json
67
from pathlib import Path
78

89
from ai_prompt_auto_commit.common import PROMPTS_DIRECTORY
9-
from ai_prompt_auto_commit.prepare_repository import prepare_repository
10+
from ai_prompt_auto_commit.prepare_repository import get_default_claude_settings, prepare_repository
1011

1112

1213
# ---------------------------------------------------------------------------
@@ -17,32 +18,47 @@ def test_prompts_dir_created(repo: Path) -> None:
1718
assert (repo / PROMPTS_DIRECTORY).is_dir()
1819

1920

20-
def test_prompts_gitignore_contains_wildcard(repo: Path) -> None:
21-
gitignore = repo / PROMPTS_DIRECTORY / ".gitignore"
22-
assert gitignore.exists()
23-
assert "*" in gitignore.read_text(encoding="utf-8").splitlines()
24-
25-
26-
def test_prompts_gitignore_not_duplicated(repo: Path) -> None:
27-
prepare_repository() # second run — fixture already ran once
28-
gitignore = repo / PROMPTS_DIRECTORY / ".gitignore"
29-
assert gitignore.read_text(encoding="utf-8").splitlines().count("*") == 1
30-
31-
3221
# ---------------------------------------------------------------------------
3322
# top-level .gitignore
3423
# ---------------------------------------------------------------------------
3524

36-
def test_gitignore_is_not_touched(repo: Path) -> None:
37-
assert not (repo / ".gitignore").exists()
25+
def test_root_gitignore_contains_prompts_pattern(repo: Path) -> None:
26+
gitignore = repo / ".gitignore"
27+
assert gitignore.exists()
28+
assert f"/{PROMPTS_DIRECTORY}/" in gitignore.read_text(encoding="utf-8").splitlines()
29+
3830

39-
def test_gitignore_existing_content_preserved(repo: Path) -> None:
40-
# Overwrite with pre-existing content and re-run
31+
def test_root_gitignore_pattern_not_duplicated(repo: Path) -> None:
32+
prepare_repository() # second run
33+
gitignore = repo / ".gitignore"
34+
lines = gitignore.read_text(encoding="utf-8").splitlines()
35+
assert lines.count(f"/{PROMPTS_DIRECTORY}/") == 1
36+
37+
38+
def test_root_gitignore_existing_content_preserved(repo: Path) -> None:
4139
old_content = "*.pyc\n__pycache__\n"
4240
(repo / ".gitignore").write_text(old_content, encoding="utf-8")
4341
prepare_repository()
4442
content = (repo / ".gitignore").read_text(encoding="utf-8")
45-
assert content == old_content
43+
assert content.startswith(old_content)
44+
assert f"/{PROMPTS_DIRECTORY}/" in content
45+
46+
47+
# ---------------------------------------------------------------------------
48+
# get_default_claude_settings
49+
# ---------------------------------------------------------------------------
50+
51+
def test_get_default_claude_settings_returns_hook() -> None:
52+
settings = get_default_claude_settings()
53+
hooks = settings["hooks"]["UserPromptSubmit"][0]["hooks"]
54+
assert any(h.get("id") == "ai-prompt-auto-commit" for h in hooks)
55+
56+
57+
def test_get_default_claude_settings_version_matches_package() -> None:
58+
settings = get_default_claude_settings()
59+
hook = settings["hooks"]["UserPromptSubmit"][0]["hooks"][0]
60+
expected = importlib.metadata.version("ai-prompt-auto-commit")
61+
assert hook["version"] == expected
4662

4763
# ---------------------------------------------------------------------------
4864
# .claude/settings.json
@@ -96,5 +112,41 @@ def test_claude_settings_hook_not_duplicated(repo: Path) -> None:
96112
assert _hook_ids(settings).count("ai-prompt-auto-commit") == 1
97113

98114

115+
def test_claude_settings_hook_has_version(repo: Path) -> None:
116+
dest = repo / ".claude" / "settings.json"
117+
settings = json.loads(dest.read_text(encoding="utf-8"))
118+
hook = next(
119+
h for matcher in settings["hooks"]["UserPromptSubmit"]
120+
for h in matcher.get("hooks", [])
121+
if h.get("id") == "ai-prompt-auto-commit"
122+
)
123+
expected = importlib.metadata.version("ai-prompt-auto-commit")
124+
assert hook["version"] == expected
125+
126+
127+
def test_claude_settings_hook_updated_on_rerun(repo: Path) -> None:
128+
dest = repo / ".claude" / "settings.json"
129+
# Corrupt the existing hook with stale content
130+
settings = json.loads(dest.read_text(encoding="utf-8"))
131+
for matcher in settings["hooks"]["UserPromptSubmit"]:
132+
for h in matcher.get("hooks", []):
133+
if h.get("id") == "ai-prompt-auto-commit":
134+
h["version"] = "0.0.0"
135+
h["command"] = "stale command"
136+
h["extra_stale_key"] = "should be removed"
137+
dest.write_text(json.dumps(settings, indent=2), encoding="utf-8")
138+
prepare_repository()
139+
settings = json.loads(dest.read_text(encoding="utf-8"))
140+
hook = next(
141+
h for matcher in settings["hooks"]["UserPromptSubmit"]
142+
for h in matcher.get("hooks", [])
143+
if h.get("id") == "ai-prompt-auto-commit"
144+
)
145+
expected = get_default_claude_settings()["hooks"]["UserPromptSubmit"][0]["hooks"][0]
146+
assert hook == expected
147+
assert "extra_stale_key" not in hook
148+
149+
99150
def test_prepare_repository_returns_zero(repo: Path) -> None:
100151
assert prepare_repository() == 0
152+
assert repo.is_dir()

0 commit comments

Comments
 (0)