claude-dev-env 7.0.0 → 7.2.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 (210) hide show
  1. package/.agents/skills/pull-request/scripts/pull_request.py +2 -0
  2. package/.agents/skills/pull-request/scripts/test_pull_request.py +34 -0
  3. package/.agents/skills/session-continuity/AGENTS.md +1 -0
  4. package/.agents/skills/session-continuity/CLAUDE.md +1 -0
  5. package/.agents/skills/session-continuity/README.md +213 -0
  6. package/.agents/skills/session-continuity/SKILL.md +125 -0
  7. package/.agents/skills/session-continuity/continuity.mjs +447 -0
  8. package/.agents/skills/session-continuity/continuity.test.mjs +373 -0
  9. package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
  10. package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
  11. package/bin/cde.mjs +36 -8
  12. package/bin/cde.test.mjs +48 -0
  13. package/bin/ever-shipped-skills.mjs +2 -20
  14. package/bin/ever-shipped-skills.test.mjs +8 -0
  15. package/bin/expand_home_directory_tokens.mjs +4 -5
  16. package/bin/expand_home_directory_tokens.test.mjs +128 -0
  17. package/bin/git_hooks_installer.mjs +22 -3
  18. package/bin/git_hooks_installer.test.mjs +159 -5
  19. package/bin/install-session-continuity.mjs +88 -0
  20. package/bin/install-session-continuity.test.mjs +55 -0
  21. package/bin/install.codex-rules.test.mjs +5 -2
  22. package/bin/install.cursor-rules.test.mjs +5 -2
  23. package/bin/install.mjs +478 -63
  24. package/bin/install.policy-lint.test.mjs +56 -4
  25. package/bin/install.profiles.test.mjs +10 -0
  26. package/bin/install.prune.test.mjs +227 -17
  27. package/bin/install.test.mjs +500 -120
  28. package/bin/install.transaction.test.mjs +3 -3
  29. package/docs/CODE_RULES.md +1 -1
  30. package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
  31. package/hooks/blocking/precommit_code_rules_gate.py +5 -8
  32. package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
  33. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
  34. package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
  35. package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -244
  36. package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
  37. package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
  38. package/hooks/git-hooks/post_commit.py +33 -0
  39. package/hooks/git-hooks/pre_commit.py +18 -90
  40. package/hooks/git-hooks/pre_push.py +45 -541
  41. package/hooks/git-hooks/test_installed_policy_lint.py +55 -6
  42. package/hooks/git-hooks/test_native_hook_support.py +129 -0
  43. package/hooks/git-hooks/test_native_policy_commit.py +39 -6
  44. package/hooks/git-hooks/test_post_commit.py +43 -0
  45. package/hooks/git-hooks/test_pre_commit.py +36 -392
  46. package/hooks/git-hooks/test_pre_push.py +48 -1631
  47. package/hooks/git-hooks/test_verification_notice.py +60 -0
  48. package/hooks/git-hooks/test_verification_start.py +173 -0
  49. package/hooks/git-hooks/verification_notice.py +192 -0
  50. package/hooks/git-hooks/verification_notice_context.py +253 -0
  51. package/hooks/git-hooks/verification_notice_state.py +237 -0
  52. package/hooks/git-hooks/verification_start.py +100 -0
  53. package/hooks/hooks.json +0 -119
  54. package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
  55. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
  56. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
  57. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -50
  58. package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
  59. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
  60. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +42 -185
  61. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
  62. package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
  63. package/hooks/validators/run_all_validators.py +5 -1
  64. package/hooks/validators/security_checks.py +57 -2
  65. package/hooks/validators/test_hook_timing_harness.py +2 -5
  66. package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
  67. package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
  68. package/hooks/validators/test_security_checks.py +57 -0
  69. package/package.json +1 -1
  70. package/rules/confirm-implementation-forks.md +2 -0
  71. package/rules/git-workflow.md +12 -0
  72. package/rules/pstack-host-mapping.md +48 -0
  73. package/rules/vault-context.md +3 -5
  74. package/rules/verify-runtime-state.md +7 -0
  75. package/scripts/audit-hooks-cli.mjs +5 -0
  76. package/scripts/audit-hooks.mjs +72 -16
  77. package/scripts/automatic_advisory/__init__.py +1 -0
  78. package/scripts/automatic_advisory/checkout.py +51 -0
  79. package/scripts/automatic_advisory/cli.py +334 -0
  80. package/scripts/automatic_advisory/config/__init__.py +1 -0
  81. package/scripts/automatic_advisory/config/constants.py +93 -0
  82. package/scripts/automatic_advisory/config/timing.py +4 -0
  83. package/scripts/automatic_advisory/configuration.py +197 -0
  84. package/scripts/automatic_advisory/execution.py +350 -0
  85. package/scripts/automatic_advisory/git.py +69 -0
  86. package/scripts/automatic_advisory/model.py +107 -0
  87. package/scripts/automatic_advisory/process_host.py +32 -0
  88. package/scripts/automatic_advisory/publisher.py +84 -0
  89. package/scripts/automatic_advisory/runner.py +399 -0
  90. package/scripts/automatic_advisory/state.py +116 -0
  91. package/scripts/automatic_advisory/window_flags.py +41 -0
  92. package/scripts/automatic_advisory/windows_job.py +122 -0
  93. package/scripts/codex_compat_materializer.py +99 -13
  94. package/scripts/durable_post_lint.py +30 -2
  95. package/scripts/durable_post_lint_config/config/constants.py +10 -0
  96. package/scripts/durable_post_lint_config/config/test_constants.py +13 -0
  97. package/scripts/local_report_cli.py +184 -0
  98. package/scripts/local_report_core.py +247 -0
  99. package/scripts/local_report_publisher.py +12 -0
  100. package/scripts/local_report_validation.py +357 -0
  101. package/scripts/local_verification/__init__.py +23 -0
  102. package/scripts/local_verification/check_runner.py +364 -0
  103. package/scripts/local_verification/check_support.py +99 -0
  104. package/scripts/local_verification/cli.py +158 -0
  105. package/scripts/local_verification/command_runner.py +84 -0
  106. package/scripts/local_verification/config/__init__.py +1 -0
  107. package/scripts/local_verification/config/constants.py +89 -0
  108. package/scripts/local_verification/config/timing.py +1 -0
  109. package/scripts/local_verification/git_state.py +146 -0
  110. package/scripts/local_verification/manifest.py +224 -0
  111. package/scripts/local_verification/model.py +174 -0
  112. package/scripts/local_verification/runner.py +234 -0
  113. package/scripts/policy_lint/adapter_pairing.py +58 -3
  114. package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
  115. package/scripts/pr_verification/__init__.py +1 -0
  116. package/scripts/pr_verification/branch_refs.py +114 -0
  117. package/scripts/pr_verification/config/__init__.py +1 -0
  118. package/scripts/pr_verification/config/constants.py +84 -0
  119. package/scripts/pr_verification/config/timing.py +5 -0
  120. package/scripts/pr_verification/github.py +137 -0
  121. package/scripts/pr_verification/github_api.py +376 -0
  122. package/scripts/pr_verification/github_labels.py +88 -0
  123. package/scripts/pr_verification/github_parsing.py +182 -0
  124. package/scripts/pr_verification/github_transport.py +91 -0
  125. package/scripts/pr_verification/lock.py +122 -0
  126. package/scripts/pr_verification/model.py +43 -0
  127. package/scripts/pr_verification/requirements.txt +1 -0
  128. package/scripts/test_durable_post_lint.py +97 -0
  129. package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
  130. package/scripts/tests/test-audit-hooks.mjs +94 -2
  131. package/scripts/tests/test_automatic_advisory.py +376 -0
  132. package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
  133. package/scripts/tests/test_automatic_advisory_polling.py +216 -0
  134. package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
  135. package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
  136. package/scripts/tests/test_automatic_advisory_settings.py +48 -0
  137. package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
  138. package/scripts/tests/test_closed_pr_label.py +157 -0
  139. package/scripts/tests/test_codex_compat_materializer.py +189 -6
  140. package/scripts/tests/test_github_labels.py +101 -0
  141. package/scripts/tests/test_github_transport.py +125 -0
  142. package/scripts/tests/test_local_report_publisher.py +396 -0
  143. package/scripts/tests/test_local_report_publisher_cli.py +85 -0
  144. package/scripts/tests/test_local_verification.py +348 -0
  145. package/scripts/tests/test_local_verification_cli.py +98 -0
  146. package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
  147. package/scripts/tests/test_pr_verification_github.py +392 -0
  148. package/scripts/tests/test_pr_verification_lock.py +168 -0
  149. package/scripts/tests/test_verification_notice.py +347 -0
  150. package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
  151. package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
  152. package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
  153. package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
  154. package/hooks/blocking/destructive_command_blocker.py +0 -2073
  155. package/hooks/blocking/fable_spawn_gate.py +0 -187
  156. package/hooks/blocking/luna_fast_mode_gate.py +0 -160
  157. package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
  158. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
  159. package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
  160. package/hooks/blocking/pii_prevention_blocker.py +0 -365
  161. package/hooks/blocking/piped_pytest_blocker.py +0 -1223
  162. package/hooks/blocking/question_to_user_enforcer.py +0 -130
  163. package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
  164. package/hooks/blocking/sensitive_file_protector.py +0 -167
  165. package/hooks/blocking/session_edit_stage_gate.py +0 -684
  166. package/hooks/blocking/session_handoff_blocker.py +0 -182
  167. package/hooks/blocking/shell_substitution_blocker.py +0 -350
  168. package/hooks/blocking/test_ask_user_question_shape.py +0 -69
  169. package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
  170. package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
  171. package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
  172. package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
  173. package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
  174. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
  175. package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
  176. package/hooks/blocking/test_fable_spawn_gate.py +0 -381
  177. package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
  178. package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
  179. package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
  180. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
  181. package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
  182. package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
  183. package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
  184. package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
  185. package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
  186. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
  187. package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
  188. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
  189. package/hooks/blocking/test_sensitive_file_protector.py +0 -365
  190. package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
  191. package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
  192. package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
  193. package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
  194. package/hooks/blocking/test_session_handoff_blocker.py +0 -318
  195. package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
  196. package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
  197. package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
  198. package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
  199. package/hooks/blocking/unscoped_search_blocker.py +0 -391
  200. package/hooks/blocking/write_existing_file_blocker.py +0 -124
  201. package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
  202. package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
  203. package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
  204. package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
  205. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
  206. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
  207. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
  208. package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
  209. package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
  210. package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
