claude-dev-env 8.12.0 → 8.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/.agents/skills/pull-request/scripts/test_pull_request.py +1 -1
  2. package/.agents/skills-archived/pr-converge/scripts/test_check_convergence.py +4 -4
  3. package/_shared/advisor/reference/astra-rung.md +7 -7
  4. package/_shared/advisor/reference/third-party-bind.md +2 -2
  5. package/_shared/advisor/scripts/codex_astra_advisor.py +33 -26
  6. package/_shared/advisor/scripts/codex_astra_preflight.py +57 -55
  7. package/_shared/advisor/scripts/config/advisor_scripts_constants/astra_advisor_constants.py +8 -8
  8. package/_shared/advisor/scripts/tests/test_codex_astra_advisor.py +69 -23
  9. package/_shared/advisor/scripts/tests/test_codex_astra_preflight.py +4 -4
  10. package/_shared/pr-loop/scripts/check_convergence.py +54 -49
  11. package/_shared/pr-loop/scripts/codex_review_scripts_constants/run_constants.py +4 -0
  12. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +10 -1
  13. package/_shared/pr-loop/scripts/run_codex_review.py +4 -6
  14. package/_shared/pr-loop/scripts/test_check_convergence.py +6 -6
  15. package/_shared/pr-loop/scripts/test_check_convergence_codex.py +73 -31
  16. package/_shared/pr-loop/scripts/test_check_convergence_contract.py +2 -2
  17. package/_shared/pr-loop/scripts/tests/test__claude_permissions_common.py +2 -2
  18. package/bin/git_hooks_installer.test.mjs +1 -1
  19. package/bin/install-constants.mjs +1 -6
  20. package/bin/install.cursor-rules.test.mjs +24 -51
  21. package/bin/install.mjs +16 -70
  22. package/docs/codex-accounts.md +79 -0
  23. package/hooks/blocking/test_duplicate_rmtree_helper_blocker.py +5 -5
  24. package/hooks/blocking/test_pii_scanner.py +1 -1
  25. package/hooks/blocking/test_pytest_testpaths_orphan_blocker.py +2 -2
  26. package/hooks/git-hooks/conftest.py +16 -0
  27. package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +1 -1
  28. package/hooks/git-hooks/test_post_commit.py +1 -1
  29. package/hooks/git-hooks/test_pre_commit.py +1 -1
  30. package/hooks/git-hooks/test_pre_push.py +2 -2
  31. package/hooks/git-hooks/test_verification_start.py +1 -1
  32. package/hooks/git-hooks/verification_notice.py +2 -2
  33. package/hooks/git-hooks/verification_notice_context.py +21 -2
  34. package/hooks/git-hooks/verification_start.py +2 -3
  35. package/package.json +1 -1
  36. package/rules/durable-post-artifacts.md +4 -3
  37. package/scripts/claude_account_profile.py +21 -9
  38. package/scripts/codex_account_choice.py +396 -0
  39. package/scripts/codex_account_meters.py +372 -0
  40. package/scripts/dev_env_scripts_constants/codex_account_constants.py +141 -0
  41. package/scripts/dev_env_scripts_constants/private_term_constants.py +17 -2
  42. package/scripts/private_term_scan.py +6 -2
  43. package/scripts/repository_checks/config/constants.py +1 -0
  44. package/scripts/repository_checks/tracked_private_terms.py +7 -0
  45. package/scripts/test_claude_account_profile.py +12 -0
  46. package/scripts/test_codex_account_choice.py +276 -0
  47. package/scripts/test_codex_account_meters.py +205 -0
  48. package/scripts/tests/test_automatic_advisory.py +3 -3
  49. package/scripts/tests/test_automatic_advisory_polling.py +1 -1
  50. package/scripts/tests/test_automatic_advisory_recovery.py +1 -1
  51. package/scripts/tests/test_closed_pr_label.py +1 -1
  52. package/scripts/tests/test_private_term_scan.py +28 -0
  53. package/scripts/tests/test_review_closure.py +2 -2
  54. package/scripts/tests/test_review_closure_model.py +2 -2
  55. package/scripts/tests/test_rules.py +2 -83
  56. package/scripts/tests/test_tracked_private_terms.py +15 -0
  57. package/scripts/tests/test_verification_notice.py +29 -13
  58. package/.agents/skills/cert-classification-rule/SKILL.md +0 -224
  59. package/.agents/skills/cert-classification-rule/scripts/resort_cert_folders.py +0 -158
  60. package/_shared/pr-loop/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +0 -82
  61. package/_shared/pr-loop/scripts/codex_usage_probe.py +0 -573
  62. package/_shared/pr-loop/scripts/test_codex_usage_probe.py +0 -683
  63. package/scripts/sync_to_cursor/AGENTS.md +0 -1
  64. package/scripts/sync_to_cursor/CLAUDE.md +0 -1
  65. package/scripts/sync_to_cursor/__init__.py +0 -13
  66. package/scripts/sync_to_cursor/canonical_docs.py +0 -66
  67. package/scripts/sync_to_cursor/config/__init__.py +0 -8
  68. package/scripts/sync_to_cursor/engine.py +0 -219
  69. package/scripts/sync_to_cursor/hashing.py +0 -7
  70. package/scripts/sync_to_cursor/paths.py +0 -18
  71. package/scripts/sync_to_cursor/rules.py +0 -494
  72. package/scripts/sync_to_cursor.py +0 -22
  73. package/scripts/tests/test_engine.py +0 -101
  74. package/scripts/tests/test_sync_to_cursor.py +0 -315
