claude-dev-env 4.2.0 → 4.3.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 (78) hide show
  1. package/.agents/skills/_shared/pr-loop/scripts/durable_post_lint.py +242 -0
  2. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py +106 -0
  3. package/.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint.py +332 -0
  4. package/.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py +25 -0
  5. package/.agents/skills/issue-tracker/SKILL.md +6 -0
  6. package/.agents/skills/pull-request/SKILL.md +218 -0
  7. package/.agents/skills/pull-request/reference/publication-tasks.md +18 -0
  8. package/.agents/skills/pull-request/scripts/github_pr_command_constants/__init__.py +1 -0
  9. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/__init__.py +1 -0
  10. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +61 -0
  11. package/.agents/skills/pull-request/scripts/pull_request.py +276 -0
  12. package/.agents/skills/pull-request/scripts/recover_legacy_author.py +251 -0
  13. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +49 -0
  14. package/.agents/skills/pull-request/scripts/test_pull_request.py +398 -0
  15. package/.agents/skills/pull-request/scripts/test_recover_legacy_author.py +221 -0
  16. package/.agents/skills/test-runner/SKILL.md +69 -0
  17. package/.agents/skills/test-runner/scripts/preflight_checks.py +365 -0
  18. package/.agents/skills/test-runner/scripts/run_tests.py +316 -0
  19. package/.agents/skills/test-runner/scripts/test_preflight_checks.py +185 -0
  20. package/.agents/skills/test-runner/scripts/test_run_tests.py +281 -0
  21. package/.agents/skills/test-runner/scripts/test_runner_constants/__init__.py +1 -0
  22. package/.agents/skills/test-runner/scripts/test_runner_constants/config/__init__.py +1 -0
  23. package/.agents/skills/test-runner/scripts/test_runner_constants/config/constants.py +96 -0
  24. package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
  25. package/bin/ever-shipped-skills.mjs +2 -0
  26. package/bin/ever-shipped-skills.test.mjs +8 -0
  27. package/bin/install.mjs +9 -1
  28. package/bin/install.prune.test.mjs +90 -0
  29. package/bin/install.test.mjs +24 -7
  30. package/hooks/blocking/pii_prevention_blocker.py +32 -51
  31. package/hooks/blocking/test_bash_post_call_dispatcher.py +5 -28
  32. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +1 -4
  33. package/hooks/blocking/test_code_rules_command_dispatch.py +2 -2
  34. package/hooks/blocking/test_pii_prevention_blocker.py +37 -89
  35. package/hooks/gh_post_body_texts.py +195 -0
  36. package/hooks/hooks.json +0 -15
  37. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +0 -1
  38. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -8
  39. package/hooks/hooks_constants/gh_post_body_texts_constants.py +12 -0
  40. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +0 -7
  41. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -7
  42. package/hooks/hooks_constants/test_failure_recorder_constants.py +3 -3
  43. package/hooks/hooks_constants/test_gh_post_body_texts_constants.py +19 -0
  44. package/hooks/observability/test_failure_recorder.py +1 -1
  45. package/hooks/pyproject.toml +1 -1
  46. package/hooks/test_gh_post_body_texts.py +79 -0
  47. package/package.json +1 -1
  48. package/rules/durable-post-artifacts.md +31 -20
  49. package/rules/gh-cli-conventions.md +23 -13
  50. package/rules/git-workflow.md +21 -8
  51. package/scripts/policy_lint/adapter_configuration.py +53 -12
  52. package/scripts/repository_checks/config/constants.py +0 -5
  53. package/scripts/tests/test_adapter_configuration.py +54 -0
  54. package/hooks/_gh_pr_author_swap_utils.py +0 -1225
  55. package/hooks/blocking/conventional_pr_title_gate.py +0 -443
  56. package/hooks/blocking/gh_body_arg_blocker.py +0 -176
  57. package/hooks/blocking/gh_pr_author_enforcer.py +0 -487
  58. package/hooks/blocking/gh_pr_author_restore.py +0 -105
  59. package/hooks/blocking/pr_description_writer_gate.py +0 -256
  60. package/hooks/blocking/test_conventional_pr_title_gate.py +0 -640
  61. package/hooks/blocking/test_gh_body_arg_blocker.py +0 -441
  62. package/hooks/blocking/test_gh_pr_author_enforcer.py +0 -1166
  63. package/hooks/blocking/test_gh_pr_author_restore.py +0 -513
  64. package/hooks/blocking/test_gh_pr_author_swap_utils.py +0 -910
  65. package/hooks/blocking/test_pr_description_writer_gate.py +0 -234
  66. package/hooks/blocking/test_preflight_check.py +0 -343
  67. package/hooks/blocking/test_test_preflight_check.py +0 -80
  68. package/hooks/blocking/test_volatile_path_in_post_blocker.py +0 -322
  69. package/hooks/blocking/volatile_path_in_post_blocker.py +0 -461
  70. package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +0 -58
  71. package/hooks/hooks_constants/gh_pr_author_swap_constants.py +0 -76
  72. package/hooks/hooks_constants/pr_description_writer_gate_constants.py +0 -73
  73. package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +0 -55
  74. package/hooks/observability/pr_description_writer_spawn_tracker.py +0 -113
  75. package/hooks/observability/test_pr_description_writer_spawn_tracker.py +0 -150
  76. package/hooks/session/gh_pr_author_session_cleanup.py +0 -175
  77. package/hooks/session/test_gh_pr_author_session_cleanup.py +0 -576
  78. package/hooks/test__gh_pr_author_swap_utils.py +0 -333
