claude-dev-env 2.27.0 → 2.28.1
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.
- package/.agents/skills/hitl/SKILL.md +45 -0
- package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +16 -29
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
- package/bin/ever-shipped-skills.mjs +1 -0
- package/docs/CODE_RULES.md +3 -3
- package/docs/agent-spawn-protocol.md +1 -1
- package/docs/references/prose-style-enforcement.md +5 -9
- package/hooks/blocking/AGENTS.md +3 -13
- package/hooks/blocking/code_rules_docstrings.py +10 -2138
- package/hooks/blocking/code_rules_enforcer.py +0 -121
- package/hooks/blocking/code_rules_imports_logging.py +1 -236
- package/hooks/blocking/code_rules_shared.py +23 -0
- package/hooks/blocking/code_rules_test_layout.py +8 -8
- package/hooks/blocking/config/prose_style_enforcement_constants.py +4 -2
- package/hooks/blocking/config/test_prose_style_enforcement_constants.py +5 -1
- package/hooks/blocking/precommit_code_rules_gate.py +8 -43
- package/hooks/blocking/state_description_blocker.py +1 -7
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
- package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +0 -8
- package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +11 -112
- package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +0 -1
- package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +1 -18
- package/hooks/blocking/test_code_rules_shared.py +12 -0
- package/hooks/blocking/test_precommit_code_rules_gate.py +32 -179
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -1
- package/hooks/blocking/test_state_description_blocker.py +6 -4
- package/hooks/blocking/test_stop_dispatcher.py +5 -29
- package/hooks/hooks_constants/AGENTS.md +1 -6
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -1
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +3 -0
- package/hooks/hooks_constants/messages.py +0 -2
- package/hooks/hooks_constants/precommit_code_rules_gate_constants.py +3 -17
- package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -2
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -1
- package/hooks/hooks_constants/test_code_rules_enforcer_constants.py +7 -0
- package/hooks/hooks_constants/test_messages.py +5 -3
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -2
- package/package.json +1 -1
- package/rules/AGENTS.md +3 -6
- package/rules/claims-as-quotes.md +0 -10
- package/rules/code-standards.md +6 -6
- package/rules/explore-thoroughly.md +0 -1
- package/rules/failure-blast-radius.md +0 -8
- package/rules/falsify-before-green.md +0 -8
- package/rules/file-global-constants.md +2 -2
- package/rules/filesystem-search.md +1 -1
- package/rules/git-workflow.md +1 -9
- package/rules/hedging-claims.md +2 -6
- package/rules/long-horizon-autonomy.md +1 -1
- package/rules/measurement-denominators.md +0 -9
- package/rules/research-mode.md +0 -6
- package/rules/verify-before-asking.md +0 -5
- package/rules/verify-runtime-state.md +0 -5
- package/scripts/codex_compat_materializer.py +99 -24
- package/scripts/sync_to_cursor/rules.py +0 -10
- package/scripts/tests/test_codex_compat_materializer.py +70 -19
- package/scripts/tests/test_engine.py +0 -1
- package/scripts/tests/test_rules.py +0 -1
- package/scripts/tests/test_sync_to_cursor.py +0 -1
- package/system-prompts/software-engineer.xml +3 -3
- package/hooks/blocking/code_rules_dead_argparse_argument.py +0 -554
- package/hooks/blocking/code_rules_dead_config_field.py +0 -568
- package/hooks/blocking/code_rules_dead_dataclass_field.py +0 -348
- package/hooks/blocking/code_rules_dead_module_constant.py +0 -757
- package/hooks/blocking/code_rules_dead_split_branch.py +0 -225
- package/hooks/blocking/code_rules_mock_completeness.py +0 -295
- package/hooks/blocking/code_rules_scope_binding.py +0 -151
- package/hooks/blocking/code_rules_unused_imports.py +0 -197
- package/hooks/blocking/hedging_language_blocker.py +0 -221
- package/hooks/blocking/intent_only_ending_blocker.py +0 -148
- package/hooks/blocking/test_code_rules_enforcer_dead_argparse_argument.py +0 -534
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +0 -846
- package/hooks/blocking/test_code_rules_enforcer_dead_dataclass_field.py +0 -507
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +0 -679
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_alias.py +0 -133
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +0 -103
- package/hooks/blocking/test_code_rules_enforcer_dead_split_branch.py +0 -105
- package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +0 -425
- package/hooks/blocking/test_code_rules_enforcer_docstring_cardinal_family.py +0 -176
- package/hooks/blocking/test_code_rules_enforcer_docstring_delegation_summary.py +0 -385
- package/hooks/blocking/test_code_rules_enforcer_docstring_fallback_branch.py +0 -398
- package/hooks/blocking/test_code_rules_enforcer_docstring_field_runmode_outcome.py +0 -129
- package/hooks/blocking/test_code_rules_enforcer_docstring_inline_literal_claim.py +0 -93
- package/hooks/blocking/test_code_rules_enforcer_docstring_length_constant_superlative.py +0 -198
- package/hooks/blocking/test_code_rules_enforcer_docstring_mark_glyph_enumeration.py +0 -262
- package/hooks/blocking/test_code_rules_enforcer_docstring_no_consumer.py +0 -93
- package/hooks/blocking/test_code_rules_enforcer_docstring_no_network.py +0 -115
- package/hooks/blocking/test_code_rules_enforcer_docstring_raises_largezipfile.py +0 -226
- package/hooks/blocking/test_code_rules_enforcer_docstring_returns_plural_cardinality.py +0 -207
- package/hooks/blocking/test_code_rules_enforcer_docstring_step_dispatch.py +0 -262
- package/hooks/blocking/test_code_rules_enforcer_docstring_type_checking_gate.py +0 -164
- package/hooks/blocking/test_code_rules_enforcer_docstring_unguarded_payload.py +0 -188
- package/hooks/blocking/test_code_rules_enforcer_import_block_sort.py +0 -157
- package/hooks/blocking/test_code_rules_enforcer_split_mocks_1.py +0 -303
- package/hooks/blocking/test_code_rules_enforcer_split_mocks_2.py +0 -111
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +0 -656
- package/hooks/blocking/test_hedging_language_blocker.py +0 -261
- package/hooks/blocking/test_intent_only_ending_blocker.py +0 -209
- package/hooks/hooks_constants/dead_argparse_argument_constants.py +0 -28
- package/hooks/hooks_constants/dead_config_field_constants.py +0 -39
- package/hooks/hooks_constants/dead_dataclass_field_constants.py +0 -25
- package/hooks/hooks_constants/dead_module_constant_constants.py +0 -30
- package/hooks/hooks_constants/hedging_uncertainty_constants.py +0 -42
- package/hooks/hooks_constants/test_dispatcher_constants_docstrings.py +0 -44
- package/rules/conservative-action.md +0 -17
- package/rules/context7.md +0 -8
- package/rules/few-words.md +0 -3
- package/rules/parallel-tools.md +0 -23
|
@@ -2,7 +2,7 @@ import json
|
|
|
2
2
|
import subprocess
|
|
3
3
|
import sys
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any, cast
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
8
|
import tomllib
|
|
@@ -30,6 +30,23 @@ from codex_compat_materializer import (
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def _required_manifest_path(config: MaterializerConfig) -> Path:
|
|
34
|
+
assert config.manifest_path is not None
|
|
35
|
+
return config.manifest_path
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _manifest_files(manifest_path: Path) -> dict[str, dict[str, object]]:
|
|
39
|
+
return cast(dict[str, dict[str, object]], load_manifest(manifest_path)["files"])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _hook_group(matcher: str, *all_hook_commands: str) -> dict[str, object]:
|
|
43
|
+
return {"matcher": matcher, "hooks": [{"type": "command", "command": each_command} for each_command in all_hook_commands]}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _hook_commands(manifest: dict[str, Any], event_name: str) -> list[str]:
|
|
47
|
+
return [each_hook["command"] for each_group in manifest["hooks"][event_name] for each_hook in each_group["hooks"]]
|
|
48
|
+
|
|
49
|
+
|
|
33
50
|
def test_conversion_escapes_toml_content() -> None:
|
|
34
51
|
agent = parse_frontmatter(Path("agent.md"), '---\nname: sample\ndescription: "say \\"hi\\""\ntools: ["Read", "Write"]\n---\n', "agent.md")
|
|
35
52
|
converted = convert_agent(agent)
|
|
@@ -196,7 +213,7 @@ def test_case_folded_target_collisions_include_existing_and_manifest(tmp_path: P
|
|
|
196
213
|
def test_manifest_target_collision_is_rejected(tmp_path: Path) -> None:
|
|
197
214
|
config = MaterializerConfig(tmp_path / "source", tmp_path / "target", should_apply=True)
|
|
198
215
|
planned = [PlannedFile("a.md", "Luna.toml", "managed", "hash")]
|
|
199
|
-
atomic_write(config
|
|
216
|
+
atomic_write(_required_manifest_path(config), '{"files": {"luna.TOML": {}}, "version": 1}\n')
|
|
200
217
|
with pytest.raises(MaterializerError):
|
|
201
218
|
publish_plan(config, planned)
|
|
202
219
|
|
|
@@ -206,12 +223,12 @@ def test_first_run_manifest_target_collision_is_rejected(tmp_path: Path) -> None
|
|
|
206
223
|
target = tmp_path / "target"
|
|
207
224
|
source.mkdir()
|
|
208
225
|
config = MaterializerConfig(source, target, should_apply=True)
|
|
209
|
-
planned = [PlannedFile("a.md", config.
|
|
226
|
+
planned = [PlannedFile("a.md", _required_manifest_path(config).name, "managed", "hash")]
|
|
210
227
|
|
|
211
228
|
with pytest.raises(MaterializerError, match="compatibility manifest"):
|
|
212
229
|
publish_plan(config, planned)
|
|
213
230
|
|
|
214
|
-
assert not config.
|
|
231
|
+
assert not _required_manifest_path(config).exists()
|
|
215
232
|
assert not config.target_root.exists()
|
|
216
233
|
|
|
217
234
|
|
|
@@ -238,11 +255,11 @@ def test_dry_run_is_non_mutating_and_writes_are_idempotent(tmp_path: Path) -> No
|
|
|
238
255
|
planned, report = build_plan(apply_config)
|
|
239
256
|
publish_plan(apply_config, planned, report)
|
|
240
257
|
first = (target / "Luna.toml").read_text(encoding="utf-8")
|
|
241
|
-
first_manifest = apply_config.
|
|
258
|
+
first_manifest = _required_manifest_path(apply_config).read_bytes()
|
|
242
259
|
planned, report = build_plan(apply_config)
|
|
243
260
|
second_report = publish_plan(apply_config, planned, report)
|
|
244
261
|
assert (target / "Luna.toml").read_text(encoding="utf-8") == first
|
|
245
|
-
assert apply_config.
|
|
262
|
+
assert _required_manifest_path(apply_config).read_bytes() == first_manifest
|
|
246
263
|
assert second_report.written == 0
|
|
247
264
|
assert second_report.deleted == 0
|
|
248
265
|
assert second_report.errors == 0
|
|
@@ -292,13 +309,13 @@ def test_manifest_is_published_last_and_previous_manifest_survives_failure(tmp_p
|
|
|
292
309
|
target = tmp_path / "target"
|
|
293
310
|
source.mkdir()
|
|
294
311
|
config = MaterializerConfig(source, target, should_apply=True)
|
|
295
|
-
manifest = config
|
|
312
|
+
manifest = _required_manifest_path(config)
|
|
296
313
|
manifest.parent.mkdir()
|
|
297
314
|
atomic_write(manifest, '{"files": {}, "version": 1}\n')
|
|
298
315
|
planned = [PlannedFile("agent.md", "Luna.toml", "managed", "hash")]
|
|
299
316
|
with pytest.raises(RuntimeError):
|
|
300
317
|
publish_plan(config, planned, failure_injector=lambda _: (_ for _ in ()).throw(RuntimeError("stop")))
|
|
301
|
-
assert
|
|
318
|
+
assert _manifest_files(manifest) == {}
|
|
302
319
|
assert not (target / "Luna.toml").exists()
|
|
303
320
|
|
|
304
321
|
|
|
@@ -399,7 +416,7 @@ def test_report_contains_deterministic_category_details_and_error_count(
|
|
|
399
416
|
assert (target / "Luna.toml").read_text(encoding="utf-8") == "new"
|
|
400
417
|
|
|
401
418
|
(target / "Luna.toml").write_text("changed", encoding="utf-8")
|
|
402
|
-
atomic_write(config
|
|
419
|
+
atomic_write(_required_manifest_path(config), json.dumps({"version": 1, "files": {
|
|
403
420
|
"Luna.toml": {"hash": hash_content(managed_content)}
|
|
404
421
|
}}))
|
|
405
422
|
stale_report = publish_plan(pruning_config, [])
|
|
@@ -407,7 +424,7 @@ def test_report_contains_deterministic_category_details_and_error_count(
|
|
|
407
424
|
assert stale_report.details["stale_managed"] == []
|
|
408
425
|
|
|
409
426
|
(target / "Luna.toml").write_text(managed_content, encoding="utf-8")
|
|
410
|
-
atomic_write(config
|
|
427
|
+
atomic_write(_required_manifest_path(config), json.dumps({"version": 1, "files": {
|
|
411
428
|
"Luna.toml": {"hash": hash_content(managed_content)}
|
|
412
429
|
}}))
|
|
413
430
|
deleted_report = publish_plan(pruning_config, [])
|
|
@@ -423,7 +440,7 @@ def test_report_contains_deterministic_category_details_and_error_count(
|
|
|
423
440
|
assert collision_report.details["conflicted"] == ["Nova.toml"]
|
|
424
441
|
|
|
425
442
|
manifest = {"version": 1, "files": {"Missing.toml": {"hash": hash_content("missing")}}}
|
|
426
|
-
atomic_write(config
|
|
443
|
+
atomic_write(_required_manifest_path(config), json.dumps(manifest))
|
|
427
444
|
error_report = publish_plan(pruning_config, [])
|
|
428
445
|
assert error_report.errors == 1
|
|
429
446
|
assert error_report.error_details == ["missing managed path: Missing.toml"]
|
|
@@ -582,7 +599,7 @@ def test_reapply_rewrites_a_managed_file_whose_source_description_changed(
|
|
|
582
599
|
assert 'description = "Light"' not in published_text
|
|
583
600
|
assert refreshed_report.written == 1
|
|
584
601
|
assert refreshed_report.conflicted == 0
|
|
585
|
-
assert
|
|
602
|
+
assert _manifest_files(_required_manifest_path(config))["Luna.toml"]["hash"] == hash_content(
|
|
586
603
|
published_text
|
|
587
604
|
)
|
|
588
605
|
|
|
@@ -624,7 +641,7 @@ def test_crash_orphan_matching_the_plan_is_adopted_into_the_manifest(tmp_path: P
|
|
|
624
641
|
assert adoption_report.errors == 0
|
|
625
642
|
assert adoption_report.details["adopted"] == ["Nova.toml"]
|
|
626
643
|
assert (target / "Nova.toml").read_text(encoding="utf-8") == orphan_content
|
|
627
|
-
assert
|
|
644
|
+
assert _manifest_files(_required_manifest_path(config))["Nova.toml"]["hash"] == hash_content(
|
|
628
645
|
orphan_content
|
|
629
646
|
)
|
|
630
647
|
|
|
@@ -806,7 +823,7 @@ def test_build_plan_publishes_owned_agents_projection_and_tracks_drift(
|
|
|
806
823
|
|
|
807
824
|
projected_path = target / "AGENTS.md"
|
|
808
825
|
first_projection = projected_path.read_text(encoding="utf-8")
|
|
809
|
-
manifest_record =
|
|
826
|
+
manifest_record = _manifest_files(_required_manifest_path(config))["AGENTS.md"]
|
|
810
827
|
assert manifest_record["ownership"] == "codex-compat"
|
|
811
828
|
assert manifest_record["source"] == "rules/failure-blast-radius.md"
|
|
812
829
|
|
|
@@ -871,6 +888,7 @@ def _prepare_projection_fixture(
|
|
|
871
888
|
existing_hooks = {
|
|
872
889
|
"hooks": {
|
|
873
890
|
"PreToolUse": [
|
|
891
|
+
{"matcher": "apply_patch"},
|
|
874
892
|
{
|
|
875
893
|
"matcher": "Write|Edit",
|
|
876
894
|
"hooks": [{"type": "command", "command": "python existing.py"}],
|
|
@@ -894,6 +912,28 @@ def _prepare_projection_fixture(
|
|
|
894
912
|
],
|
|
895
913
|
},
|
|
896
914
|
],
|
|
915
|
+
"SessionStart": [_hook_group(
|
|
916
|
+
"",
|
|
917
|
+
"python3 %CODEX_HOME%\\hooks\\session\\untracked_repo_detector.py",
|
|
918
|
+
"python3 $env:CODEX_HOME/hooks/session/untracked_repo_detector.py",
|
|
919
|
+
"python3 ${env:CODEX_HOME}/hooks/session/untracked_repo_detector.py",
|
|
920
|
+
"python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/fix_worktree_hookspath.py",
|
|
921
|
+
)],
|
|
922
|
+
"UserPromptSubmit": [_hook_group(
|
|
923
|
+
"",
|
|
924
|
+
"python3 /home/example/custom/hooks/session/untracked_repo_detector.py",
|
|
925
|
+
"python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/untracked_repo_detector.py.bak",
|
|
926
|
+
)],
|
|
927
|
+
"ConfigChange": [
|
|
928
|
+
None,
|
|
929
|
+
{"matcher": "partial"},
|
|
930
|
+
{"matcher": "malformed", "hooks": "not a list"},
|
|
931
|
+
{"matcher": "malformed-hook", "hooks": [{"command": 7}]},
|
|
932
|
+
{"hooks": [{"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/untracked_repo_detector.py"}]},
|
|
933
|
+
_hook_group(
|
|
934
|
+
"retired", "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/untracked_repo_detector.py"
|
|
935
|
+
),
|
|
936
|
+
],
|
|
897
937
|
"Bash": [{"matcher": "", "hooks": []}],
|
|
898
938
|
}
|
|
899
939
|
}
|
|
@@ -938,7 +978,8 @@ def _assert_projection_and_manifest(
|
|
|
938
978
|
projected_instruction = (target / "AGENTS.md").read_text(encoding="utf-8")
|
|
939
979
|
assert "Three real attempts, then park." in projected_instruction
|
|
940
980
|
manifest = json.loads((target / "hooks.json").read_text(encoding="utf-8"))
|
|
941
|
-
assert manifest["hooks"]["PreToolUse"][0]
|
|
981
|
+
assert manifest["hooks"]["PreToolUse"][0]["matcher"] == "apply_patch"
|
|
982
|
+
assert manifest["hooks"]["PreToolUse"][1] == existing_hooks["hooks"]["PreToolUse"][1]
|
|
942
983
|
apply_patch_entries = [
|
|
943
984
|
each_entry
|
|
944
985
|
for each_entry in manifest["hooks"]["PreToolUse"]
|
|
@@ -953,7 +994,15 @@ def _assert_projection_and_manifest(
|
|
|
953
994
|
f'python3 "{target / "hooks" / "blocking" / "code_rules_enforcer.py"}"'
|
|
954
995
|
)
|
|
955
996
|
assert managed_command in apply_patch_commands
|
|
956
|
-
|
|
997
|
+
expected_commands_by_event = {
|
|
998
|
+
"SessionStart": ["python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/fix_worktree_hookspath.py"],
|
|
999
|
+
"UserPromptSubmit": ["python3 /home/example/custom/hooks/session/untracked_repo_detector.py", "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/untracked_repo_detector.py.bak"],
|
|
1000
|
+
}
|
|
1001
|
+
for each_event_name, expected_commands in expected_commands_by_event.items():
|
|
1002
|
+
assert _hook_commands(manifest, each_event_name) == expected_commands
|
|
1003
|
+
assert manifest["hooks"]["ConfigChange"] == existing_hooks["hooks"]["ConfigChange"][:5]
|
|
1004
|
+
assert manifest["hooks"]["Bash"] == existing_hooks["hooks"]["Bash"]
|
|
1005
|
+
manifest_record = _manifest_files(target / ".codex-compat-manifest.json")
|
|
957
1006
|
assert manifest_record["AGENTS.md"]["ownership"] == "codex-compat"
|
|
958
1007
|
assert manifest_record["hooks.json"]["source"] == "hooks/hooks.json"
|
|
959
1008
|
|
|
@@ -984,12 +1033,14 @@ def _assert_detached_enforcer_behavior(target: Path) -> None:
|
|
|
984
1033
|
assert allow_run.returncode == 0
|
|
985
1034
|
assert allow_run.stdout == ""
|
|
986
1035
|
|
|
1036
|
+
allow_tool_input = allow_payload["tool_input"]
|
|
1037
|
+
assert isinstance(allow_tool_input, dict)
|
|
1038
|
+
allow_command = allow_tool_input["command"]
|
|
1039
|
+
assert isinstance(allow_command, str)
|
|
987
1040
|
deny_payload = {
|
|
988
1041
|
**allow_payload,
|
|
989
1042
|
"tool_input": {
|
|
990
|
-
"command":
|
|
991
|
-
"AssetItemBlocked", "RuntimeError"
|
|
992
|
-
)
|
|
1043
|
+
"command": allow_command.replace("AssetItemBlocked", "RuntimeError")
|
|
993
1044
|
},
|
|
994
1045
|
}
|
|
995
1046
|
deny_run = subprocess.run(
|
|
@@ -46,7 +46,6 @@ def _write_minimal_curated_rules(rules_directory: Path) -> None:
|
|
|
46
46
|
encoding="utf-8",
|
|
47
47
|
)
|
|
48
48
|
(rules_directory / "research-mode.md").write_text("# RM\n", encoding="utf-8")
|
|
49
|
-
(rules_directory / "conservative-action.md").write_text("# CA\n", encoding="utf-8")
|
|
50
49
|
(rules_directory / "explore-thoroughly.md").write_text("# ET\n", encoding="utf-8")
|
|
51
50
|
|
|
52
51
|
|
|
@@ -30,7 +30,6 @@ def _write_minimal_curated_rules(rules_directory: Path) -> None:
|
|
|
30
30
|
encoding="utf-8",
|
|
31
31
|
)
|
|
32
32
|
(rules_directory / "research-mode.md").write_text("# RM\n", encoding="utf-8")
|
|
33
|
-
(rules_directory / "conservative-action.md").write_text("# CA\n", encoding="utf-8")
|
|
34
33
|
(rules_directory / "explore-thoroughly.md").write_text("# ET\n", encoding="utf-8")
|
|
35
34
|
|
|
36
35
|
|
|
@@ -44,7 +44,6 @@ def _minimal_rule_files(claude_rules: Path) -> None:
|
|
|
44
44
|
encoding="utf-8",
|
|
45
45
|
)
|
|
46
46
|
(claude_rules / "research-mode.md").write_text("# RM\n", encoding="utf-8")
|
|
47
|
-
(claude_rules / "conservative-action.md").write_text("# CA\n", encoding="utf-8")
|
|
48
47
|
(claude_rules / "explore-thoroughly.md").write_text("# ET\n", encoding="utf-8")
|
|
49
48
|
|
|
50
49
|
|
|
@@ -94,9 +94,9 @@
|
|
|
94
94
|
</problem_solving>
|
|
95
95
|
|
|
96
96
|
<code_quality>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
The checklist below is a short reminder, not a
|
|
97
|
+
~/.claude/docs/CODE_RULES.md is the full code-quality review contract, loaded on demand.
|
|
98
|
+
The checked-in .cursor/BUGBOT.md is the pointer file Cursor BugBot reads; it points at
|
|
99
|
+
CODE_RULES.md. The checklist below is a short reminder, not a
|
|
100
100
|
second canonical source.
|
|
101
101
|
|
|
102
102
|
Naming — full words only:
|