ima-claude 2.9.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 (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +463 -0
  3. package/dist/cli.js +1064 -0
  4. package/package.json +49 -0
  5. package/platforms/claude/adapter.ts +115 -0
  6. package/platforms/junie/adapter.ts +254 -0
  7. package/platforms/junie/agents-template.md +113 -0
  8. package/platforms/junie/hook-translations.md +84 -0
  9. package/platforms/shared/detector.ts +27 -0
  10. package/platforms/shared/installer.ts +202 -0
  11. package/platforms/shared/types.ts +78 -0
  12. package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
  13. package/plugins/ima-claude/agents/explorer.md +30 -0
  14. package/plugins/ima-claude/agents/implementer.md +30 -0
  15. package/plugins/ima-claude/agents/memory.md +42 -0
  16. package/plugins/ima-claude/agents/reviewer.md +53 -0
  17. package/plugins/ima-claude/agents/tester.md +33 -0
  18. package/plugins/ima-claude/agents/wp-developer.md +46 -0
  19. package/plugins/ima-claude/hooks/README.md +145 -0
  20. package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
  21. package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
  22. package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
  23. package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
  24. package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
  25. package/plugins/ima-claude/hooks/docs_organization.py +104 -0
  26. package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
  27. package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
  28. package/plugins/ima-claude/hooks/hook_logger.py +69 -0
  29. package/plugins/ima-claude/hooks/hooks.json +239 -0
  30. package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
  31. package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
  32. package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
  33. package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
  34. package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
  35. package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
  36. package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
  37. package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
  38. package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
  39. package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
  40. package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
  41. package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
  42. package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
  43. package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
  44. package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
  45. package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
  46. package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
  47. package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
  48. package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
  49. package/plugins/ima-claude/personalities/README.md +45 -0
  50. package/plugins/ima-claude/personalities/enable-40k.md +69 -0
  51. package/plugins/ima-claude/personalities/enable-templars.md +69 -0
  52. package/plugins/ima-claude/skills/.research-summary.md +340 -0
  53. package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
  54. package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
  55. package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
  56. package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
  57. package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
  58. package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
  59. package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
  60. package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
  61. package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
  62. package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
  63. package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
  64. package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
  65. package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
  66. package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
  67. package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
  68. package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
  69. package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
  70. package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
  71. package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
  72. package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
  73. package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
  74. package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
  75. package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
  76. package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
  77. package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
  78. package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
  79. package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
  80. package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
  81. package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
  82. package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
  83. package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
  84. package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
  85. package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
  86. package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
  87. package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
  88. package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
  89. package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
  90. package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
  91. package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
  92. package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
  93. package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
  94. package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
  95. package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
  96. package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
  97. package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
  98. package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
  99. package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
  100. package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
  101. package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
  102. package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
  103. package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
  104. package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
  105. package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
  106. package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
  107. package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
  108. package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
  109. package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
  110. package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
  111. package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
  112. package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
  113. package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
  114. package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
  115. package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
  116. package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
  117. package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
  118. package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
  119. package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
  120. package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
  121. package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
  122. package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
  123. package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
  124. package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
  125. package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
  126. package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
  127. package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
  128. package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
  129. package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
  130. package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
  131. package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
  132. package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
  133. package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
  134. package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
  135. package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
  136. package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
  137. package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
  138. package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
  139. package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
  140. package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
  141. package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
  142. package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
  143. package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
  144. package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
  145. package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
  146. package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
  147. package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
  148. package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
  149. package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
  150. package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
  151. package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
  152. package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
  153. package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
  154. package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
  155. package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
  156. package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
  157. package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
  158. package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
  159. package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
  160. package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
  161. package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
  162. package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
  163. package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
  164. package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
  165. package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
  166. package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
  167. package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
  168. package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
  169. package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
  170. package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
  171. package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
  172. package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
  173. package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
  174. package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
  175. package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
  176. package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
  177. package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
  178. package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
  179. package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
  180. package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
  181. package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
  182. package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Standalone debug logger for ima-claude hooks.
4
+
5
+ Only active when the environment variable CLAUDE_HOOK_DEBUG=1 is set.
6
+ When the variable is absent or set to anything else, log_hook() is a no-op —
7
+ zero overhead in normal operation.
8
+
9
+ Usage
10
+ -----
11
+ Add to any hook script to gain a persistent audit trail:
12
+
13
+ import sys, os
14
+ sys.path.insert(0, os.path.dirname(__file__))
15
+ from hook_logger import log_hook
16
+
17
+ # When the hook fires:
18
+ log_hook("my_hook_name", triggered=True, reason="why it fired")
19
+
20
+ # On skip paths:
21
+ log_hook("my_hook_name", triggered=False, reason="why it was skipped")
22
+
23
+ Enable logging before launching Claude Code:
24
+
25
+ export CLAUDE_HOOK_DEBUG=1
26
+ claude
27
+
28
+ Watch the log in real time:
29
+
30
+ tail -f ~/.claude/hook-activity.log
31
+
32
+ Log format:
33
+
34
+ 2026-02-27 14:23:01 | enforce_rg_over_grep | TRIGGERED | grep found in command
35
+ 2026-02-27 14:23:05 | serena_over_read | SKIPPED | non-code file extension
36
+
37
+ This file does NOT modify any existing hook scripts. It is imported on demand only.
38
+ """
39
+ import datetime
40
+ import os
41
+
42
+
43
+ LOG_FILE = os.path.expanduser("~/.claude/hook-activity.log")
44
+ _DEBUG_ENABLED = os.environ.get("CLAUDE_HOOK_DEBUG", "").strip() == "1"
45
+
46
+
47
+ def log_hook(hook_name: str, triggered: bool, reason: str = "") -> None:
48
+ """Write a timestamped entry to the hook activity log.
49
+
50
+ Args:
51
+ hook_name: Short name identifying the hook (e.g. "enforce_rg_over_grep").
52
+ triggered: True if the hook emitted a warning/action; False if it skipped.
53
+ reason: Human-readable explanation for the outcome.
54
+ """
55
+ if not _DEBUG_ENABLED:
56
+ return
57
+
58
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
59
+ status = "TRIGGERED" if triggered else "SKIPPED"
60
+ entry = f"{timestamp} | {hook_name:<24} | {status:<9} | {reason}\n"
61
+
62
+ try:
63
+ log_dir = os.path.dirname(LOG_FILE)
64
+ os.makedirs(log_dir, exist_ok=True)
65
+ with open(LOG_FILE, "a", encoding="utf-8") as f:
66
+ f.write(entry)
67
+ except OSError:
68
+ # Never let logging errors propagate into a hook and alter its exit code.
69
+ pass
@@ -0,0 +1,239 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume|clear|compact",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/bootstrap.sh"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PreToolUse": [
15
+ {
16
+ "matcher": "Bash",
17
+ "hooks": [
18
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/block_sed_edits.py" },
19
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/enforce_rg_over_grep.py" },
20
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/memory_bootstrap.py" }
21
+ ]
22
+ },
23
+ {
24
+ "matcher": "Read",
25
+ "hooks": [
26
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/memory_bootstrap.py" },
27
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/serena_over_read.py" }
28
+ ]
29
+ },
30
+ {
31
+ "matcher": "Edit",
32
+ "hooks": [
33
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/memory_bootstrap.py" }
34
+ ]
35
+ },
36
+ {
37
+ "matcher": "Write",
38
+ "hooks": [
39
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/memory_bootstrap.py" }
40
+ ]
41
+ },
42
+ {
43
+ "matcher": "Glob",
44
+ "hooks": [
45
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/memory_bootstrap.py" }
46
+ ]
47
+ },
48
+ {
49
+ "matcher": "Grep",
50
+ "hooks": [
51
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/memory_bootstrap.py" },
52
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/serena_over_grep.py" }
53
+ ]
54
+ },
55
+ {
56
+ "matcher": "mcp__tavily__tavily-extract",
57
+ "hooks": [
58
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/tavily_extract_advanced.py" },
59
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/vestige_before_external.py" }
60
+ ]
61
+ },
62
+ {
63
+ "matcher": "mcp__tavily__tavily_search",
64
+ "hooks": [
65
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/vestige_before_external.py" }
66
+ ]
67
+ },
68
+ {
69
+ "matcher": "mcp__tavily__tavily_research",
70
+ "hooks": [
71
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/vestige_before_external.py" }
72
+ ]
73
+ },
74
+ {
75
+ "matcher": "mcp__context7__resolve-library-id",
76
+ "hooks": [
77
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/vestige_before_external.py" }
78
+ ]
79
+ },
80
+ {
81
+ "matcher": "mcp__context7__query-docs",
82
+ "hooks": [
83
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/vestige_before_external.py" }
84
+ ]
85
+ },
86
+ {
87
+ "matcher": "WebFetch",
88
+ "hooks": [
89
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/webfetch_to_tavily.py" }
90
+ ]
91
+ },
92
+ {
93
+ "matcher": "WebSearch",
94
+ "hooks": [
95
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/websearch_to_tavily.py" }
96
+ ]
97
+ },
98
+ {
99
+ "matcher": "mcp__serena__jet_brains_find_symbol",
100
+ "hooks": [
101
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/serena_project_check.py" }
102
+ ]
103
+ },
104
+ {
105
+ "matcher": "mcp__serena__jet_brains_find_referencing_symbols",
106
+ "hooks": [
107
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/serena_project_check.py" }
108
+ ]
109
+ },
110
+ {
111
+ "matcher": "mcp__serena__jet_brains_get_symbols_overview",
112
+ "hooks": [
113
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/serena_project_check.py" }
114
+ ]
115
+ },
116
+ {
117
+ "matcher": "mcp__serena__jet_brains_type_hierarchy",
118
+ "hooks": [
119
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/serena_project_check.py" }
120
+ ]
121
+ },
122
+ {
123
+ "matcher": "mcp__claude_ai_Atlassian__getJiraIssue",
124
+ "hooks": [
125
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
126
+ ]
127
+ },
128
+ {
129
+ "matcher": "mcp__claude_ai_Atlassian__editJiraIssue",
130
+ "hooks": [
131
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
132
+ ]
133
+ },
134
+ {
135
+ "matcher": "mcp__claude_ai_Atlassian__createJiraIssue",
136
+ "hooks": [
137
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
138
+ ]
139
+ },
140
+ {
141
+ "matcher": "mcp__claude_ai_Atlassian__searchJiraIssuesUsingJql",
142
+ "hooks": [
143
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
144
+ ]
145
+ },
146
+ {
147
+ "matcher": "mcp__claude_ai_Atlassian__transitionJiraIssue",
148
+ "hooks": [
149
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
150
+ ]
151
+ },
152
+ {
153
+ "matcher": "mcp__claude_ai_Atlassian__addCommentToJiraIssue",
154
+ "hooks": [
155
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
156
+ ]
157
+ },
158
+ {
159
+ "matcher": "mcp__claude_ai_Atlassian__getConfluencePage",
160
+ "hooks": [
161
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
162
+ ]
163
+ },
164
+ {
165
+ "matcher": "mcp__claude_ai_Atlassian__createConfluencePage",
166
+ "hooks": [
167
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
168
+ ]
169
+ },
170
+ {
171
+ "matcher": "mcp__claude_ai_Atlassian__updateConfluencePage",
172
+ "hooks": [
173
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
174
+ ]
175
+ },
176
+ {
177
+ "matcher": "mcp__claude_ai_Atlassian__searchConfluenceUsingCql",
178
+ "hooks": [
179
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
180
+ ]
181
+ },
182
+ {
183
+ "matcher": "mcp__claude_ai_Atlassian__getAccessibleAtlassianResources",
184
+ "hooks": [
185
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
186
+ ]
187
+ },
188
+ {
189
+ "matcher": "mcp__claude_ai_Atlassian__getTransitionsForJiraIssue",
190
+ "hooks": [
191
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/atlassian_prereqs.py" }
192
+ ]
193
+ }
194
+ ],
195
+ "PostToolUse": [
196
+ {
197
+ "matcher": "Edit",
198
+ "hooks": [
199
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/memory_store_reminder.py" },
200
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/wp_security_check.py" },
201
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/sql_injection_check.py" },
202
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/fp_utility_check.py" },
203
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/jquery_in_wordpress.py" },
204
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/bootstrap_utility_check.py" },
205
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/composer_autoload_check.py" }
206
+ ]
207
+ },
208
+ {
209
+ "matcher": "Write",
210
+ "hooks": [
211
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/memory_store_reminder.py" },
212
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/wp_security_check.py" },
213
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/sql_injection_check.py" },
214
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/fp_utility_check.py" },
215
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/jquery_in_wordpress.py" },
216
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/bootstrap_utility_check.py" },
217
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/composer_autoload_check.py" },
218
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/docs_organization.py" }
219
+ ]
220
+ },
221
+ {
222
+ "matcher": "ExitPlanMode",
223
+ "hooks": [
224
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/task_master_after_plan.py" }
225
+ ]
226
+ }
227
+ ],
228
+ "UserPromptSubmit": [
229
+ {
230
+ "hooks": [
231
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/prompt_coach.py" },
232
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/jira_issue_fetch.py" },
233
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/task_master_before_impl.py" },
234
+ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/sequential_thinking_check.py" }
235
+ ]
236
+ }
237
+ ]
238
+ }
239
+ }
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ UserPromptSubmit hook: Remind Claude to fetch Jira issue context when a key is detected.
4
+
5
+ M6 — Auto-fetch Jira issue when key detected in prompt.
6
+
7
+ Scans the user prompt for Jira issue key patterns (e.g., FNR-123, IMA-456).
8
+ Prints a reminder to fetch the issue via Atlassian MCP for the first new key seen.
9
+ Tracks seen keys in ~/.claude/.jira_keys_fetched to avoid repeat reminders.
10
+ Exit code 0 = soft warning via stderr.
11
+ """
12
+ import json
13
+ import os
14
+ import re
15
+ import sys
16
+ import time
17
+
18
+ STATE_FILE = os.path.expanduser("~/.claude/.jira_keys_fetched")
19
+ STALENESS_SECONDS = 3600 # 1 hour — reset seen keys after this gap
20
+
21
+ JIRA_KEY_PATTERN = re.compile(r"\b[A-Z]{2,10}-\d+\b")
22
+
23
+ CLOUD_ID = "<cloudId>"
24
+
25
+
26
+ def load_seen_keys() -> set[str]:
27
+ """Load previously seen Jira keys from state file, if still fresh."""
28
+ if not os.path.exists(STATE_FILE):
29
+ return set()
30
+ try:
31
+ mtime = os.path.getmtime(STATE_FILE)
32
+ if (time.time() - mtime) >= STALENESS_SECONDS:
33
+ return set()
34
+ with open(STATE_FILE, "r") as f:
35
+ return set(line.strip() for line in f if line.strip())
36
+ except OSError:
37
+ return set()
38
+
39
+
40
+ def save_seen_keys(keys: set[str]) -> None:
41
+ """Persist the set of seen Jira keys to the state file."""
42
+ state_dir = os.path.dirname(STATE_FILE)
43
+ os.makedirs(state_dir, exist_ok=True)
44
+ with open(STATE_FILE, "w") as f:
45
+ f.write("\n".join(sorted(keys)))
46
+
47
+
48
+ try:
49
+ input_data = json.load(sys.stdin)
50
+ except json.JSONDecodeError:
51
+ sys.exit(0)
52
+
53
+ prompt = input_data.get("user_prompt", "")
54
+
55
+ if not prompt:
56
+ sys.exit(0)
57
+
58
+ matches = JIRA_KEY_PATTERN.findall(prompt)
59
+
60
+ if not matches:
61
+ sys.exit(0)
62
+
63
+ first_key = matches[0]
64
+
65
+ seen_keys = load_seen_keys()
66
+
67
+ if first_key in seen_keys:
68
+ sys.exit(0)
69
+
70
+ seen_keys.add(first_key)
71
+ save_seen_keys(seen_keys)
72
+
73
+ print(
74
+ f'Jira issue key {first_key} detected — consider fetching context:\n'
75
+ f' mcp__claude_ai_Atlassian__getJiraIssue issueIdOrKey: "{first_key}" cloudId: "{CLOUD_ID}"',
76
+ file=sys.stderr,
77
+ )
78
+
79
+ sys.exit(0)
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook: Warn about vanilla DOM JS in WordPress context.
4
+
5
+ Checks (soft warning only, exit 0):
6
+ M2 — querySelector/addEventListener/etc. when jQuery is already loaded
7
+
8
+ WordPress context is detected via file path (wp-content/plugins, wp-content/themes)
9
+ or file content (jQuery signals).
10
+
11
+ Applies to: Edit, Write on .js files only.
12
+ """
13
+ import json
14
+ import re
15
+ import sys
16
+
17
+ WP_PATH_SIGNALS = ("wp-content/plugins/", "wp-content/themes/")
18
+
19
+ WP_CONTENT_SIGNALS = re.compile(
20
+ r"jQuery\s*[\(\.]|"
21
+ r"\(function\s*\(\$\)|"
22
+ r"\$\s*\(document\)|"
23
+ r"\bwp\."
24
+ )
25
+
26
+ VANILLA_DOM_PATTERNS = re.compile(
27
+ r"document\.querySelectorAll\s*\(|"
28
+ r"document\.querySelector\s*\(|"
29
+ r"document\.getElementById\s*\(|"
30
+ r"document\.getElementsByClassName\s*\(|"
31
+ r"\.addEventListener\s*\(|"
32
+ r"document\.createElement\s*\("
33
+ )
34
+
35
+ WARNING = (
36
+ "⚠️ Vanilla DOM JS in WordPress context — jQuery is already loaded (0 extra bytes).\n"
37
+ " document.querySelector('.x') → $('.x')\n"
38
+ " el.addEventListener('click') → $(el).on('click')\n"
39
+ " See /jquery skill for FP-aligned patterns."
40
+ )
41
+
42
+
43
+ def is_wordpress_path(file_path: str) -> bool:
44
+ return any(signal in file_path for signal in WP_PATH_SIGNALS)
45
+
46
+
47
+ def is_wordpress_content(content: str) -> bool:
48
+ return bool(WP_CONTENT_SIGNALS.search(content))
49
+
50
+
51
+ def has_vanilla_dom(content: str) -> bool:
52
+ return bool(VANILLA_DOM_PATTERNS.search(content))
53
+
54
+
55
+ def read_file(file_path: str) -> str:
56
+ try:
57
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
58
+ return f.read()
59
+ except OSError:
60
+ return ""
61
+
62
+
63
+ try:
64
+ input_data = json.load(sys.stdin)
65
+ except json.JSONDecodeError:
66
+ sys.exit(0)
67
+
68
+ tool_name = input_data.get("tool_name", "")
69
+ tool_input = input_data.get("tool_input", {})
70
+ file_path = tool_input.get("file_path", "")
71
+
72
+ if tool_name not in ("Edit", "Write"):
73
+ sys.exit(0)
74
+
75
+ if not file_path.endswith(".js"):
76
+ sys.exit(0)
77
+
78
+ if tool_name == "Write":
79
+ written_content = tool_input.get("content", "")
80
+ wp_context = is_wordpress_path(file_path) or is_wordpress_content(written_content)
81
+ vanilla_found = has_vanilla_dom(written_content)
82
+ else:
83
+ # Edit: check path for WP context; read disk file for content signals
84
+ new_string = tool_input.get("new_string", "")
85
+ disk_content = read_file(file_path)
86
+ wp_context = is_wordpress_path(file_path) or is_wordpress_content(disk_content)
87
+ vanilla_found = has_vanilla_dom(new_string)
88
+
89
+ if wp_context and vanilla_found:
90
+ print(WARNING, file=sys.stderr)
91
+
92
+ sys.exit(0)
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PreToolUse hook: Remind Claude to search Vestige + Qdrant before starting work.
4
+
5
+ On the first non-memory tool use per session, prints a reminder to stderr.
6
+ Uses a timestamp-based state file to avoid repeating within the same session.
7
+ Exit code 0 = allow tool to proceed (soft warning only).
8
+ """
9
+ import json
10
+ import os
11
+ import sys
12
+ import time
13
+
14
+ STATE_FILE = os.path.expanduser("~/.claude/.memory_bootstrapped")
15
+ STALENESS_SECONDS = 3600 # 1 hour — new session after this gap
16
+
17
+ MEMORY_TOOLS = {
18
+ "mcp__vestige__search",
19
+ "mcp__vestige__smart_ingest",
20
+ "mcp__vestige__ingest",
21
+ "mcp__vestige__memory",
22
+ "mcp__vestige__intention",
23
+ "mcp__vestige__codebase",
24
+ "mcp__vestige__promote_memory",
25
+ "mcp__vestige__demote_memory",
26
+ "mcp__vestige__session_checkpoint",
27
+ "mcp__qdrant-memory__qdrant-find",
28
+ "mcp__qdrant-memory__qdrant-store",
29
+ "mcp__serena__read_memory",
30
+ "mcp__serena__list_memories",
31
+ "mcp__serena__write_memory",
32
+ }
33
+
34
+ REMINDER = """Memory bootstrap: Search Vestige and Qdrant before starting work.
35
+ mcp__vestige__search query: "{project}" limit: 5
36
+ mcp__qdrant-memory__qdrant-find query: "{project}"
37
+ mcp__vestige__intention action: "check"
38
+ """
39
+
40
+
41
+ def is_bootstrapped():
42
+ """Check if memory bootstrap already happened this session."""
43
+ if not os.path.exists(STATE_FILE):
44
+ return False
45
+ try:
46
+ mtime = os.path.getmtime(STATE_FILE)
47
+ return (time.time() - mtime) < STALENESS_SECONDS
48
+ except OSError:
49
+ return False
50
+
51
+
52
+ def mark_bootstrapped():
53
+ """Mark that bootstrap reminder has fired."""
54
+ state_dir = os.path.dirname(STATE_FILE)
55
+ os.makedirs(state_dir, exist_ok=True)
56
+ with open(STATE_FILE, "w") as f:
57
+ f.write(str(time.time()))
58
+
59
+
60
+ try:
61
+ input_data = json.load(sys.stdin)
62
+ except json.JSONDecodeError:
63
+ sys.exit(0)
64
+
65
+ tool_name = input_data.get("tool_name", "")
66
+
67
+ # If this IS a memory tool, mark as bootstrapped and exit silently
68
+ if tool_name in MEMORY_TOOLS:
69
+ mark_bootstrapped()
70
+ sys.exit(0)
71
+
72
+ # If already bootstrapped this session, exit silently
73
+ if is_bootstrapped():
74
+ sys.exit(0)
75
+
76
+ # First non-memory tool use — print reminder and mark bootstrapped
77
+ print(REMINDER, file=sys.stderr)
78
+ mark_bootstrapped()
79
+ sys.exit(0)
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook: Nudge Claude to store decisions/patterns after several edits.
4
+
5
+ After every 5th Edit/Write without a Vestige or Qdrant store, prints a gentle reminder.
6
+ Counter resets when a memory store is detected or after the reminder fires.
7
+ Exit code 0 = allow tool to proceed (soft warning only).
8
+ """
9
+ import json
10
+ import os
11
+ import sys
12
+
13
+ STATE_FILE = os.path.expanduser("~/.claude/.memory_edit_count")
14
+ EDIT_THRESHOLD = 5
15
+
16
+ MEMORY_STORE_TOOLS = {
17
+ "mcp__vestige__smart_ingest",
18
+ "mcp__vestige__ingest",
19
+ "mcp__vestige__codebase",
20
+ "mcp__vestige__session_checkpoint",
21
+ "mcp__qdrant-memory__qdrant-store",
22
+ "mcp__serena__write_memory",
23
+ }
24
+
25
+ REMINDER = """You've made several changes this session. Any decisions or patterns worth storing?
26
+ → Vestige smart_ingest for decisions/patterns (neural, fades if unused)
27
+ → Qdrant qdrant-store for reference material (permanent library)
28
+ """
29
+
30
+
31
+ def get_edit_count():
32
+ """Read current edit count from state file."""
33
+ if not os.path.exists(STATE_FILE):
34
+ return 0
35
+ try:
36
+ with open(STATE_FILE, "r") as f:
37
+ return int(f.read().strip())
38
+ except (ValueError, OSError):
39
+ return 0
40
+
41
+
42
+ def set_edit_count(count):
43
+ """Write edit count to state file."""
44
+ state_dir = os.path.dirname(STATE_FILE)
45
+ os.makedirs(state_dir, exist_ok=True)
46
+ with open(STATE_FILE, "w") as f:
47
+ f.write(str(count))
48
+
49
+
50
+ try:
51
+ input_data = json.load(sys.stdin)
52
+ except json.JSONDecodeError:
53
+ sys.exit(0)
54
+
55
+ tool_name = input_data.get("tool_name", "")
56
+
57
+ # If a memory store just happened, reset counter
58
+ if tool_name in MEMORY_STORE_TOOLS:
59
+ set_edit_count(0)
60
+ sys.exit(0)
61
+
62
+ # Only count Edit and Write tools
63
+ if tool_name not in ("Edit", "Write"):
64
+ sys.exit(0)
65
+
66
+ # Increment edit count
67
+ count = get_edit_count() + 1
68
+
69
+ if count >= EDIT_THRESHOLD:
70
+ print(REMINDER, file=sys.stderr)
71
+ set_edit_count(0) # Reset after reminding
72
+ else:
73
+ set_edit_count(count)
74
+
75
+ sys.exit(0)