@@ -23,30 +23,69 @@ def hook_configuration_diagnostics(
23
23
  adapter_support._document_path(repository_root, document)
24
24
  if document.path.name != "hooks.json":
25
25
  return ()
26
- all_messages = _hook_configuration_messages(document.text)
26
+ all_messages = _hook_configuration_messages(document.text, document.prior_text)
27
27
  return adapter_support._diagnostics_for_messages(
28
28
  document, "hook-configuration", all_messages
29
29
  )
30
30
 
31
31
 
32
- def _hook_configuration_messages(configuration_text: str) -> tuple[str, ...]:
32
+ def _parsed_configuration(
33
+ configuration_text: str,
34
+ invalid_json_message: str,
35
+ invalid_object_message: str,
36
+ ) -> dict[object, object]:
33
37
  try:
34
38
  parsed_configuration = json.loads(configuration_text)
35
- except json.JSONDecodeError:
36
- return ("Hook configuration is not valid JSON",)
39
+ except json.JSONDecodeError as error:
40
+ raise ValueError(invalid_json_message) from error
37
41
  if not isinstance(parsed_configuration, dict):
38
- return ("Hook configuration must be an object",)
39
- all_registered_strings = _registered_hook_strings(parsed_configuration)
40
- if any(
41
- _contains_action_boundary_marker(each_string)
42
- for each_string in all_registered_strings
43
- ):
42
+ raise TypeError(invalid_object_message)
43
+ return parsed_configuration
44
+
45
+
46
+ def _action_boundary_strings(
47
+ all_configuration_entries: dict[object, object],
48
+ ) -> frozenset[str]:
49
+ return frozenset(
50
+ each_string
51
+ for each_string in _registered_hook_strings(all_configuration_entries)
52
+ if _contains_action_boundary_marker(each_string)
53
+ )
54
+
55
+
56
+ def _hook_configuration_messages(
57
+ configuration_text: str,
58
+ prior_configuration_text: str | None,
59
+ ) -> tuple[str, ...]:
60
+ try:
61
+ current_configuration = _parsed_configuration(
62
+ configuration_text,
63
+ "Hook configuration is not valid JSON",
64
+ "Hook configuration must be an object",
65
+ )
66
+ all_current_action_boundary_strings = _action_boundary_strings(
67
+ current_configuration
68
+ )
69
+ if prior_configuration_text is None:
70
+ all_prior_action_boundary_strings: frozenset[str] = frozenset()
71
+ else:
72
+ prior_configuration = _parsed_configuration(
73
+ prior_configuration_text,
74
+ "Prior hook configuration is not valid JSON",
75
+ "Prior hook configuration must be an object",
76
+ )
77
+ all_prior_action_boundary_strings = _action_boundary_strings(
78
+ prior_configuration
79
+ )
80
+ except (TypeError, ValueError) as error:
81
+ return (str(error),)
82
+ if all_current_action_boundary_strings - all_prior_action_boundary_strings:
44
83
  return ("Hook configuration registers a deny, block, or ask policy path",)
45
84
  return ()
46
85
 
47
86
 
48
87
  def _registered_hook_strings(
49
- all_configuration_entries: dict[object, object]
88
+ all_configuration_entries: dict[object, object],
50
89
  ) -> tuple[str, ...]:
51
90
  all_registered_strings: list[str] = []
52
91
  for each_key, each_entry in all_configuration_entries.items():
@@ -62,7 +101,9 @@ def _registered_strings_for_entry(
62
101
  all_registered_strings: list[str] = []
63
102
  if each_key in constants.ALL_HOOK_REGISTRATION_KEYS and isinstance(each_entry, str):
64
103
  all_registered_strings.append(each_entry)
65
- if each_key in constants.ALL_DECISION_REGISTRATION_KEYS and isinstance(each_entry, str):
104
+ if each_key in constants.ALL_DECISION_REGISTRATION_KEYS and isinstance(
105
+ each_entry, str
106
+ ):
66
107
  all_registered_strings.append(each_entry)
67
108
  all_registered_strings.extend(_registered_nested_strings(each_entry))
68
109
  return tuple(all_registered_strings)
@@ -131,11 +131,6 @@ ALL_TRACKED_SECRET_EXACT_EXEMPTIONS: frozenset[tuple[str, str, str]] = frozenset
131
131
  "home-path",
132
132
  "09f83c224a02c370d3d63de5c330e1a830fc92866330fc7963b5b5fd20ffcb91",
133
133
  ),
134
- (
135
- "packages/claude-dev-env/hooks/blocking/volatile_path_in_post_blocker.py",
136
- "home-path",
137
- "ed9203c8078e4c6b832600085f28f24656d679aa8dca82ecc2557afc3df25036",
138
- ),
139
134
  (
140
135
  "packages/claude-dev-env/hooks/hooks_constants/hardcoded_user_path_constants.py",
141
136
  "home-path",
@@ -0,0 +1,54 @@
1
+ """Incremental migration tests for the hook-configuration lint rule."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import replace
7
+ from pathlib import Path
8
+
9
+ from policy_lint import adapters
10
+ from policy_lint.model import Document
11
+
12
+
13
+ def _hook_document(all_commands: list[str], prior_text: str) -> Document:
14
+ current_text = json.dumps(
15
+ {
16
+ "hooks": {
17
+ "PreToolUse": [
18
+ {
19
+ "hooks": [
20
+ {"command": each_command} for each_command in all_commands
21
+ ]
22
+ }
23
+ ]
24
+ }
25
+ }
26
+ )
27
+ return replace(
28
+ Document.from_text("hooks/hooks.json", current_text),
29
+ prior_text=prior_text,
30
+ )
31
+
32
+
33
+ def test_incremental_blocker_removal_is_clean(tmp_path: Path) -> None:
34
+ prior_text = _hook_document(
35
+ ["hooks/blocking/first.py", "hooks/blocking/second.py"],
36
+ '{"hooks": {}}',
37
+ ).text
38
+ current_document = _hook_document(
39
+ ["hooks/blocking/second.py"],
40
+ prior_text,
41
+ )
42
+ assert adapters.hook_configuration_diagnostics(current_document, tmp_path) == ()
43
+
44
+
45
+ def test_new_blocker_in_staged_change_is_rejected(tmp_path: Path) -> None:
46
+ current_document = _hook_document(
47
+ ["hooks/blocking/new.py"],
48
+ '{"hooks": {}}',
49
+ )
50
+ all_diagnostics = adapters.hook_configuration_diagnostics(
51
+ current_document, tmp_path
52
+ )
53
+ assert len(all_diagnostics) == 1
54
+ assert all_diagnostics[0].rule_id == "hook-configuration"