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,246 @@
1
+ {
2
+ "description": "Code standards enforcement, safety guards, and development workflow hooks",
3
+ "hooks": {
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "Write|Edit",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/write-existing-file-blocker.py",
11
+ "timeout": 10
12
+ },
13
+ {
14
+ "type": "command",
15
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/sensitive-file-protector.py",
16
+ "timeout": 10
17
+ },
18
+ {
19
+ "type": "command",
20
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/pyautogui-scroll-blocker.py",
21
+ "timeout": 10
22
+ },
23
+ {
24
+ "type": "command",
25
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/validation/hook-format-validator.py",
26
+ "timeout": 15
27
+ },
28
+ {
29
+ "type": "command",
30
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/validators/run_all_validators.py",
31
+ "timeout": 15
32
+ },
33
+ {
34
+ "type": "command",
35
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/code-rules-enforcer.py",
36
+ "timeout": 15
37
+ },
38
+ {
39
+ "type": "command",
40
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/tdd-enforcer.py",
41
+ "timeout": 10
42
+ },
43
+ {
44
+ "type": "command",
45
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/validation/code-style-validator.py",
46
+ "timeout": 15
47
+ },
48
+ {
49
+ "type": "command",
50
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/docker-settings-guard.py",
51
+ "timeout": 15
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "matcher": "Edit",
57
+ "hooks": [
58
+ {
59
+ "type": "command",
60
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/advisory/refactor-guard.py",
61
+ "timeout": 15
62
+ },
63
+ {
64
+ "type": "command",
65
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/advisory/migration-safety-advisor.py",
66
+ "timeout": 15
67
+ }
68
+ ]
69
+ },
70
+ {
71
+ "matcher": "Bash",
72
+ "hooks": [
73
+ {
74
+ "type": "command",
75
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/destructive-command-blocker.py",
76
+ "timeout": 10
77
+ },
78
+ {
79
+ "type": "command",
80
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/block-main-commit.py",
81
+ "timeout": 15
82
+ },
83
+ {
84
+ "type": "command",
85
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/pr-description-enforcer.py",
86
+ "timeout": 10
87
+ },
88
+ {
89
+ "type": "command",
90
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/test-preflight-check.py",
91
+ "timeout": 10
92
+ }
93
+ ]
94
+ },
95
+ {
96
+ "matcher": "Task|Agent",
97
+ "hooks": [
98
+ {
99
+ "type": "command",
100
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/parallel-task-blocker.py",
101
+ "timeout": 10
102
+ }
103
+ ]
104
+ },
105
+ {
106
+ "matcher": "AskUserQuestion",
107
+ "hooks": [
108
+ {
109
+ "type": "command",
110
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/notification/attention-needed-notify.py",
111
+ "timeout": 15
112
+ }
113
+ ]
114
+ }
115
+ ],
116
+ "UserPromptSubmit": [
117
+ {
118
+ "matcher": "",
119
+ "hooks": [
120
+ {
121
+ "type": "command",
122
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/hook-structure-context.py",
123
+ "timeout": 10
124
+ },
125
+ {
126
+ "type": "command",
127
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/bulk-edit-reminder.py",
128
+ "timeout": 15
129
+ },
130
+ {
131
+ "type": "command",
132
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/code-rules-reminder.py",
133
+ "timeout": 15
134
+ }
135
+ ]
136
+ }
137
+ ],
138
+ "SessionStart": [
139
+ {
140
+ "matcher": "compact",
141
+ "hooks": [
142
+ {
143
+ "type": "command",
144
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/compact-context-reinject.py",
145
+ "timeout": 10
146
+ }
147
+ ]
148
+ },
149
+ {
150
+ "matcher": "",
151
+ "hooks": [
152
+ {
153
+ "type": "command",
154
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/plugin-data-dir-cleanup.py",
155
+ "timeout": 10
156
+ }
157
+ ]
158
+ }
159
+ ],
160
+ "Stop": [
161
+ {
162
+ "matcher": "",
163
+ "hooks": [
164
+ {
165
+ "type": "command",
166
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/notification/attention-needed-notify.py",
167
+ "timeout": 15
168
+ },
169
+ {
170
+ "type": "command",
171
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/blocking/hedging-language-blocker.py",
172
+ "timeout": 10
173
+ }
174
+ ]
175
+ }
176
+ ],
177
+ "SessionEnd": [
178
+ {
179
+ "matcher": "",
180
+ "hooks": [
181
+ {
182
+ "type": "command",
183
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/lifecycle/session-end-cleanup.py",
184
+ "timeout": 15
185
+ }
186
+ ]
187
+ }
188
+ ],
189
+ "ConfigChange": [
190
+ {
191
+ "matcher": "user_settings",
192
+ "hooks": [
193
+ {
194
+ "type": "command",
195
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/lifecycle/config-change-guard.py",
196
+ "timeout": 10
197
+ }
198
+ ]
199
+ }
200
+ ],
201
+ "PostToolUse": [
202
+ {
203
+ "matcher": "Write|Edit",
204
+ "hooks": [
205
+ {
206
+ "type": "command",
207
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/validation/mypy_validator.py",
208
+ "timeout": 30
209
+ },
210
+ {
211
+ "type": "command",
212
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/validation/e2e-test-validator.py",
213
+ "timeout": 15
214
+ },
215
+ {
216
+ "type": "command",
217
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/workflow/auto-formatter.py",
218
+ "timeout": 30
219
+ }
220
+ ]
221
+ },
222
+ {
223
+ "matcher": "Agent|Task|TeamCreate",
224
+ "hooks": [
225
+ {
226
+ "type": "command",
227
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/workflow/investigation-tracker-reset.py",
228
+ "timeout": 10
229
+ }
230
+ ]
231
+ }
232
+ ],
233
+ "Notification": [
234
+ {
235
+ "matcher": "",
236
+ "hooks": [
237
+ {
238
+ "type": "command",
239
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/notification/claude-notification-handler.py",
240
+ "timeout": 15
241
+ }
242
+ ]
243
+ }
244
+ ]
245
+ }
246
+ }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+ from datetime import datetime
3
+ import json
4
+ import os
5
+ import sys
6
+
7
+ AUDIT_LOG = os.path.expanduser("~/.claude/cache/config-change-audit.log")
8
+ KNOWN_HOOK_COUNT_FILE = os.path.expanduser("~/.claude/cache/known-hook-count.txt")
9
+
10
+
11
+ def count_hooks_in_settings(file_path: str) -> int:
12
+ try:
13
+ with open(file_path) as settings_file:
14
+ settings = json.load(settings_file)
15
+ except (OSError, json.JSONDecodeError):
16
+ return 0
17
+ hooks_section = settings.get("hooks", {})
18
+ total_count = 0
19
+ for event_hook_groups in hooks_section.values():
20
+ for hook_configuration in event_hook_groups:
21
+ total_count += len(hook_configuration.get("hooks", []))
22
+ return total_count
23
+
24
+
25
+ def write_audit_entry(source: str, file_path: str) -> None:
26
+ audit_entry = f"{datetime.now().isoformat()} source={source} file={file_path}\n"
27
+ try:
28
+ os.makedirs(os.path.dirname(AUDIT_LOG), exist_ok=True)
29
+ with open(AUDIT_LOG, "a") as audit_file:
30
+ audit_file.write(audit_entry)
31
+ except OSError:
32
+ pass
33
+
34
+
35
+ def guard_hook_injection(file_path: str) -> None:
36
+ current_count = count_hooks_in_settings(file_path)
37
+
38
+ if not os.path.exists(KNOWN_HOOK_COUNT_FILE):
39
+ try:
40
+ with open(KNOWN_HOOK_COUNT_FILE, "w") as count_file:
41
+ count_file.write(str(current_count))
42
+ except OSError:
43
+ pass
44
+ return
45
+
46
+ try:
47
+ with open(KNOWN_HOOK_COUNT_FILE) as count_file:
48
+ stored_count = int(count_file.read().strip())
49
+ except (OSError, ValueError):
50
+ stored_count = current_count
51
+
52
+ try:
53
+ with open(KNOWN_HOOK_COUNT_FILE, "w") as count_file:
54
+ count_file.write(str(current_count))
55
+ except OSError:
56
+ pass
57
+
58
+ if current_count > stored_count:
59
+ block_decision = {
60
+ "decision": "block",
61
+ "reason": f"Hook count changed {stored_count} -> {current_count}. Delete known-hook-count.txt to reset.",
62
+ }
63
+ print(json.dumps(block_decision))
64
+
65
+
66
+ def main() -> None:
67
+ try:
68
+ hook_input = json.load(sys.stdin)
69
+ except json.JSONDecodeError:
70
+ sys.exit(0)
71
+
72
+ source = hook_input.get("source", "")
73
+ file_path = hook_input.get("file_path", "")
74
+
75
+ write_audit_entry(source, file_path)
76
+
77
+ if source == "user_settings" and file_path:
78
+ guard_hook_injection(file_path)
79
+
80
+ sys.exit(0)
81
+
82
+
83
+ if __name__ == "__main__":
84
+ main()
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import sys
5
+ import time
6
+ from typing import Callable
7
+
8
+ CACHE_DIR = os.path.expanduser("~/.claude/cache/")
9
+ STALE_THRESHOLD_DAYS = 7
10
+ BACKUP_RETENTION_DAYS = 30
11
+ SECONDS_PER_DAY = 86400
12
+
13
+
14
+ def purge_old_entries(
15
+ directory: str,
16
+ max_age_days: int,
17
+ should_include: Callable[[os.DirEntry[str]], bool] = lambda _entry: True,
18
+ ) -> None:
19
+ if not os.path.exists(directory):
20
+ return
21
+ cutoff_time = time.time() - (max_age_days * SECONDS_PER_DAY)
22
+ try:
23
+ for each_entry in os.scandir(directory):
24
+ if should_include(each_entry):
25
+ try:
26
+ if each_entry.stat().st_mtime < cutoff_time:
27
+ os.unlink(each_entry.path)
28
+ except OSError:
29
+ pass
30
+ except OSError:
31
+ pass
32
+
33
+
34
+ def main() -> None:
35
+ try:
36
+ json.load(sys.stdin)
37
+ except json.JSONDecodeError:
38
+ sys.exit(0)
39
+
40
+ purge_old_entries(
41
+ CACHE_DIR,
42
+ STALE_THRESHOLD_DAYS,
43
+ should_include=lambda each_entry: each_entry.name.startswith("claude-ctx-") and each_entry.name.endswith(".json"),
44
+ )
45
+ purge_old_entries(
46
+ CACHE_DIR,
47
+ STALE_THRESHOLD_DAYS,
48
+ should_include=lambda each_entry: each_entry.name.endswith(".tmp"),
49
+ )
50
+ purge_old_entries(
51
+ os.path.join(CACHE_DIR, "transcript-backups"),
52
+ BACKUP_RETENTION_DAYS,
53
+ )
54
+
55
+ sys.exit(0)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ main()
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Notification hook - cross-platform (Windows/Linux/WSL)
4
+ Plays chimes sound + shows desktop notification when Claude needs user input.
5
+ """
6
+
7
+ import json
8
+ import os
9
+ import platform
10
+ import sys
11
+
12
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
13
+ from notification_utils import (
14
+ notify_ntfy,
15
+ is_wsl,
16
+ notify_windows,
17
+ notify_wsl,
18
+ notify_linux,
19
+ sound_windows,
20
+ sound_wsl,
21
+ sound_linux,
22
+ get_project_name,
23
+ )
24
+
25
+ DEFAULT_MESSAGE = "Input needed"
26
+
27
+
28
+ def get_question_from_stdin() -> str:
29
+ """Extract question text from hook input JSON."""
30
+ try:
31
+ hook_input = json.load(sys.stdin)
32
+ tool_input = hook_input.get("tool_input", {})
33
+ questions = tool_input.get("questions", [])
34
+ if questions:
35
+ return questions[0].get("question", DEFAULT_MESSAGE)
36
+ except (json.JSONDecodeError, KeyError, IndexError):
37
+ pass
38
+ return DEFAULT_MESSAGE
39
+
40
+
41
+ def main() -> None:
42
+ system = platform.system()
43
+
44
+ project_name = get_project_name()
45
+ question_text = get_question_from_stdin()
46
+
47
+ notify_ntfy(title=project_name, message=question_text)
48
+
49
+ if system == "Windows":
50
+ sound_windows()
51
+ notify_windows(project_name, question_text)
52
+ elif is_wsl():
53
+ sound_wsl()
54
+ notify_wsl(project_name, question_text)
55
+ elif system == "Linux":
56
+ sound_linux()
57
+ notify_linux()
58
+ else:
59
+ print("\a", end="", flush=True)
60
+
61
+
62
+ if __name__ == "__main__":
63
+ main()
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import json
4
+ import os
5
+ import platform
6
+ import sys
7
+
8
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
9
+ from notification_utils import (
10
+ notify_ntfy,
11
+ is_wsl,
12
+ notify_windows,
13
+ notify_wsl,
14
+ sound_wsl,
15
+ sound_windows,
16
+ get_project_name,
17
+ )
18
+
19
+
20
+ def send_desktop_and_push_notification(
21
+ project_name: str,
22
+ notification_message: str,
23
+ ntfy_priority: str,
24
+ ) -> None:
25
+ notify_ntfy(title=project_name, message=notification_message, priority=ntfy_priority)
26
+ system = platform.system()
27
+ if system == "Windows":
28
+ sound_windows()
29
+ notify_windows(project_name, notification_message)
30
+ elif is_wsl():
31
+ sound_wsl()
32
+ notify_wsl(project_name, notification_message)
33
+
34
+
35
+ def main() -> None:
36
+ try:
37
+ hook_input = json.load(sys.stdin)
38
+ except (json.JSONDecodeError, ValueError):
39
+ sys.exit(0)
40
+
41
+ notification_type = hook_input.get("notification_type", "")
42
+ notification_message = hook_input.get("message", "Claude needs attention")
43
+ project_name = get_project_name()
44
+
45
+ if notification_type == "idle_prompt":
46
+ send_desktop_and_push_notification(project_name, notification_message, ntfy_priority="default")
47
+ elif notification_type == "permission_prompt":
48
+ permission_message = f"[PERMISSION] {notification_message}"
49
+ send_desktop_and_push_notification(project_name, permission_message, ntfy_priority="high")
50
+ elif notification_type == "auth_success":
51
+ print(f"auth_success: {notification_message}", file=sys.stderr)
52
+ elif notification_type == "elicitation_dialog":
53
+ print(f"elicitation_dialog: {notification_message}", file=sys.stderr)
54
+
55
+ sys.exit(0)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ main()