claude-dev-env 1.0.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 (215) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +219 -0
  3. package/agents/agent-writer.md +157 -0
  4. package/agents/clasp-deployment-orchestrator.md +609 -0
  5. package/agents/clean-coder.md +295 -0
  6. package/agents/code-quality-agent.md +40 -0
  7. package/agents/code-standards-agent.md +93 -0
  8. package/agents/config-centralizer.md +686 -0
  9. package/agents/config-extraction-agent.md +225 -0
  10. package/agents/doc-orchestrator.md +47 -0
  11. package/agents/docs-agent.md +112 -0
  12. package/agents/docx-agent.md +211 -0
  13. package/agents/git-commit-crafter.md +100 -0
  14. package/agents/magic-value-eliminator-agent.md +72 -0
  15. package/agents/mandatory-agent-workflow-agent.md +88 -0
  16. package/agents/parallel-workflow-coordinator.md +779 -0
  17. package/agents/pdf-agent.md +302 -0
  18. package/agents/plan-executor.md +226 -0
  19. package/agents/pr-description-writer.md +87 -0
  20. package/agents/project-context-loader.md +238 -0
  21. package/agents/project-docs-analyzer.md +54 -0
  22. package/agents/project-structure-organizer-agent.md +72 -0
  23. package/agents/readability-review-agent.md +76 -0
  24. package/agents/refactoring-specialist.md +69 -0
  25. package/agents/right-sized-engineer.md +129 -0
  26. package/agents/session-continuity-manager.md +53 -0
  27. package/agents/skill-to-agent-converter.md +371 -0
  28. package/agents/skill-writer-agent.md +470 -0
  29. package/agents/stub-detector-agent.md +140 -0
  30. package/agents/tdd-test-writer.md +62 -0
  31. package/agents/test-data-builder.md +68 -0
  32. package/agents/tooling-builder.md +78 -0
  33. package/agents/user-docs-writer.md +67 -0
  34. package/agents/validation-expert.md +71 -0
  35. package/agents/workflow-visual-documenter.md +82 -0
  36. package/agents/xlsx-agent.md +169 -0
  37. package/bin/install.mjs +256 -0
  38. package/commands/commit.md +28 -0
  39. package/commands/docupdate.md +322 -0
  40. package/commands/implement.md +102 -0
  41. package/commands/initialize.md +91 -0
  42. package/commands/plan.md +63 -0
  43. package/commands/pr-comments.md +47 -0
  44. package/commands/readability-review.md +20 -0
  45. package/commands/review-plan.md +7 -0
  46. package/commands/right-size.md +15 -0
  47. package/commands/stubcheck.md +89 -0
  48. package/commands/sum.md +30 -0
  49. package/docs/CODE_RULES.md +186 -0
  50. package/docs/DJANGO_PATTERNS.md +80 -0
  51. package/docs/REACT_PATTERNS.md +185 -0
  52. package/docs/TEST_QUALITY.md +104 -0
  53. package/hooks/advisory/migration-safety-advisor.py +49 -0
  54. package/hooks/advisory/refactor-guard.py +205 -0
  55. package/hooks/blocking/block-main-commit.py +168 -0
  56. package/hooks/blocking/code-rules-enforcer.py +549 -0
  57. package/hooks/blocking/destructive-command-blocker.py +107 -0
  58. package/hooks/blocking/docker-settings-guard.py +44 -0
  59. package/hooks/blocking/hedging-language-blocker.py +130 -0
  60. package/hooks/blocking/parallel-task-blocker.py +69 -0
  61. package/hooks/blocking/pr-description-enforcer.py +87 -0
  62. package/hooks/blocking/pyautogui-scroll-blocker.py +74 -0
  63. package/hooks/blocking/sensitive-file-protector.py +70 -0
  64. package/hooks/blocking/tdd-enforcer.py +62 -0
  65. package/hooks/blocking/test-preflight-check.py +343 -0
  66. package/hooks/blocking/write-existing-file-blocker.py +63 -0
  67. package/hooks/git-hooks/post-commit.py +103 -0
  68. package/hooks/github-action/test_workflow.py +33 -0
  69. package/hooks/hooks.json +246 -0
  70. package/hooks/lifecycle/config-change-guard.py +84 -0
  71. package/hooks/lifecycle/session-end-cleanup.py +59 -0
  72. package/hooks/notification/attention-needed-notify.py +63 -0
  73. package/hooks/notification/claude-notification-handler.py +59 -0
  74. package/hooks/notification/notification_utils.py +206 -0
  75. package/hooks/rewrite-plugin-paths.py +116 -0
  76. package/hooks/session/bulk-edit-reminder.py +30 -0
  77. package/hooks/session/code-rules-reminder.py +97 -0
  78. package/hooks/session/compact-context-reinject.py +39 -0
  79. package/hooks/session/hook-structure-context.py +140 -0
  80. package/hooks/session/plugin-data-dir-cleanup.py +39 -0
  81. package/hooks/validation/code-style-validator.py +145 -0
  82. package/hooks/validation/e2e-test-validator.py +142 -0
  83. package/hooks/validation/hook-format-validator.py +66 -0
  84. package/hooks/validation/mypy_validator.py +180 -0
  85. package/hooks/validators/README.md +125 -0
  86. package/hooks/validators/VALIDATION_REPORT.md +287 -0
  87. package/hooks/validators/__init__.py +19 -0
  88. package/hooks/validators/abbreviation_checks.py +82 -0
  89. package/hooks/validators/code_quality_checks.py +133 -0
  90. package/hooks/validators/comment_checks.py +188 -0
  91. package/hooks/validators/file_structure_checks.py +182 -0
  92. package/hooks/validators/git_checks.py +107 -0
  93. package/hooks/validators/health_check.py +214 -0
  94. package/hooks/validators/magic_value_checks.py +81 -0
  95. package/hooks/validators/mypy_integration.py +52 -0
  96. package/hooks/validators/output_formatter.py +266 -0
  97. package/hooks/validators/pr_reference_checks.py +72 -0
  98. package/hooks/validators/python_antipattern_checks.py +110 -0
  99. package/hooks/validators/python_style_checks.py +364 -0
  100. package/hooks/validators/react_checks.py +90 -0
  101. package/hooks/validators/ruff_integration.py +80 -0
  102. package/hooks/validators/run_all_validators.py +772 -0
  103. package/hooks/validators/security_checks.py +135 -0
  104. package/hooks/validators/test_abbreviation_checks.py +76 -0
  105. package/hooks/validators/test_bad.tsx +7 -0
  106. package/hooks/validators/test_code_quality_checks.py +129 -0
  107. package/hooks/validators/test_file_structure_checks.py +307 -0
  108. package/hooks/validators/test_files/01_basic_component.tsx +10 -0
  109. package/hooks/validators/test_files/02_component_without_react.tsx +10 -0
  110. package/hooks/validators/test_files/03_pure_component.tsx +10 -0
  111. package/hooks/validators/test_files/04_pure_component_import.tsx +10 -0
  112. package/hooks/validators/test_files/05_typescript_generics.tsx +14 -0
  113. package/hooks/validators/test_files/06_typescript_two_generics.tsx +18 -0
  114. package/hooks/validators/test_files/07_multiline_declaration.tsx +11 -0
  115. package/hooks/validators/test_files/08_error_boundary_valid.tsx +14 -0
  116. package/hooks/validators/test_files/09_error_boundary_with_other_class.tsx +20 -0
  117. package/hooks/validators/test_files/10_inheritance_chain.tsx +16 -0
  118. package/hooks/validators/test_files/11_ts_file.ts +10 -0
  119. package/hooks/validators/test_files/12_non_react_class.tsx +14 -0
  120. package/hooks/validators/test_files/13_functional_component.tsx +8 -0
  121. package/hooks/validators/test_files/14_indented_class.tsx +13 -0
  122. package/hooks/validators/test_files/15_getDerivedStateFromError.tsx +14 -0
  123. package/hooks/validators/test_files/16_mixed_components.tsx +20 -0
  124. package/hooks/validators/test_files/EXECUTIVE_SUMMARY.md +175 -0
  125. package/hooks/validators/test_files/TEST_RESULTS_TABLE.txt +60 -0
  126. package/hooks/validators/test_files/VALIDATION_REPORT.md +201 -0
  127. package/hooks/validators/test_files/async_views.py +23 -0
  128. package/hooks/validators/test_files/async_with_imports.py +14 -0
  129. package/hooks/validators/test_files/bad_inline_imports.py +37 -0
  130. package/hooks/validators/test_files/management/commands/cmd_01_no_debug_check.py +10 -0
  131. package/hooks/validators/test_files/management/commands/cmd_02_proper_debug_check.py +14 -0
  132. package/hooks/validators/test_files/management/commands/cmd_03_debug_check_with_return.py +14 -0
  133. package/hooks/validators/test_files/management/commands/cmd_04_imported_DEBUG.py +14 -0
  134. package/hooks/validators/test_files/management/commands/cmd_05_debug_check_in_helper.py +16 -0
  135. package/hooks/validators/test_files/management/commands/cmd_06_debug_check_late.py +22 -0
  136. package/hooks/validators/test_files/management/commands/cmd_07_positive_debug_check.py +15 -0
  137. package/hooks/validators/test_files/management/commands/cmd_08_debug_with_and.py +14 -0
  138. package/hooks/validators/test_files/not_management_command.py +10 -0
  139. package/hooks/validators/test_files/skip_decorators/test_01_simple_skip.py +8 -0
  140. package/hooks/validators/test_files/skip_decorators/test_02_pytest_skipif.py +8 -0
  141. package/hooks/validators/test_files/skip_decorators/test_03_unittest_skipIf.py +8 -0
  142. package/hooks/validators/test_files/skip_decorators/test_04_skip_with_parens.py +8 -0
  143. package/hooks/validators/test_files/skip_decorators/test_05_xfail.py +7 -0
  144. package/hooks/validators/test_files/skip_decorators/test_06_custom_skip.py +11 -0
  145. package/hooks/validators/test_files/skip_decorators/test_07_capital_Skip.py +8 -0
  146. package/hooks/validators/test_files/skip_decorators/test_08_skipUnless.py +7 -0
  147. package/hooks/validators/test_files/skip_decorators/test_09_pytest_mark_skip_simple.py +7 -0
  148. package/hooks/validators/test_files/test_async_functions.py +45 -0
  149. package/hooks/validators/test_files/test_purecomponent/PureComponentExample.tsx +7 -0
  150. package/hooks/validators/test_files/test_purecomponent/ReactPureComponentExample.tsx +7 -0
  151. package/hooks/validators/test_git_checks.py +295 -0
  152. package/hooks/validators/test_good.tsx +5 -0
  153. package/hooks/validators/test_health_check.py +57 -0
  154. package/hooks/validators/test_magic_value_checks.py +63 -0
  155. package/hooks/validators/test_mypy_integration.py +27 -0
  156. package/hooks/validators/test_output_formatter.py +150 -0
  157. package/hooks/validators/test_pr_reference_checks.py +41 -0
  158. package/hooks/validators/test_python_antipattern_checks.py +113 -0
  159. package/hooks/validators/test_python_style_checks.py +439 -0
  160. package/hooks/validators/test_react_checks.py +213 -0
  161. package/hooks/validators/test_results.txt +25 -0
  162. package/hooks/validators/test_ruff_integration.py +27 -0
  163. package/hooks/validators/test_run_all_validators.py +228 -0
  164. package/hooks/validators/test_run_all_validators_integration.py +48 -0
  165. package/hooks/validators/test_safety_checks.py +243 -0
  166. package/hooks/validators/test_security_checks.py +105 -0
  167. package/hooks/validators/test_test_safety_checks.py +321 -0
  168. package/hooks/validators/test_todo_checks.py +39 -0
  169. package/hooks/validators/test_type_safety_checks.py +85 -0
  170. package/hooks/validators/test_useless_test_checks.py +55 -0
  171. package/hooks/validators/test_validator_base.py +26 -0
  172. package/hooks/validators/test_verify_paths.py +34 -0
  173. package/hooks/validators/todo_checks.py +59 -0
  174. package/hooks/validators/type_safety_checks.py +101 -0
  175. package/hooks/validators/useless_test_checks.py +92 -0
  176. package/hooks/validators/validator_base.py +19 -0
  177. package/hooks/validators/verify_paths.py +57 -0
  178. package/hooks/workflow/auto-formatter.py +114 -0
  179. package/hooks/workflow/investigation-tracker-reset.py +46 -0
  180. package/package.json +30 -0
  181. package/rules/agent-spawn-protocol.md +47 -0
  182. package/rules/cleanup-temp-files.md +27 -0
  183. package/rules/code-reviews.md +11 -0
  184. package/rules/code-standards.md +43 -0
  185. package/rules/conservative-action.md +20 -0
  186. package/rules/context7.md +12 -0
  187. package/rules/explore-thoroughly.md +27 -0
  188. package/rules/git-workflow.md +42 -0
  189. package/rules/parallel-tools.md +23 -0
  190. package/rules/research-mode.md +23 -0
  191. package/rules/right-sized-engineering.md +28 -0
  192. package/rules/tdd.md +7 -0
  193. package/rules/testing.md +12 -0
  194. package/skills/agent-prompt/SKILL.md +102 -0
  195. package/skills/anthropic-plan/SKILL.md +107 -0
  196. package/skills/everything-search/SKILL.md +144 -0
  197. package/skills/ingest/SKILL.md +40 -0
  198. package/skills/npm-creator/SKILL.md +183 -0
  199. package/skills/pr-review-responder/EXAMPLES.md +590 -0
  200. package/skills/pr-review-responder/PRINCIPLES.md +539 -0
  201. package/skills/pr-review-responder/README.md +209 -0
  202. package/skills/pr-review-responder/SKILL.md +202 -0
  203. package/skills/pr-review-responder/TESTING.md +407 -0
  204. package/skills/pr-review-responder/scripts/respond_to_reviews.py +376 -0
  205. package/skills/pr-review-responder/update_skill.py +297 -0
  206. package/skills/prompt-generator/REFERENCE.md +150 -0
  207. package/skills/prompt-generator/SKILL.md +154 -0
  208. package/skills/readability-review/SKILL.md +127 -0
  209. package/skills/recall/SKILL.md +27 -0
  210. package/skills/remember/SKILL.md +63 -0
  211. package/skills/rule-audit/SKILL.md +307 -0
  212. package/skills/rule-creator/SKILL.md +150 -0
  213. package/skills/skill-writer/REFERENCE.md +246 -0
  214. package/skills/skill-writer/SKILL.md +270 -0
  215. package/skills/tdd-team/SKILL.md +128 -0
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import os
4
+ import platform
5
+ import subprocess
6
+
7
+ NTFY_TOPIC = os.environ.get("NTFY_TOPIC", "claude-notifications")
8
+ NTFY_BASE_URL = f"https://ntfy.sh/{NTFY_TOPIC}"
9
+ WINDOWS_CHIMES_PATH = os.path.join(os.environ.get("SYSTEMROOT", r"C:\Windows"), "Media", "Windows Battery Critical.wav")
10
+ LINUX_NOTIFICATION_SOUND = os.environ.get("NOTIFICATION_SOUND", "/usr/share/sounds/freedesktop/stereo/message.oga")
11
+ LINUX_NOTIFICATION_TIMEOUT_MS = "3000"
12
+ TOAST_DISPLAY_DURATION_MILLISECONDS = 6000
13
+ DEFAULT_LINUX_TOAST_TITLE = "Claude Code"
14
+ DEFAULT_LINUX_TOAST_MESSAGE = "Waiting for your input"
15
+
16
+ TOAST_SCRIPT_TEMPLATE = r'''
17
+ Add-Type -AssemblyName System.Windows.Forms
18
+ Add-Type -AssemblyName System.Drawing
19
+ Add-Type @"
20
+ using System;
21
+ using System.Runtime.InteropServices;
22
+ public class Win32 {{
23
+ [DllImport("user32.dll")]
24
+ public static extern bool SetProcessDPIAware();
25
+ [DllImport("user32.dll")]
26
+ public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
27
+ [DllImport("user32.dll")]
28
+ public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
29
+ [DllImport("user32.dll")]
30
+ public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
31
+ [DllImport("user32.dll")]
32
+ public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
33
+ public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
34
+ public const uint SWP_NOACTIVATE = 0x0010;
35
+ public const uint SWP_SHOWWINDOW = 0x0040;
36
+ public const int GWL_EXSTYLE = -20;
37
+ public const int WS_EX_LAYERED = 0x80000;
38
+ public const int WS_EX_TRANSPARENT = 0x20;
39
+ public const uint LWA_ALPHA = 0x2;
40
+ }}
41
+ "@
42
+
43
+ [Win32]::SetProcessDPIAware() | Out-Null
44
+
45
+ $form = New-Object System.Windows.Forms.Form
46
+ $form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None
47
+ $form.Size = New-Object System.Drawing.Size(520, 110)
48
+ $form.ShowInTaskbar = $false
49
+ $form.BackColor = [System.Drawing.Color]::FromArgb(66, 135, 245)
50
+ $form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
51
+
52
+ $screen = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea
53
+ $x = [int]($screen.Left + ($screen.Width - 520) / 2)
54
+ $y = [int]($screen.Bottom - 110 - 50)
55
+ $form.Location = New-Object System.Drawing.Point($x, $y)
56
+
57
+ $inner = New-Object System.Windows.Forms.Panel
58
+ $inner.Size = New-Object System.Drawing.Size(514, 104)
59
+ $inner.Location = New-Object System.Drawing.Point(3, 3)
60
+ $inner.BackColor = [System.Drawing.Color]::FromArgb(45, 45, 45)
61
+ $form.Controls.Add($inner)
62
+
63
+ $titleLabel = New-Object System.Windows.Forms.Label
64
+ $titleLabel.Text = "{title}"
65
+ $titleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold)
66
+ $titleLabel.ForeColor = [System.Drawing.Color]::FromArgb(120, 180, 255)
67
+ $titleLabel.AutoSize = $false
68
+ $titleLabel.Size = New-Object System.Drawing.Size(514, 30)
69
+ $titleLabel.Location = New-Object System.Drawing.Point(0, 8)
70
+ $titleLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
71
+ $inner.Controls.Add($titleLabel)
72
+
73
+ $messageLabel = New-Object System.Windows.Forms.Label
74
+ $messageLabel.Text = "{message}"
75
+ $messageLabel.Font = New-Object System.Drawing.Font("Segoe UI", 11)
76
+ $messageLabel.ForeColor = [System.Drawing.Color]::White
77
+ $messageLabel.AutoSize = $false
78
+ $messageLabel.Size = New-Object System.Drawing.Size(500, 58)
79
+ $messageLabel.Location = New-Object System.Drawing.Point(7, 40)
80
+ $messageLabel.TextAlign = [System.Drawing.ContentAlignment]::TopCenter
81
+ $inner.Controls.Add($messageLabel)
82
+
83
+ $timer = New-Object System.Windows.Forms.Timer
84
+ $timer.Interval = {toast_duration}
85
+ $timer.Add_Tick({{ $form.Close() }})
86
+ $timer.Start()
87
+
88
+ $exStyle = [Win32]::GetWindowLong($form.Handle, [Win32]::GWL_EXSTYLE)
89
+ [Win32]::SetWindowLong($form.Handle, [Win32]::GWL_EXSTYLE, $exStyle -bor [Win32]::WS_EX_LAYERED -bor [Win32]::WS_EX_TRANSPARENT)
90
+ [Win32]::SetLayeredWindowAttributes($form.Handle, 0, 230, [Win32]::LWA_ALPHA)
91
+ [Win32]::SetWindowPos($form.Handle, [Win32]::HWND_TOPMOST, $x, $y, 520, 110, [Win32]::SWP_NOACTIVATE -bor [Win32]::SWP_SHOWWINDOW)
92
+ $form.Show()
93
+ [System.Windows.Forms.Application]::Run($form)
94
+ '''
95
+
96
+
97
+ def is_wsl() -> bool:
98
+ if platform.system() != "Linux":
99
+ return False
100
+ try:
101
+ with open("/proc/version") as proc_version_file:
102
+ return "microsoft" in proc_version_file.read().lower()
103
+ except FileNotFoundError:
104
+ return False
105
+
106
+
107
+ def build_toast_script(title: str, message: str) -> str:
108
+ safe_title = title.replace('"', '`"').replace("'", "`'")
109
+ safe_message = message.replace('"', '`"').replace("'", "`'")
110
+ return TOAST_SCRIPT_TEMPLATE.format(
111
+ title=safe_title,
112
+ message=safe_message,
113
+ toast_duration=TOAST_DISPLAY_DURATION_MILLISECONDS,
114
+ )
115
+
116
+
117
+ def notify_wsl(title: str, message: str) -> None:
118
+ script = build_toast_script(title, message)
119
+ try:
120
+ subprocess.Popen(
121
+ ["powershell.exe", "-ExecutionPolicy", "Bypass", "-Command", script],
122
+ stdout=subprocess.DEVNULL,
123
+ stderr=subprocess.DEVNULL,
124
+ start_new_session=True
125
+ )
126
+ except FileNotFoundError:
127
+ pass
128
+
129
+
130
+ def notify_windows(title: str, message: str) -> None:
131
+ script = build_toast_script(title, message)
132
+ subprocess.Popen(
133
+ ["powershell", "-ExecutionPolicy", "Bypass", "-Command", script],
134
+ stdout=subprocess.DEVNULL,
135
+ stderr=subprocess.DEVNULL,
136
+ creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0
137
+ )
138
+
139
+
140
+ def notify_ntfy(title: str, message: str, priority: str = "high") -> None:
141
+ try:
142
+ subprocess.Popen(
143
+ [
144
+ "curl", "-s",
145
+ "-H", f"Priority: {priority}",
146
+ "-H", "Tags: bell",
147
+ "-H", f"Title: {title}",
148
+ "-d", message,
149
+ NTFY_BASE_URL,
150
+ ],
151
+ stdout=subprocess.DEVNULL,
152
+ stderr=subprocess.DEVNULL
153
+ )
154
+ except FileNotFoundError:
155
+ pass
156
+
157
+
158
+ def notify_linux() -> None:
159
+ subprocess.Popen(
160
+ [
161
+ "notify-send", "-t", LINUX_NOTIFICATION_TIMEOUT_MS,
162
+ "-u", "normal", "-i", "dialog-warning",
163
+ DEFAULT_LINUX_TOAST_TITLE, DEFAULT_LINUX_TOAST_MESSAGE,
164
+ ],
165
+ stdout=subprocess.DEVNULL,
166
+ stderr=subprocess.DEVNULL
167
+ )
168
+
169
+
170
+ def sound_windows() -> None:
171
+ subprocess.Popen(
172
+ ["powershell", "-WindowStyle", "Hidden", "-Command", f"(New-Object Media.SoundPlayer '{WINDOWS_CHIMES_PATH}').PlaySync()"],
173
+ stdout=subprocess.DEVNULL,
174
+ stderr=subprocess.DEVNULL,
175
+ creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0
176
+ )
177
+
178
+
179
+ def sound_wsl() -> None:
180
+ try:
181
+ subprocess.Popen(
182
+ ["powershell.exe", "-WindowStyle", "Hidden", "-Command", f"(New-Object Media.SoundPlayer '{WINDOWS_CHIMES_PATH}').PlaySync()"],
183
+ stdout=subprocess.DEVNULL,
184
+ stderr=subprocess.DEVNULL
185
+ )
186
+ except FileNotFoundError:
187
+ pass
188
+
189
+
190
+ def sound_linux() -> None:
191
+ if os.path.exists(LINUX_NOTIFICATION_SOUND):
192
+ for each_player in ["paplay", "aplay", "play"]:
193
+ try:
194
+ subprocess.Popen(
195
+ [each_player, LINUX_NOTIFICATION_SOUND],
196
+ stdout=subprocess.DEVNULL,
197
+ stderr=subprocess.DEVNULL
198
+ )
199
+ return
200
+ except FileNotFoundError:
201
+ continue
202
+ print("\a", end="", flush=True)
203
+
204
+
205
+ def get_project_name() -> str:
206
+ return os.path.basename(os.getcwd())
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env python3
2
+ """Rewrite plugin config paths to match the current platform.
3
+
4
+ Solves the cross-platform path problem for shared ~/.claude directories.
5
+ On WSL/Docker, rewrites Windows paths to Linux paths.
6
+ On Windows, rewrites Linux paths back to Windows paths (heals WSL damage).
7
+
8
+ Uses json.load/json.dump to avoid all backslash escaping issues.
9
+ Called from Docker entrypoint.sh and can be called from session hooks.
10
+ """
11
+
12
+ import json
13
+ import os
14
+ import sys
15
+ from typing import Union
16
+
17
+
18
+ PLUGIN_CONFIG_FILENAMES = [
19
+ "known_marketplaces.json",
20
+ "installed_plugins.json",
21
+ ]
22
+
23
+ WINDOWS_PATH_PREFIX = os.path.join("C:\\Users", os.environ.get("USERNAME", "unknown"), ".claude")
24
+ LINUX_HOME_PATH_PREFIX = os.path.join(os.path.expanduser("~"), ".claude") if os.name != "nt" else ""
25
+
26
+ JsonValue = Union[str, int, float, bool, None, list, dict]
27
+
28
+
29
+ def detect_local_claude_directory() -> str:
30
+ return os.path.join(os.path.expanduser("~"), ".claude")
31
+
32
+
33
+ def rewrite_paths_in_value(
34
+ value: JsonValue,
35
+ from_prefix: str,
36
+ to_prefix: str,
37
+ ) -> JsonValue:
38
+ if isinstance(value, str):
39
+ return value.replace(from_prefix, to_prefix)
40
+ if isinstance(value, dict):
41
+ return {
42
+ each_key: rewrite_paths_in_value(each_value, from_prefix, to_prefix)
43
+ for each_key, each_value in value.items()
44
+ }
45
+ if isinstance(value, list):
46
+ return [
47
+ rewrite_paths_in_value(each_element, from_prefix, to_prefix)
48
+ for each_element in value
49
+ ]
50
+ return value
51
+
52
+
53
+ def rewrite_plugin_config_file(
54
+ file_path: str,
55
+ from_prefix: str,
56
+ to_prefix: str,
57
+ ) -> bool:
58
+ try:
59
+ with open(file_path, "r", encoding="utf-8") as source_file:
60
+ parsed_config = json.load(source_file)
61
+ except FileNotFoundError:
62
+ return False
63
+ except json.JSONDecodeError as e:
64
+ print(
65
+ f"[rewrite-paths] ERROR: Invalid JSON in {file_path}: {e}",
66
+ file=sys.stderr,
67
+ )
68
+ return False
69
+
70
+ rewritten_config = rewrite_paths_in_value(
71
+ parsed_config,
72
+ from_prefix,
73
+ to_prefix,
74
+ )
75
+
76
+ if rewritten_config == parsed_config:
77
+ return False
78
+
79
+ with open(file_path, "w", encoding="utf-8") as destination_file:
80
+ json.dump(rewritten_config, destination_file, indent=2)
81
+ destination_file.write("\n")
82
+
83
+ return True
84
+
85
+
86
+ def main() -> None:
87
+ is_windows = os.name == "nt"
88
+ local_claude_directory = detect_local_claude_directory()
89
+
90
+ if is_windows:
91
+ from_prefix = LINUX_HOME_PATH_PREFIX
92
+ to_prefix = WINDOWS_PATH_PREFIX
93
+ else:
94
+ from_prefix = WINDOWS_PATH_PREFIX
95
+ to_prefix = local_claude_directory
96
+
97
+ plugins_directory = os.path.join(local_claude_directory, "plugins")
98
+
99
+ rewritten_count = 0
100
+ for each_filename in PLUGIN_CONFIG_FILENAMES:
101
+ each_file_path = os.path.join(plugins_directory, each_filename)
102
+ was_rewritten = rewrite_plugin_config_file(
103
+ each_file_path,
104
+ from_prefix,
105
+ to_prefix,
106
+ )
107
+ if was_rewritten:
108
+ rewritten_count += 1
109
+ print(
110
+ f"[rewrite-paths] Rewrote paths in {each_filename}",
111
+ file=sys.stderr,
112
+ )
113
+
114
+
115
+ if __name__ == "__main__":
116
+ main()
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import sys
4
+
5
+ BULK_UPDATE_KEYWORDS = ["update all", "replace all", "change all", "fix all", "rename all"]
6
+
7
+ BULK_UPDATE_REMINDER = "BULK UPDATE DETECTED: Use a Python script with --preview/--apply instead of line-by-line edits."
8
+
9
+
10
+ def main() -> None:
11
+ try:
12
+ input_data = json.load(sys.stdin)
13
+ except json.JSONDecodeError:
14
+ sys.exit(0)
15
+
16
+ prompt = input_data.get("prompt", "")
17
+
18
+ if not prompt:
19
+ sys.exit(0)
20
+
21
+ message_lower = prompt.lower()
22
+
23
+ if any(keyword in message_lower for keyword in BULK_UPDATE_KEYWORDS):
24
+ print(BULK_UPDATE_REMINDER)
25
+
26
+ sys.exit(0)
27
+
28
+
29
+ if __name__ == "__main__":
30
+ main()
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ UserPromptSubmit hook that detects code-related requests and injects CODE_RULES.md reminder.
4
+ Triggers on keywords indicating code writing, planning, or implementation.
5
+ """
6
+
7
+ import json
8
+ import re
9
+ import sys
10
+
11
+ CODE_KEYWORDS = [
12
+ # Creation verbs
13
+ r'\b(write|create|implement|add|build|make|generate|develop|setup|scaffold|bootstrap|initialize|init|compose|construct|define|declare|register|wire|connect|integrate|introduce)\b',
14
+ # Code nouns
15
+ r'\b(code|function|class|method|script|module|component|hook|test|spec|api|endpoint|route|handler|service|util|helper|factory|interface|type|enum|constant|variable|parameter|argument|logic|algorithm|feature|library|package|dependency|plugin|extension|widget|element|node|token|parser|serializer|validator|formatter|linter|compiler|transpiler|bundler|loader|resolver|provider|consumer|producer|subscriber|publisher|emitter|dispatcher|reducer|selector|adapter|wrapper|decorator|mixin|trait|protocol|abstract|generic|iterator|generator|coroutine|fiber|thread|process|worker|job|task|queue|stack|buffer|stream|pipe|socket|channel|signal|slot|observer|mediator|strategy|command|visitor|singleton|repository|gateway|mapper|transformer|converter|encoder|decoder|interceptor|guard|filter|middleware|pipeline|chain|proxy|facade|bridge|flyweight|memento|prototype)\b',
16
+ # Modification verbs
17
+ r'\b(fix|update|refactor|modify|change|edit|rewrite|improve|enhance|optimize|debug|patch|correct|adjust|tweak|rework|revise|extend|expand|rename|move|extract|inline|split|merge|combine|consolidate|simplify|clean|cleanup|reorganize|restructure|decouple|encapsulate|abstract|generalize|specialize|upgrade|downgrade|migrate|convert|transform|adapt|port|backport)\b',
18
+ # Deletion/removal verbs
19
+ r'\b(delete|remove|drop|deprecate|disable|deactivate|unregister|detach|disconnect|unbind|unsubscribe|uninstall|prune|trim|strip|purge|clear|reset|destroy|dispose|release|free|deallocate)\b',
20
+ # Planning verbs
21
+ r'\b(plan|design|structure|architect|outline|draft|sketch|propose|suggest|approach|strategy|solution|how would|how should|how do|how can|how to|what if|where should|when should|why does|why is|could we|should we|can we|let.s|need to|want to|going to)\b',
22
+ # Review/analysis verbs
23
+ r'\b(review|check|analyze|audit|inspect|examine|validate|verify|assess|evaluate|trace|profile|benchmark|measure|monitor|diagnose|troubleshoot|investigate|identify|detect|discover|locate|find the bug|root cause)\b',
24
+ # Testing verbs
25
+ r'\b(test|run tests|unit test|integration test|e2e test|end.to.end|assert|expect|mock|stub|spy|fake|fixture|setup|teardown|arrange|act|coverage|regression|smoke test|snapshot|parameterize)\b',
26
+ # File types
27
+ r'\.(py|js|ts|tsx|jsx|css|scss|less|html|json|yaml|yml|toml|ini|cfg|sql|sh|bash|zsh|vue|svelte|go|rs|java|kt|swift|rb|php|c|cpp|h|hpp|cs|fs|ex|exs|erl|hs|ml|clj|scala|groovy|dart|lua|r|jl|nim|zig|wasm|graphql|proto|tf|hcl)\b',
28
+ # Programming concepts
29
+ r'\b(loop|condition|if statement|switch|try|catch|exception|error handling|async|await|promise|callback|event|listener|state|props|render|return|import|export|inherit|extend|override|decorator|middleware|migration|schema|model|view|controller|template|query|mutation|subscription|context|scope|closure|binding|reference|pointer|memory|allocation|garbage collection|concurrency|parallelism|synchronization|deadlock|race condition|mutex|semaphore|lock|atomic|transaction|rollback|commit|index|constraint|foreign key|primary key|join|aggregate|subquery|cursor|trigger|stored procedure|materialized view)\b',
30
+ # DevOps/infrastructure
31
+ r'\b(deploy|release|publish|ship|rollout|rollback|ci|cd|pipeline|docker|container|kubernetes|k8s|terraform|ansible|nginx|apache|server|cluster|replica|shard|partition|load balancer|proxy|cdn|ssl|tls|certificate|dns|domain|cors|csp|firewall|vpc|subnet|security group|iam|role|policy|secret|vault|env var|environment variable|configuration|config file|dotenv)\b',
32
+ # Database/data
33
+ r'\b(database|db|sql|nosql|mongo|postgres|mysql|sqlite|redis|elasticsearch|dynamodb|cassandra|orm|queryset|recordset|dataset|dataframe|csv|parquet|avro|protobuf|graphql|rest|grpc|websocket|sse|webhook|polling|pagination|cursor|offset|limit|batch|bulk|upsert|crud)\b',
34
+ # Sample/example requests
35
+ r'\b(example|sample|snippet|demo|prototype|proof of concept|poc|skeleton|boilerplate|starter|template|scaffold|seed|initial|baseline|reference implementation|minimal|basic|simple|quick|small)\b',
36
+ # Common tool/framework references
37
+ r'\b(django|flask|fastapi|express|next|react|vue|angular|svelte|tailwind|bootstrap|jest|pytest|mocha|cypress|playwright|selenium|webpack|vite|rollup|esbuild|npm|yarn|pnpm|pip|poetry|cargo|gradle|maven|cmake|bazel|make|dockerfile|compose|github|gitlab|bitbucket|jira|confluence)\b',
38
+ ]
39
+
40
+ CONDENSED_RULES = """
41
+ <code-rules-reminder>
42
+ ## MANDATORY CODE RULES - APPLY TO ALL CODE (samples, plans, implementations)
43
+
44
+ 1. **NO COMMENTS** - Self-documenting names only
45
+ - BAD: `d = 0.5 # delay` -> GOOD: `delay_between_retries_seconds = 0.5`
46
+
47
+ 2. **NO MAGIC VALUES** - Everything named and in config
48
+ - BAD: `if score > 0.8:` -> GOOD: `if score > MINIMUM_CONFIDENCE_THRESHOLD:`
49
+
50
+ 3. **NO ABBREVIATIONS** - Full words always
51
+ - BAD: `ctx`, `cfg`, `msg`, `btn` -> GOOD: `context`, `configuration`, `message`, `button`
52
+
53
+ 4. **COMPLETE TYPE HINTS** - All parameters and returns typed, no `Any`
54
+
55
+ 5. **CENTRALIZED CONFIG** - Constants in config/, imported everywhere
56
+
57
+ 6. **SEARCH BEFORE CREATE** - Use everything-search skill before defining constants
58
+
59
+ 7. **ALL IMPORTS SHOWN** - Every code block includes its imports
60
+
61
+ 8. **SELF-CONTAINED COMPONENTS** - Components own their modals/toasts/state
62
+
63
+ CHECKLIST before writing ANY code:
64
+ [ ] No comments (names explain everything)
65
+ [ ] No magic values (all named constants)
66
+ [ ] No abbreviations (full words)
67
+ [ ] Complete types (all params + returns)
68
+ [ ] Imports shown
69
+
70
+ SCOPE: These rules apply to code you WRITE or MODIFY. Do NOT fix violations in untouched code unless explicitly instructed.
71
+ </code-rules-reminder>
72
+ """
73
+
74
+
75
+ def main() -> None:
76
+ try:
77
+ hook_input = json.load(sys.stdin)
78
+ except json.JSONDecodeError:
79
+ sys.exit(0)
80
+
81
+ prompt = hook_input.get("prompt", "")
82
+
83
+ if not prompt:
84
+ sys.exit(0)
85
+
86
+ message_lower = prompt.lower()
87
+
88
+ for pattern in CODE_KEYWORDS:
89
+ if re.search(pattern, message_lower, re.IGNORECASE):
90
+ print(CONDENSED_RULES)
91
+ sys.exit(0)
92
+
93
+ sys.exit(0)
94
+
95
+
96
+ if __name__ == "__main__":
97
+ main()
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import sys
5
+
6
+ PLUGIN_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
7
+ CODE_RULES_PATH = os.path.join(PLUGIN_ROOT, "docs", "CODE_RULES.md")
8
+
9
+
10
+ def load_code_rules() -> str:
11
+ try:
12
+ with open(CODE_RULES_PATH, encoding="utf-8") as code_rules_file:
13
+ return code_rules_file.read()
14
+ except (FileNotFoundError, OSError):
15
+ return ""
16
+
17
+
18
+ def main() -> None:
19
+ try:
20
+ json.load(sys.stdin)
21
+ except json.JSONDecodeError:
22
+ sys.exit(0)
23
+
24
+ code_rules_content = load_code_rules()
25
+ if not code_rules_content:
26
+ sys.exit(0)
27
+
28
+ reinject_payload = {
29
+ "hookSpecificOutput": {
30
+ "hookEventName": "SessionStart",
31
+ "additionalContext": code_rules_content,
32
+ }
33
+ }
34
+ print(json.dumps(reinject_payload))
35
+ sys.exit(0)
36
+
37
+
38
+ if __name__ == "__main__":
39
+ main()
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ UserPromptSubmit hook that detects "hook" mentions and injects
4
+ context about our current hook structure and how to add new hooks.
5
+ """
6
+
7
+ import json
8
+ import sys
9
+
10
+
11
+ TRIGGER_PHRASES = [
12
+ "hook",
13
+ ]
14
+
15
+ EXCLUDE_PHRASES = [
16
+ "react hook",
17
+ "usehook",
18
+ "usestate",
19
+ "useeffect",
20
+ "custom hook",
21
+ "git hook",
22
+ "pre-commit hook",
23
+ "webhook",
24
+ ]
25
+
26
+ CONTEXT = """
27
+ <hook-structure-context>
28
+ ## Claude Code Hook System
29
+
30
+ ### Architecture
31
+ - **Runner pattern**: `run-hook-wrapper.js` (Node.js) -> `run-hook.py` (Python) -> individual hook
32
+ - **Hook directory**: `hooks/`
33
+
34
+ ### Hook Organization (subfolder structure)
35
+ ```
36
+ hooks/
37
+ |-- rewrite-plugin-paths.py
38
+ |-- session/
39
+ | |-- compact-context-reinject.py
40
+ | |-- plugin-data-dir-cleanup.py
41
+ | +-- hook-structure-context.py
42
+ |-- notification/
43
+ | |-- attention-needed-notify.py
44
+ | |-- claude-notification-handler.py
45
+ | +-- notification_utils.py
46
+ |-- advisory/
47
+ | |-- refactor-guard.py
48
+ | +-- migration-safety-advisor.py
49
+ |-- validation/
50
+ | |-- code-style-validator.py
51
+ | |-- hook-format-validator.py
52
+ | |-- mypy_validator.py
53
+ | +-- e2e-test-validator.py
54
+ |-- lifecycle/
55
+ | |-- config-change-guard.py
56
+ | +-- session-end-cleanup.py
57
+ |-- blocking/
58
+ | |-- pyautogui-scroll-blocker.py
59
+ | |-- sensitive-file-protector.py
60
+ | |-- write-existing-file-blocker.py
61
+ | +-- destructive-command-blocker.py
62
+ |-- git-hooks/
63
+ | +-- post-commit.py
64
+ |-- github-action/
65
+ | +-- test_workflow.py
66
+ +-- validators/
67
+ |-- (validation check modules)
68
+ +-- test_files/
69
+ ```
70
+
71
+ ### Event Types
72
+ | Event | When | Input (stdin JSON) | Can Block? |
73
+ |-------|------|-------------------|------------|
74
+ | SessionStart | Session begins | `{}` | No |
75
+ | UserPromptSubmit | User sends message | `{"prompt": "..."}` | No (advisory) |
76
+ | PreToolUse | Before tool execution | `{"tool_name": "...", "tool_input": {...}}` | YES (exit 2) |
77
+ | PostToolUse | After tool execution | `{"tool_name": "...", "tool_input": {...}, "tool_output": "..."}` | No |
78
+ | SubagentStop | Agent completes | `{"agent_type": "...", ...}` | No |
79
+ | Stop | Session ends | `{}` | No |
80
+
81
+ ### How to Add a New Hook
82
+
83
+ 1. **Create the hook file** in the appropriate subfolder:
84
+ ```python
85
+ #!/usr/bin/env python3
86
+ import json
87
+ import sys
88
+
89
+ hook_input = json.load(sys.stdin)
90
+ print("Context to inject")
91
+ sys.exit(0)
92
+ ```
93
+
94
+ 2. **Register in settings.json** under the correct event type:
95
+ ```json
96
+ {
97
+ "type": "command",
98
+ "command": "node -e \\"process.argv.splice(1,0,'_');require(require('os').homedir()+'/.claude/hooks/run-hook-wrapper.js')\\" \\"session/your-hook.py\\"",
99
+ "timeout": 15000
100
+ }
101
+ ```
102
+
103
+ 3. **Key rules**:
104
+ - Always use the `run-hook-wrapper.js` pattern (cross-platform)
105
+ - Set explicit timeouts (10000-30000ms)
106
+ - PreToolUse hooks use `matcher` to scope which tools they fire on
107
+ - UserPromptSubmit hooks match on `prompt` content
108
+ - Print output = context injected into Claude's conversation
109
+ - Advisory hooks exit 0; blocking hooks exit 2 with `hookSpecificOutput.permissionDecision`
110
+ </hook-structure-context>
111
+ """
112
+
113
+
114
+ def main() -> None:
115
+ try:
116
+ hook_input = json.load(sys.stdin)
117
+ except json.JSONDecodeError:
118
+ sys.exit(0)
119
+
120
+ prompt = hook_input.get("prompt", "")
121
+
122
+ if not prompt:
123
+ sys.exit(0)
124
+
125
+ message_lower = prompt.lower()
126
+
127
+ for exclude in EXCLUDE_PHRASES:
128
+ if exclude in message_lower:
129
+ sys.exit(0)
130
+
131
+ for phrase in TRIGGER_PHRASES:
132
+ if phrase in message_lower:
133
+ print(CONTEXT)
134
+ sys.exit(0)
135
+
136
+ sys.exit(0)
137
+
138
+
139
+ if __name__ == "__main__":
140
+ main()
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3
2
+ """SessionStart hook — removes empty plugin data directories to prevent EEXIST.
3
+
4
+ Claude Code's infrastructure calls mkdirSync without {recursive: true} when
5
+ setting up plugin data directories for hook execution. If the directory already
6
+ exists from a previous session, the mkdir throws EEXIST and the hook fails.
7
+
8
+ This workaround removes known empty plugin data directories at session start
9
+ so the infrastructure's mkdir succeeds when the Stop hook fires later.
10
+
11
+ Tracking: https://github.com/anthropics/claude-code/issues — unfixed code path
12
+ separate from the EEXIST fixes in v2.1.70-v2.1.72.
13
+ """
14
+
15
+ import os
16
+ import sys
17
+
18
+ PLUGINS_DATA_DIRECTORY = os.path.join(
19
+ os.path.expanduser("~"), ".claude", "plugins", "data"
20
+ )
21
+
22
+ AFFECTED_PLUGIN_DIRECTORIES = [
23
+ "ralph-loop-claude-plugins-official",
24
+ ]
25
+
26
+
27
+ def main() -> None:
28
+ for plugin_directory_name in AFFECTED_PLUGIN_DIRECTORIES:
29
+ target_path = os.path.join(PLUGINS_DATA_DIRECTORY, plugin_directory_name)
30
+ if not os.path.isdir(target_path):
31
+ continue
32
+ try:
33
+ os.rmdir(target_path)
34
+ except OSError:
35
+ pass
36
+
37
+
38
+ if __name__ == "__main__":
39
+ main()