@@ -1,124 +0,0 @@
1
- #!/usr/bin/env python3
2
- """PreToolUse hook — blocks Write and apply_patch "Add File" onto an existing target.
3
-
4
- Agents should use Edit for modifying existing files, and an apply_patch
5
- "Update File" section rather than "Add File" for a path that already exists.
6
- Exemptions: Jupyter notebooks (.ipynb) and files in ~/.claude/hooks/ (standalone scripts).
7
-
8
- MultiEdit carries no create-or-clobber path this hook needs to guard: every
9
- MultiEdit edit must match an ``old_string`` already present in the target file,
10
- so a MultiEdit can never introduce content nobody read the way a blind Write or
11
- an apply_patch "Add File" can. This hook allows MultiEdit unconditionally.
12
- """
13
-
14
- import json
15
- import os
16
- import sys
17
- from pathlib import Path
18
-
19
- _hooks_dir = str(Path(__file__).resolve().parent.parent)
20
- if _hooks_dir not in sys.path:
21
- sys.path.insert(0, _hooks_dir)
22
-
23
- try:
24
- from codex_apply_patch import (
25
- CODEX_ADD_OPERATION,
26
- CodexPatchError,
27
- codex_patch_operation_targets,
28
- payload_patch_command,
29
- )
30
- from hooks_constants.hook_block_logger import log_hook_block
31
- from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
32
- from hooks_constants.pre_tool_use_dispatcher_constants import APPLY_PATCH_TOOL_NAME
33
- from sensitive_file_protector import build_deny_response
34
- except ImportError as import_error:
35
- raise ImportError(
36
- "write_existing_file_blocker: cannot import its sibling modules; "
37
- "ensure the hooks directory is importable."
38
- ) from import_error
39
-
40
- JUPYTER_EXTENSION = ".ipynb"
41
- HOOKS_DIRECTORY = os.path.normpath(os.path.expanduser("~/.claude/hooks"))
42
-
43
-
44
- def is_jupyter_notebook(file_path: str) -> bool:
45
- return file_path.lower().endswith(JUPYTER_EXTENSION)
46
-
47
-
48
- def is_inside_hooks_directory(file_path: str) -> bool:
49
- normalized_path = os.path.normpath(file_path)
50
- return normalized_path.startswith(HOOKS_DIRECTORY)
51
-
52
-
53
- def _apply_patch_add_deny(payload: dict, tool_input: dict) -> tuple[str, str] | None:
54
- """Return (deny_reason, offending_path) for an apply_patch 'Add File' onto an existing path."""
55
- command, working_directory = payload_patch_command(payload, tool_input)
56
- if not command:
57
- return None
58
- try:
59
- all_operation_targets = codex_patch_operation_targets(command, working_directory)
60
- except CodexPatchError:
61
- return None
62
- for each_operation, each_target_path in all_operation_targets:
63
- if each_operation != CODEX_ADD_OPERATION:
64
- continue
65
- if os.path.exists(each_target_path):
66
- deny_reason = (
67
- f"BLOCKED: apply_patch 'Add File' on existing file {each_target_path}. "
68
- "Use an 'Update File' section instead."
69
- )
70
- return deny_reason, each_target_path
71
- return None
72
-
73
-
74
- def _write_onto_existing_file_deny(tool_input: dict) -> tuple[str, str] | None:
75
- """Return (deny_reason, offending_path) for a Write onto an existing target."""
76
- target_file_path = tool_input.get("file_path", "")
77
- if not target_file_path:
78
- return None
79
- if is_jupyter_notebook(target_file_path):
80
- return None
81
- if is_inside_hooks_directory(target_file_path):
82
- return None
83
- if not os.path.exists(target_file_path):
84
- return None
85
- deny_reason = f"BLOCKED: Write on existing file {target_file_path}. Use Edit tool instead."
86
- return deny_reason, target_file_path
87
-
88
-
89
- def _resolve_deny(tool_name: str, payload: dict, tool_input: dict) -> tuple[str, str] | None:
90
- """Return (deny_reason, offending_path) for the payload, or None to allow."""
91
- if tool_name == APPLY_PATCH_TOOL_NAME:
92
- return _apply_patch_add_deny(payload, tool_input)
93
- if tool_name != "Write":
94
- return None
95
- return _write_onto_existing_file_deny(tool_input)
96
-
97
-
98
- def main() -> None:
99
- input_payload = read_hook_input_dictionary_from_stdin()
100
- if input_payload is None:
101
- sys.exit(0)
102
-
103
- raw_tool_name = input_payload.get("tool_name", "")
104
- raw_tool_input = input_payload.get("tool_input", {})
105
- tool_name = raw_tool_name if isinstance(raw_tool_name, str) else ""
106
- tool_input = raw_tool_input if isinstance(raw_tool_input, dict) else {}
107
-
108
- resolved_deny = _resolve_deny(tool_name, input_payload, tool_input)
109
- if resolved_deny is None:
110
- sys.exit(0)
111
- deny_reason, offending_path = resolved_deny
112
-
113
- log_hook_block(
114
- calling_hook_name="write_existing_file_blocker.py",
115
- hook_event="PreToolUse",
116
- block_reason=deny_reason,
117
- offending_input_preview=offending_path,
118
- )
119
- print(json.dumps(build_deny_response(deny_reason)))
120
- sys.exit(0)
121
-
122
-
123
- if __name__ == "__main__":
124
- main()
@@ -1,19 +0,0 @@
1
- """Configuration constants for the bot_mention_comment_blocker PreToolUse hook."""
2
-
3
- TOOL_NAME: str = "mcp__plugin_github_github__add_issue_comment"
4
-
5
- CURSOR_MENTION_TOKEN: str = "@cursor"
6
- COPILOT_MENTION_TOKEN: str = "@copilot"
7
-
8
- CORRECTIVE_MESSAGE_CURSOR: str = (
9
- "BLOCKED [bot-mention]: Invalid comment. "
10
- "Post exactly ``bugbot run`` with no other text as your issue comment "
11
- "to trigger Bugbot."
12
- )
13
-
14
- CORRECTIVE_MESSAGE_COPILOT: str = (
15
- "BLOCKED [bot-mention]: Invalid comment. "
16
- "To request a Copilot review, use the GitHub REST API:\n"
17
- " gh api --method POST repos/<owner>/<repo>/pulls/<number>/requested_reviewers \\\n"
18
- " -f 'reviewers[]=copilot-pull-request-reviewer[bot]'"
19
- )
@@ -1,79 +0,0 @@
1
- """Constants for the Cursor-vs-Python gate misfire PreToolUse blocker.
2
-
3
- Blocks Shell commands that launch Cursor's CLI or GUI binary against a Python
4
- script with code-rules-gate flags. That path feeds unknown options into
5
- Cursor's main process ``resolveArgs``, where ``console.warn`` on a closed pipe
6
- raises an EPIPE error dialog and still opens the script as an editor path.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import re
12
-
13
- __all__ = [
14
- "ALL_SUPPORTED_TOOL_NAMES",
15
- "BASH_TOOL_NAME",
16
- "POWERSHELL_TOOL_NAME",
17
- "TOOL_NAME_KEY",
18
- "TOOL_INPUT_KEY",
19
- "COMMAND_KEY",
20
- "HOOK_SPECIFIC_OUTPUT_KEY",
21
- "HOOK_EVENT_NAME_KEY",
22
- "HOOK_EVENT_NAME",
23
- "PERMISSION_DECISION_KEY",
24
- "DENY_DECISION",
25
- "PERMISSION_DECISION_REASON_KEY",
26
- "CALLING_HOOK_NAME",
27
- "CURSOR_LAUNCH_PATTERN",
28
- "GATE_SCRIPT_PATTERN",
29
- "GATE_FLAG_PATTERN",
30
- "PYTHON_PATH_PATTERN",
31
- "CORRECTIVE_MESSAGE",
32
- ]
33
-
34
- BASH_TOOL_NAME = "Bash"
35
- POWERSHELL_TOOL_NAME = "PowerShell"
36
- ALL_SUPPORTED_TOOL_NAMES: frozenset[str] = frozenset(
37
- {BASH_TOOL_NAME, POWERSHELL_TOOL_NAME}
38
- )
39
-
40
- TOOL_NAME_KEY = "tool_name"
41
- TOOL_INPUT_KEY = "tool_input"
42
- COMMAND_KEY = "command"
43
-
44
- HOOK_SPECIFIC_OUTPUT_KEY = "hookSpecificOutput"
45
- HOOK_EVENT_NAME_KEY = "hookEventName"
46
- HOOK_EVENT_NAME = "PreToolUse"
47
- PERMISSION_DECISION_KEY = "permissionDecision"
48
- DENY_DECISION = "deny"
49
- PERMISSION_DECISION_REASON_KEY = "permissionDecisionReason"
50
-
51
- CALLING_HOOK_NAME = "cursor_cli_python_misfire_blocker.py"
52
-
53
- CURSOR_LAUNCH_PATTERN = re.compile(
54
- r"(?i)"
55
- r"(?:"
56
- r"(?:^|[\s;&|])(?:cursor(?:\.cmd|\.exe)?)(?=\s|[\"']|$)"
57
- r"|"
58
- r"[\\/](?:cursor(?:\.cmd|\.exe)?)(?=\s|[\"']|$)"
59
- r")"
60
- )
61
- GATE_SCRIPT_PATTERN = re.compile(r"(?i)code_rules_gate\.py\b")
62
- GATE_FLAG_PATTERN = re.compile(
63
- r"(?i)(?:^|[\s,\"'])(?:--base|--staged|--repo-root|--only-under)"
64
- r"(?:\s|=|$|[\"'])"
65
- )
66
- PYTHON_PATH_PATTERN = re.compile(r"(?i)\.py(?:\s|$|['\":])")
67
-
68
- CORRECTIVE_MESSAGE = (
69
- "BLOCKED [cursor-python-misfire]: Cursor's CLI/GUI was invoked against a "
70
- "Python script with code-rules-gate flags. Cursor treats those flags as "
71
- "unknown options, warns on a closed pipe, and raises an EPIPE error dialog "
72
- "while still opening the script as an editor path.\n\n"
73
- "To run the gate:\n"
74
- " python <path-to>/code_rules_gate.py --base origin/main\n\n"
75
- "To open a file on screen in its native Windows app:\n"
76
- " Invoke-Item -LiteralPath '<path>'\n\n"
77
- "Do not pass --base, --staged, --repo-root, or --only-under to cursor / "
78
- "Cursor.exe."
79
- )
@@ -1,61 +0,0 @@
1
- """Constants for the fable-tier subagent spawn gate.
2
-
3
- The gate denies an ``Agent`` or ``Task`` spawn at the fable tier whose
4
- ``prompt`` lacks ``FABLE_SPAWN_AUTHORIZATION_MARKER``. ``DENY_REASON`` points
5
- at the protocol document rather than quoting the token, so pasting a denial
6
- into a retry prompt authorizes nothing.
7
- ``MODEL_SEGMENT_SPLIT_PATTERN`` finds that tier in a full model id too::
8
-
9
- fable -> ['fable'] flag: fable tier
10
- claude-fable-5 -> ['claude', 'fable', '5'] flag: fable tier
11
- claude-sonnet-4 -> ['claude', 'sonnet', '4'] ok: another tier
12
-
13
- Every literal the gate body reads lives here. ``AGENT_TOOL_NAME`` and
14
- ``TASK_TOOL_NAME`` name the spawn tools the gate evaluates.
15
- """
16
-
17
- from __future__ import annotations
18
-
19
- import re
20
-
21
- AGENT_TOOL_NAME: str = "Agent"
22
- TASK_TOOL_NAME: str = "Task"
23
-
24
- ALL_SPAWN_TOOL_NAMES: frozenset[str] = frozenset({AGENT_TOOL_NAME, TASK_TOOL_NAME})
25
-
26
- FABLE_MODEL_ALIAS: str = "fable"
27
- MODEL_SEGMENT_SPLIT_PATTERN: re.Pattern[str] = re.compile(r"[^a-z0-9]+")
28
- FABLE_SPAWN_AUTHORIZATION_MARKER: str = "FABLE-SPAWN-AUTHORIZED"
29
- ADVISOR_PROTOCOL_DOCUMENT_PATH: str = "_shared/advisor/advisor-protocol.md"
30
-
31
- TOOL_NAME_FIELD_NAME: str = "tool_name"
32
- TOOL_INPUT_FIELD_NAME: str = "tool_input"
33
- MODEL_FIELD_NAME: str = "model"
34
- PROMPT_FIELD_NAME: str = "prompt"
35
-
36
- CALLING_HOOK_NAME: str = "fable_spawn_gate.py"
37
- HOOK_EVENT_NAME: str = "PreToolUse"
38
-
39
- DENY_REASON: str = (
40
- "BLOCKED [fable-spawn-gate]: this subagent spawn names the fable tier in "
41
- "its model field and carries no authorization marker. To authorize a "
42
- f"fable bind, put the token named in {ADVISOR_PROTOCOL_DOCUMENT_PATH} as "
43
- "plain text in the spawn prompt (substring match). Otherwise set model "
44
- "to opus, sonnet, or haiku. The gate checks only model tier and marker "
45
- "presence — it does not verify which session placed the token."
46
- )
47
-
48
- DENY_ADDITIONAL_CONTEXT: str = (
49
- "[fable-spawn-gate] This Agent/Task spawn was denied: the model field "
50
- "names the fable tier and the spawn prompt carries no authorization "
51
- "marker. Preferred recovery: re-spawn at model opus, or an "
52
- "opus-equivalent tier (sonnet/haiku when opus is unavailable) — those "
53
- "tiers need no marker. Authorized fable bind only: put the token named "
54
- f"in {ADVISOR_PROTOCOL_DOCUMENT_PATH} as plain text in the spawn prompt "
55
- "(substring match), then retry. The gate checks only model tier and "
56
- "marker presence. Do not paste this denial into a retry prompt — it "
57
- "does not authorize the spawn."
58
- )
59
-
60
- DENY_PREVIEW_TEMPLATE: str = "model={model_text} marker_present=False"
61
- MAXIMUM_PREVIEW_MODEL_LENGTH: int = 40
@@ -1,45 +0,0 @@
1
- """Constants for the Codex Luna fast-mode spawn gate.
2
-
3
- The gate checks the model and service tier fields on Agent, Task, and native
4
- Codex multi-agent spawn calls. Agent and Task require exact ``fast``. Native
5
- Codex accepts exact ``fast`` or ``priority`` for Luna models.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- import re
11
-
12
- AGENT_TOOL_NAME: str = "Agent"
13
- TASK_TOOL_NAME: str = "Task"
14
- CODEX_AGENT_TOOL_NAME: str = "multi_agent_v1__spawn_agent"
15
- ALL_SPAWN_TOOL_NAMES: frozenset[str] = frozenset(
16
- {AGENT_TOOL_NAME, TASK_TOOL_NAME, CODEX_AGENT_TOOL_NAME}
17
- )
18
-
19
- LUNA_MODEL_ALIAS: str = "luna"
20
- FAST_SERVICE_TIER: str = "fast"
21
- PRIORITY_SERVICE_TIER: str = "priority"
22
- MODEL_SEGMENT_SPLIT_PATTERN: re.Pattern[str] = re.compile(r"[^a-z0-9]+")
23
-
24
- TOOL_NAME_FIELD_NAME: str = "tool_name"
25
- TOOL_INPUT_FIELD_NAME: str = "tool_input"
26
- MODEL_FIELD_NAME: str = "model"
27
- SERVICE_TIER_FIELD_NAME: str = "service_tier"
28
-
29
- CALLING_HOOK_NAME: str = "luna_fast_mode_gate.py"
30
- HOOK_EVENT_NAME: str = "PreToolUse"
31
-
32
- DENY_REASON: str = (
33
- "BLOCKED [luna-fast-mode-gate]: Agent and Task Luna spawns require "
34
- "service_tier fast. Native Codex Luna spawns require service_tier fast "
35
- "or priority. Retry with an allowed service_tier."
36
- )
37
-
38
- DENY_ADDITIONAL_CONTEXT: str = (
39
- "[luna-fast-mode-gate] Agent and Task Luna spawns require service_tier "
40
- "fast. Native Codex Luna spawns accept service_tier fast or priority. "
41
- "Retry with an allowed service_tier."
42
- )
43
-
44
- DENY_PREVIEW_TEMPLATE: str = "model={model_text} service_tier={service_tier_text}"
45
- MAXIMUM_PREVIEW_FIELD_LENGTH: int = 40
@@ -1,20 +0,0 @@
1
- """Constants for the NAS ssh binary enforcer hook.
2
-
3
- Holds the Bash tool name, the ssh-family command basenames, the Windows OpenSSH
4
- binary path suffixes, and the batch-mode pattern. Segment splitting and
5
- leading-program resolution come from ``shell_command_segments``.
6
- """
7
-
8
- import re
9
-
10
- BASH_TOOL_NAME = "Bash"
11
-
12
- ALL_SSH_FAMILY_COMMAND_BASENAMES = frozenset(
13
- {"ssh", "scp", "sftp", "ssh.exe", "scp.exe", "sftp.exe"}
14
- )
15
- ALL_OPENSSH_BINARY_PATH_SUFFIXES = (
16
- "/openssh/ssh.exe",
17
- "/openssh/scp.exe",
18
- "/openssh/sftp.exe",
19
- )
20
- BATCH_MODE_PATTERN = re.compile(r"batchmode\s*=\s*yes", re.IGNORECASE)
@@ -1,57 +0,0 @@
1
- """Constants for the orchestrator-refresh ScheduleWakeup reschedule gate."""
2
-
3
- from __future__ import annotations
4
-
5
- SCHEDULE_WAKEUP_TOOL_NAME = "ScheduleWakeup"
6
- CRON_CREATE_TOOL_NAME = "CronCreate"
7
- ALL_SCHEDULE_TOOL_NAMES: frozenset[str] = frozenset(
8
- {
9
- SCHEDULE_WAKEUP_TOOL_NAME,
10
- CRON_CREATE_TOOL_NAME,
11
- }
12
- )
13
-
14
- ORCHESTRATOR_REFRESH_PROMPT_TOKEN = "/orchestrator-refresh"
15
- RUN_SLUG_PROMPT_FLAG = "--run-slug"
16
- RUN_SLUG_PROMPT_VALUE_PATTERN = r"(?:=\s*|\s+)([^\s]+)"
17
- PROMPT_FIELD_NAME = "prompt"
18
- TOOL_NAME_FIELD_NAME = "tool_name"
19
- TOOL_INPUT_FIELD_NAME = "tool_input"
20
- CWD_FIELD_NAME = "cwd"
21
-
22
- REASON_CRON_CREATE_FORBIDDEN = "cron_create_forbidden"
23
-
24
- DENY_REASON_TEMPLATE = (
25
- "BLOCKED: orchestrator-refresh re-arm denied ({reason}). "
26
- "Use one-shot delayed wake only (never CronCreate / never recurring). "
27
- "At most one re-arm slot: begin-firing at refresh start; "
28
- "should-reschedule then create then claim-rearm (never claim before "
29
- "ScheduleWakeup). Status must be active: "
30
- "python skills/orchestrator/scripts/status_gate.py set "
31
- "--status active. When finished: --status done. Never re-arm an idle loop."
32
- )
33
-
34
- CALLING_HOOK_NAME = "orchestrator_refresh_reschedule_gate.py"
35
- HOOK_EVENT_NAME = "PreToolUse"
36
-
37
- ALL_PACKAGE_AGENTS_SKILL_SCRIPTS_RELATIVE_PARTS: tuple[str, ...] = (
38
- ".agents",
39
- "skills",
40
- "orchestrator",
41
- "scripts",
42
- )
43
- """Package-source path of orchestrator status_gate.py under the agents home."""
44
-
45
- ALL_SKILL_SCRIPTS_RELATIVE_PARTS: tuple[str, ...] = (
46
- "skills",
47
- "orchestrator",
48
- "scripts",
49
- )
50
- """Package-root path of orchestrator status_gate.py for a dependency layout."""
51
- PLUGIN_ROOT_ENV_VAR = "CLAUDE_PLUGIN_ROOT"
52
- ALL_HOME_SKILLS_RELATIVE_PARTS: tuple[str, ...] = (
53
- ".claude",
54
- "skills",
55
- "orchestrator",
56
- "scripts",
57
- )
@@ -1,17 +0,0 @@
1
- """Configuration constants for the send_user_file_open_locally_blocker PreToolUse hook."""
2
-
3
- TOOL_NAME: str = "SendUserFile"
4
-
5
- PROACTIVE_STATUS: str = "proactive"
6
-
7
- CORRECTIVE_MESSAGE: str = (
8
- "BLOCKED [open-locally]: SendUserFile attaches a file to the session, which "
9
- "does not let the user see it while they are at the terminal. Open the file on "
10
- "screen with its native Windows app:\n"
11
- " Invoke-Item -LiteralPath '<path>'\n"
12
- "Open every path the user named. Do not launch Cursor.exe or cursor.cmd to open "
13
- "a file — unknown flags on that path raise an EPIPE error dialog in Cursor's "
14
- "main process.\n"
15
- "The one allowed attach is a phone push: when the user has stepped away and you "
16
- 'want the file to reach their phone, call SendUserFile with status "proactive".'
17
- )
@@ -1,40 +0,0 @@
1
- """Constants for the ``sensitive_file_protector`` PreToolUse hook.
2
-
3
- The hook denies a Write, Edit, or MultiEdit whose target basename names a live
4
- secret or a lock file, and steps aside for a placeholders-only committed template.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- ALL_SENSITIVE_PATTERNS = (
10
- ".env",
11
- ".env.*",
12
- "*.env",
13
- "*.pem",
14
- "*.key",
15
- "*.p12",
16
- "*.pfx",
17
- "credentials.json",
18
- "secrets.json",
19
- "id_rsa",
20
- "id_ed25519",
21
- "package-lock.json",
22
- "yarn.lock",
23
- "Pipfile.lock",
24
- "poetry.lock",
25
- "pnpm-lock.yaml",
26
- "composer.lock",
27
- )
28
-
29
- ALL_TEMPLATE_SUFFIXES = (".example", ".sample", ".template")
30
-
31
- HOOK_SCRIPT_NAME = "sensitive_file_protector.py"
32
-
33
- HOOK_EVENT_NAME = "PreToolUse"
34
-
35
- DENY_DECISION = "deny"
36
-
37
- DENY_REASON_TEMPLATE = (
38
- "BLOCKED: Sensitive file '{filename}' (pattern: '{matched_pattern}'). "
39
- "Edit manually outside Claude Code."
40
- )
@@ -1,10 +0,0 @@
1
- """Shared compiled patterns for the session_handoff_blocker hook."""
2
-
3
- import re
4
-
5
- FIRST_PERSON_SUBJECT_PATTERN = re.compile(
6
- r"\b(?:i['’]?m|i['’]?ll|i\s+will|i\s+am|i\s+need\s+to|i\s+should"
7
- r"|i\s+recommend|i\s+suggest|let\s+me|let['’]?s"
8
- r"|we\s+(?:should|can|could)|we['’]?ll|we\s+are|we\s+will)\b",
9
- re.IGNORECASE,
10
- )
@@ -1,80 +0,0 @@
1
- """Constants for the shell-substitution PreToolUse Bash blocker.
2
-
3
- Holds the tool name, the payload keys, the four detection patterns, the deny
4
- decision shape, and the corrective message. The hook imports these so no
5
- single-use constant sits at its file scope.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- import re
11
-
12
- __all__ = [
13
- "BASH_TOOL_NAME",
14
- "TOOL_NAME_KEY",
15
- "TOOL_INPUT_KEY",
16
- "COMMAND_KEY",
17
- "HOOK_SPECIFIC_OUTPUT_KEY",
18
- "HOOK_EVENT_NAME_KEY",
19
- "HOOK_EVENT_NAME",
20
- "PERMISSION_DECISION_KEY",
21
- "DENY_DECISION",
22
- "PERMISSION_DECISION_REASON_KEY",
23
- "DOLLAR_PAREN_PATTERN",
24
- "EVEN_BACKSLASH_BACKTICK_PATTERN",
25
- "PROCESS_SUBSTITUTION_PATTERN",
26
- "SINGLE_QUOTED_RUN_PATTERN",
27
- "STRIPPED_RUN_REPLACEMENT",
28
- "COMMAND_LINE_SEPARATOR",
29
- "BACKSLASH_CHARACTER",
30
- "SINGLE_QUOTE_CHARACTER",
31
- "DOUBLE_QUOTE_CHARACTER",
32
- "NO_TERMINATOR_INDEX",
33
- "CORRECTIVE_MESSAGE",
34
- ]
35
-
36
- BASH_TOOL_NAME = "Bash"
37
-
38
- TOOL_NAME_KEY = "tool_name"
39
- TOOL_INPUT_KEY = "tool_input"
40
- COMMAND_KEY = "command"
41
-
42
- HOOK_SPECIFIC_OUTPUT_KEY = "hookSpecificOutput"
43
- HOOK_EVENT_NAME_KEY = "hookEventName"
44
- HOOK_EVENT_NAME = "PreToolUse"
45
- PERMISSION_DECISION_KEY = "permissionDecision"
46
- DENY_DECISION = "deny"
47
- PERMISSION_DECISION_REASON_KEY = "permissionDecisionReason"
48
-
49
- DOLLAR_PAREN_PATTERN = re.compile(r"\$\(")
50
- OPEN_PAREN_CHARACTER = "("
51
- CLOSE_PAREN_CHARACTER = ")"
52
- PAREN_DEPTH_AFTER_DOUBLE_OPEN = 2
53
- EVEN_BACKSLASH_BACKTICK_PATTERN = re.compile(r"(?<!\\)(?:\\\\)*`")
54
- PROCESS_SUBSTITUTION_PATTERN = re.compile(r"[<>]\(")
55
- SINGLE_QUOTED_RUN_PATTERN = re.compile(r"'[^']*'")
56
- STRIPPED_RUN_REPLACEMENT = ""
57
- COMMAND_LINE_SEPARATOR = "\n"
58
- BACKSLASH_CHARACTER = "\\"
59
- SINGLE_QUOTE_CHARACTER = "'"
60
- DOUBLE_QUOTE_CHARACTER = '"'
61
- NO_TERMINATOR_INDEX = -1
62
-
63
- CORRECTIVE_MESSAGE = (
64
- "BLOCKED [shell-substitution]: command contains $(...), backtick, or "
65
- "<(...)/>(...) process substitution, which forces a permission prompt "
66
- "because the Claude Code allowlist matcher does not descend into "
67
- "substitutions.\n\n"
68
- "Fix: split into two Bash tool calls, or rewrite without substitution.\n"
69
- ' cd X && echo "$(git rev-parse HEAD)"\n'
70
- "becomes either two calls:\n"
71
- " 1) cd X\n"
72
- " 2) git rev-parse HEAD\n"
73
- "or one substitution-free call:\n"
74
- " git -C X rev-parse HEAD\n\n"
75
- "Process substitution example:\n"
76
- " diff <(cat a) <(cat b)\n"
77
- "becomes two separate Bash calls to `cat a` and `cat b`, with the diff "
78
- "performed on the captured outputs.\n\n"
79
- "See `shell-invocation.md` in the rules directory for full guidance."
80
- )