@@ -1,315 +0,0 @@
1
- """Tests for sync_to_cursor.py: canonical docs copy, manifest, and truncation footer."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import sys
7
- from pathlib import Path
8
-
9
- import pytest
10
-
11
- _SCRIPTS_DIR = Path(__file__).resolve().parent.parent
12
- if str(_SCRIPTS_DIR) not in sys.path:
13
- sys.path.insert(0, str(_SCRIPTS_DIR))
14
-
15
- import sync_to_cursor as mod
16
- from sync_to_cursor.engine import run as run_sync_to_cursor
17
- from sync_to_cursor.rules import (
18
- _code_standards_section_order,
19
- _parse_h2_sections,
20
- _read_paths_glob,
21
- _test_quality_section_order,
22
- build_mappings,
23
- strip_leading_yaml_frontmatter,
24
- )
25
-
26
- _CLAUDE_DEV_ENV_DIR = _SCRIPTS_DIR.parent
27
- _REAL_TESTING_RULE = _CLAUDE_DEV_ENV_DIR / "rules" / "testing.md"
28
- _REAL_CODE_RULES_DOC = _CLAUDE_DEV_ENV_DIR / "docs" / "CODE_RULES.md"
29
- _REAL_TEST_QUALITY_DOC = _CLAUDE_DEV_ENV_DIR / "docs" / "TEST_QUALITY.md"
30
-
31
-
32
- def _minimal_rule_files(claude_rules: Path) -> None:
33
- claude_rules.mkdir(parents=True, exist_ok=True)
34
- (claude_rules / "code-standards.md").write_text(
35
- "# Code standards stub\n", encoding="utf-8"
36
- )
37
- (claude_rules / "tasklings-preferences.md").write_text(
38
- "---\npaths:\n - \"Y:/x/**\"\n---\n\n# Tasklings\n",
39
- encoding="utf-8",
40
- )
41
- (claude_rules / "bdd.md").write_text("# BDD\n", encoding="utf-8")
42
- (claude_rules / "testing.md").write_text(
43
- "---\npaths:\n - \"**/test_*.py\"\n---\n\n# Testing\n",
44
- encoding="utf-8",
45
- )
46
- (claude_rules / "research-mode.md").write_text("# RM\n", encoding="utf-8")
47
- (claude_rules / "explore-thoroughly.md").write_text("# ET\n", encoding="utf-8")
48
-
49
-
50
- def _minimal_code_rules_and_test_quality(claude_docs: Path) -> tuple[bytes, bytes]:
51
- claude_docs.mkdir(parents=True, exist_ok=True)
52
- cr = (
53
- "\n\n".join(f"## {title}\n\nalpha" for title in _code_standards_section_order) + "\n"
54
- ).encode("utf-8")
55
- tq = (
56
- "\n\n".join(f"## {title}\n\nbeta" for title in _test_quality_section_order) + "\n"
57
- ).encode("utf-8")
58
- (claude_docs / "CODE_RULES.md").write_bytes(cr)
59
- (claude_docs / "TEST_QUALITY.md").write_bytes(tq)
60
- return cr, tq
61
-
62
-
63
- def test_limit_lines_footer_mentions_cursor_docs() -> None:
64
- long_body = "\n".join(f"line {index}" for index in range(mod.MAX_RULE_BODY_LINES + 5))
65
- out = mod._limit_lines(long_body, mod.MAX_RULE_BODY_LINES)
66
- assert ".cursor/docs" in out
67
- assert "synced reference" in out
68
- assert "~/.claude/system-prompts/software-engineer.xml" in out
69
-
70
-
71
- def test_sync_canonical_docs_copies_byte_identical(tmp_path: Path) -> None:
72
- claude = tmp_path / ".claude"
73
- cursor = tmp_path / ".cursor"
74
- docs = claude / "docs"
75
- cr, tq = _minimal_code_rules_and_test_quality(docs)
76
- mod._sync_canonical_docs(claude, cursor, dry_run=False, quiet=True)
77
- assert (cursor / "docs" / "CODE_RULES.md").read_bytes() == cr
78
- assert (cursor / "docs" / "TEST_QUALITY.md").read_bytes() == tq
79
-
80
-
81
- def test_sync_canonical_docs_skips_missing_with_no_dst(tmp_path: Path) -> None:
82
- claude = tmp_path / ".claude"
83
- cursor = tmp_path / ".cursor"
84
- (claude / "docs").mkdir(parents=True)
85
- (claude / "docs" / "CODE_RULES.md").write_text("x", encoding="utf-8")
86
- mod._sync_canonical_docs(claude, cursor, dry_run=False, quiet=True)
87
- assert (cursor / "docs" / "CODE_RULES.md").read_text(encoding="utf-8") == "x"
88
- assert not (cursor / "docs" / "TEST_QUALITY.md").is_file()
89
-
90
-
91
- def test_check_fails_when_doc_source_changes_without_resync(
92
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
93
- ) -> None:
94
- claude = tmp_path / ".claude"
95
- _minimal_rule_files(claude / "rules")
96
- _minimal_code_rules_and_test_quality(claude / "docs")
97
- monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
98
- assert run_sync_to_cursor(["--force"]) == 0
99
- (claude / "docs" / "CODE_RULES.md").write_bytes(b"changed\n")
100
- assert run_sync_to_cursor(["--check"]) != 0
101
-
102
-
103
- def test_check_passes_after_resync(
104
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
105
- ) -> None:
106
- claude = tmp_path / ".claude"
107
- _minimal_rule_files(claude / "rules")
108
- _minimal_code_rules_and_test_quality(claude / "docs")
109
- monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
110
- assert run_sync_to_cursor(["--force"]) == 0
111
- assert run_sync_to_cursor(["--check"]) == 0
112
-
113
-
114
- def test_manifest_includes_docs_entries(
115
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
116
- ) -> None:
117
- claude = tmp_path / ".claude"
118
- cursor = tmp_path / ".cursor"
119
- _minimal_rule_files(claude / "rules")
120
- _minimal_code_rules_and_test_quality(claude / "docs")
121
- monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
122
- assert run_sync_to_cursor(["--force"]) == 0
123
- manifest = json.loads((cursor / ".sync-manifest.json").read_text(encoding="utf-8"))
124
- assert "docs_entries" in manifest
125
- docs_entries = manifest["docs_entries"]
126
- assert "docs/CODE_RULES.md" in docs_entries
127
- assert "docs/TEST_QUALITY.md" in docs_entries
128
- assert "sources_hash" in docs_entries["docs/CODE_RULES.md"]
129
- assert "output_hash" in docs_entries["docs/CODE_RULES.md"]
130
-
131
-
132
- def test_merge_code_standards_with_pointer_style_code_rules(tmp_path: Path) -> None:
133
- rules_directory = tmp_path / "rules"
134
- docs_directory = tmp_path / "docs"
135
- rules_directory.mkdir(parents=True, exist_ok=True)
136
- docs_directory.mkdir(parents=True, exist_ok=True)
137
- (rules_directory / "code-standards.md").write_text(
138
- "# Code standards stub\n\n- Use full words\n", encoding="utf-8"
139
- )
140
- (docs_directory / "CODE_RULES.md").write_text(
141
- "# CODE_RULES pointer: canonical code-quality policy lives in"
142
- " `~/.claude/system-prompts/software-engineer.xml` under `<code_quality>`.\n",
143
- encoding="utf-8",
144
- )
145
- merged = mod.merge_code_standards(
146
- (rules_directory / "code-standards.md", docs_directory / "CODE_RULES.md")
147
- )
148
- assert merged.strip(), "output must not be empty for pointer-style CODE_RULES.md"
149
- assert "software-engineer.xml" in merged
150
- assert "code_quality" in merged
151
-
152
-
153
- def test_sync_canonical_docs_deletes_stale_copy_when_source_removed(tmp_path: Path) -> None:
154
- claude = tmp_path / ".claude"
155
- cursor = tmp_path / ".cursor"
156
- _minimal_code_rules_and_test_quality(claude / "docs")
157
- mod._sync_canonical_docs(claude, cursor, dry_run=False, quiet=True)
158
- assert (cursor / "docs" / "CODE_RULES.md").is_file()
159
- (claude / "docs" / "CODE_RULES.md").unlink()
160
- mod._sync_canonical_docs(claude, cursor, dry_run=False, quiet=True)
161
- assert not (cursor / "docs" / "CODE_RULES.md").is_file()
162
-
163
-
164
- def test_dry_run_does_not_create_output_directory(
165
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
166
- ) -> None:
167
- claude = tmp_path / ".claude"
168
- cursor = tmp_path / ".cursor"
169
- _minimal_rule_files(claude / "rules")
170
- _minimal_code_rules_and_test_quality(claude / "docs")
171
- monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
172
- assert run_sync_to_cursor(["--dry-run", "--quiet"]) == 0
173
- assert not (cursor / "rules").exists(), "--dry-run must not create the output directory"
174
-
175
-
176
- def test_check_skips_optional_mapping_when_source_missing(
177
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
178
- ) -> None:
179
- claude = tmp_path / ".claude"
180
- _minimal_rule_files(claude / "rules")
181
- _minimal_code_rules_and_test_quality(claude / "docs")
182
- monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
183
- assert run_sync_to_cursor(["--force"]) == 0
184
- (claude / "rules" / "tasklings-preferences.md").unlink()
185
- assert run_sync_to_cursor(["--check"]) == 0, "--check must pass when only optional sources are missing"
186
-
187
-
188
- def test_test_quality_glob_derived_from_testing_frontmatter() -> None:
189
- expected_glob = _read_paths_glob(_REAL_TESTING_RULE)
190
- assert expected_glob, "testing.md must declare a non-empty paths frontmatter"
191
- pinned_glob = "**/test_*.py,**/*_test.py,**/*.test.*,**/*.spec.*,**/conftest.py,**/tests/**"
192
- assert expected_glob == pinned_glob
193
- test_quality_mapping = next(
194
- mapping for mapping in build_mappings(_CLAUDE_DEV_ENV_DIR) if mapping.key == "test-quality"
195
- )
196
- assert test_quality_mapping.globs == expected_glob
197
- assert test_quality_mapping.globs == pinned_glob
198
-
199
-
200
- def test_tasklings_glob_derived_from_frontmatter(tmp_path: Path) -> None:
201
- rules_directory = tmp_path / "rules"
202
- rules_directory.mkdir(parents=True)
203
- (rules_directory / "tasklings-preferences.md").write_text(
204
- '---\npaths:\n - "Y:/MyProject/**"\n - "Z:/Other/**"\n---\n\n# Tasklings\n',
205
- encoding="utf-8",
206
- )
207
- glob_value = _read_paths_glob(rules_directory / "tasklings-preferences.md")
208
- assert glob_value == "Y:/MyProject/**,Z:/Other/**"
209
-
210
-
211
- def test_merge_reference_headers_point_at_cursor_docs(tmp_path: Path) -> None:
212
- rules_directory = tmp_path / "rules"
213
- docs_directory = tmp_path / "docs"
214
- rules_directory.mkdir(parents=True, exist_ok=True)
215
- docs_directory.mkdir(parents=True, exist_ok=True)
216
- (rules_directory / "code-standards.md").write_text("# CS\n", encoding="utf-8")
217
- cr_body = "\n\n".join(f"## {t}\n\nbody" for t in _code_standards_section_order)
218
- (docs_directory / "CODE_RULES.md").write_text(cr_body, encoding="utf-8")
219
- merged = mod.merge_code_standards(
220
- (rules_directory / "code-standards.md", docs_directory / "CODE_RULES.md")
221
- )
222
- assert ".cursor/docs/CODE_RULES.md" in merged
223
-
224
- (rules_directory / "testing.md").write_text("# T\n", encoding="utf-8")
225
- tq_body = "\n\n".join(f"## {t}\n\nbody" for t in _test_quality_section_order)
226
- (docs_directory / "TEST_QUALITY.md").write_text(tq_body, encoding="utf-8")
227
- merged_tq = mod.merge_test_quality(
228
- (rules_directory / "testing.md", docs_directory / "TEST_QUALITY.md")
229
- )
230
- assert ".cursor/docs/TEST_QUALITY.md" in merged_tq
231
-
232
-
233
- def test_code_standards_section_order_titles_all_exist_in_real_doc() -> None:
234
- real_headings = set(_parse_h2_sections(_REAL_CODE_RULES_DOC.read_text(encoding="utf-8")))
235
- missing_titles = [title for title in _code_standards_section_order if title not in real_headings]
236
- assert missing_titles == [], (
237
- f"_code_standards_section_order titles absent from {_REAL_CODE_RULES_DOC.name}: "
238
- f"{missing_titles}"
239
- )
240
-
241
-
242
- def test_test_quality_section_order_titles_all_exist_in_real_doc() -> None:
243
- real_headings = set(_parse_h2_sections(_REAL_TEST_QUALITY_DOC.read_text(encoding="utf-8")))
244
- missing_titles = [title for title in _test_quality_section_order if title not in real_headings]
245
- assert missing_titles == [], (
246
- f"_test_quality_section_order titles absent from {_REAL_TEST_QUALITY_DOC.name}: "
247
- f"{missing_titles}"
248
- )
249
-
250
-
251
- def test_merge_code_standards_raises_when_expected_section_absent(tmp_path: Path) -> None:
252
- rules_directory = tmp_path / "rules"
253
- docs_directory = tmp_path / "docs"
254
- rules_directory.mkdir(parents=True, exist_ok=True)
255
- docs_directory.mkdir(parents=True, exist_ok=True)
256
- (rules_directory / "code-standards.md").write_text("# CS\n", encoding="utf-8")
257
- present_titles = [
258
- title for title in _code_standards_section_order if title != "5. NO ABBREVIATIONS"
259
- ]
260
- cr_body = "\n\n".join(f"## {title}\n\nbody" for title in present_titles)
261
- (docs_directory / "CODE_RULES.md").write_text(cr_body, encoding="utf-8")
262
- with pytest.raises(AssertionError, match="5. NO ABBREVIATIONS"):
263
- mod.merge_code_standards(
264
- (rules_directory / "code-standards.md", docs_directory / "CODE_RULES.md")
265
- )
266
-
267
-
268
- def test_build_mappings_raises_when_testing_lacks_paths_frontmatter(tmp_path: Path) -> None:
269
- claude = tmp_path / ".claude"
270
- _minimal_rule_files(claude / "rules")
271
- _minimal_code_rules_and_test_quality(claude / "docs")
272
- (claude / "rules" / "testing.md").write_text("# Testing\n", encoding="utf-8")
273
- with pytest.raises(AssertionError, match="testing.md"):
274
- build_mappings(claude)
275
-
276
-
277
- def test_merge_test_quality_raises_when_expected_section_absent(tmp_path: Path) -> None:
278
- rules_directory = tmp_path / "rules"
279
- docs_directory = tmp_path / "docs"
280
- rules_directory.mkdir(parents=True, exist_ok=True)
281
- docs_directory.mkdir(parents=True, exist_ok=True)
282
- (rules_directory / "testing.md").write_text("# T\n", encoding="utf-8")
283
- present_titles = [
284
- title for title in _test_quality_section_order if title != "Core Testing Principles"
285
- ]
286
- tq_body = "\n\n".join(f"## {title}\n\nbody" for title in present_titles)
287
- (docs_directory / "TEST_QUALITY.md").write_text(tq_body, encoding="utf-8")
288
- with pytest.raises(AssertionError, match="Core Testing Principles"):
289
- mod.merge_test_quality(
290
- (rules_directory / "testing.md", docs_directory / "TEST_QUALITY.md")
291
- )
292
-
293
-
294
- def test_merge_test_quality_strips_leading_paths_frontmatter(tmp_path: Path) -> None:
295
- docs_directory = tmp_path / "docs"
296
- docs_directory.mkdir(parents=True, exist_ok=True)
297
- test_quality_body = "\n\n".join(
298
- f"## {title}\n\nbody" for title in _test_quality_section_order
299
- )
300
- (docs_directory / "TEST_QUALITY.md").write_text(test_quality_body, encoding="utf-8")
301
- merged = mod.merge_test_quality(
302
- (_REAL_TESTING_RULE, docs_directory / "TEST_QUALITY.md")
303
- )
304
- testing_text = _REAL_TESTING_RULE.read_text(encoding="utf-8")
305
- first_content_line = next(
306
- line for line in strip_leading_yaml_frontmatter(testing_text).splitlines()
307
- if line.strip()
308
- )
309
- merged_lines = merged.splitlines()
310
- assert merged_lines[0] != "---", "merged body must not open with a leaked frontmatter fence"
311
- assert merged_lines[0] == first_content_line
312
- body_before_reference = "\n".join(
313
- merged_lines[: merged_lines.index("## Reference (full text: `.cursor/docs/TEST_QUALITY.md`)")]
314
- )
315
- assert "paths:" not in body_before_reference, "testing.md paths frontmatter leaked into rule body"