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,125 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ UserPromptSubmit hook: Evaluate prompts with Haiku for team standards.
4
+ Provides feedback inline when valuable, stays silent otherwise.
5
+
6
+ Requirements:
7
+ ANTHROPIC_API_KEY env var (same key used for Claude Code)
8
+ pip install anthropic
9
+
10
+ Environment variables:
11
+ PROMPT_COACH_ENABLED=true - Enable evaluation
12
+ PROMPT_COACH_LOG=true - Log prompts + feedback to ~/.claude/prompt_coach.log
13
+ """
14
+ import json
15
+ import os
16
+ import sys
17
+ from datetime import datetime
18
+ from pathlib import Path
19
+
20
+ # Only run if enabled
21
+ if os.environ.get("PROMPT_COACH_ENABLED", "").lower() != "true":
22
+ sys.exit(0)
23
+
24
+ # Common skip patterns (case-insensitive)
25
+ SKIP_PATTERNS = {
26
+ "yes", "no", "y", "n", "ok", "okay", "continue", "proceed",
27
+ "do it", "looks good", "go ahead", "sure", "thanks", "thank you",
28
+ "got it", "understood", "perfect", "great", "good", "nice",
29
+ "yep", "nope", "yup", "done", "next", "stop", "cancel", "abort",
30
+ }
31
+
32
+
33
+ def should_skip(prompt: str) -> bool:
34
+ """Skip short prompts and common follow-ups."""
35
+ cleaned = prompt.strip().lower()
36
+ if len(cleaned) < 20:
37
+ return True
38
+ if cleaned in SKIP_PATTERNS:
39
+ return True
40
+ return False
41
+
42
+
43
+ def log_feedback(prompt: str, feedback: str) -> None:
44
+ """Optionally log to file for review."""
45
+ if os.environ.get("PROMPT_COACH_LOG", "").lower() != "true":
46
+ return
47
+ log_path = Path.home() / ".claude" / "prompt_coach.log"
48
+ timestamp = datetime.now().isoformat()
49
+ try:
50
+ with open(log_path, "a") as f:
51
+ f.write(f"\n--- {timestamp} ---\n")
52
+ f.write(f"PROMPT: {prompt[:200]}{'...' if len(prompt) > 200 else ''}\n")
53
+ f.write(f"FEEDBACK: {feedback}\n")
54
+ except Exception:
55
+ pass # Don't fail on logging errors
56
+
57
+
58
+ def main() -> None:
59
+ try:
60
+ data = json.load(sys.stdin)
61
+ except json.JSONDecodeError:
62
+ sys.exit(0)
63
+
64
+ # Handle both possible input formats
65
+ # UserPromptSubmit sends: {"prompt": "..."}
66
+ prompt = data.get("prompt", "")
67
+
68
+ if not prompt or should_skip(prompt):
69
+ sys.exit(0)
70
+
71
+ # Load system prompt and skills digest from adjacent files
72
+ hooks_dir = Path(__file__).parent
73
+ system_prompt_path = hooks_dir / "prompt_coach_system.md"
74
+ digest_path = hooks_dir / "prompt_coach_digest.md"
75
+
76
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
77
+ if not api_key:
78
+ sys.exit(0)
79
+
80
+ model = "claude-haiku-4-5-20251001"
81
+
82
+ try:
83
+ system_prompt = system_prompt_path.read_text()
84
+ skills_digest = digest_path.read_text()
85
+ except FileNotFoundError as e:
86
+ log_feedback(prompt, f"[error: {e.filename} not found]")
87
+ sys.exit(0)
88
+
89
+ # Combine system prompt with skills digest
90
+ full_system = f"{system_prompt}\n\n---\n\n# SKILLS DIGEST\n\n{skills_digest}"
91
+
92
+ # Call Haiku API
93
+ try:
94
+ import anthropic
95
+ client = anthropic.Anthropic(api_key=api_key)
96
+
97
+ response = client.messages.create(
98
+ model=model,
99
+ max_tokens=300,
100
+ system=full_system,
101
+ messages=[{"role": "user", "content": f"USER PROMPT TO EVALUATE:\n\n{prompt}"}]
102
+ )
103
+
104
+ feedback = response.content[0].text.strip()
105
+
106
+ # Check if Haiku decided to stay silent
107
+ if feedback and feedback != "NO_FEEDBACK":
108
+ print("📋 Prompt Coach:", file=sys.stderr)
109
+ print(feedback, file=sys.stderr)
110
+ print("---", file=sys.stderr)
111
+ log_feedback(prompt, feedback)
112
+ else:
113
+ log_feedback(prompt, "[silent - no issues]")
114
+
115
+ except ImportError:
116
+ log_feedback(prompt, "[error: anthropic package not installed]")
117
+ except Exception as e:
118
+ # Fail silently - don't block on errors
119
+ log_feedback(prompt, f"[error: {e}]")
120
+
121
+ sys.exit(0)
122
+
123
+
124
+ if __name__ == "__main__":
125
+ main()
@@ -0,0 +1,48 @@
1
+ # IMA Skills Digest for Prompt Coaching
2
+
3
+ ## Skill Triggers (suggest when prompt matches)
4
+
5
+ | Prompt Contains | Suggest |
6
+ |----------------|---------|
7
+ | React, hooks, useCallback, component optimization | `js-fp-react` |
8
+ | Vue 3, composable, Composition API | `js-fp-vue` |
9
+ | Quasar, QBtn, QCard, q-pa-, utility classes | `quasar-fp` |
10
+ | Node.js API, REST, middleware, route handlers | `js-fp-api` |
11
+ | WordPress JavaScript, jQuery, AJAX, Gravity Forms | `js-fp-wordpress` |
12
+ | PHP function, pure functions, composition | `php-fp` |
13
+ | WordPress plugin, nonce, sanitization, capability | `php-fp-wordpress` |
14
+ | Architecture, new project, scaling, microservices | `architect` |
15
+ | Documentation structure, organize docs | `docs-organize` |
16
+ | WP-CLI, Local WP, wp plugin, wp db query | `wp-local` |
17
+ | Find in files, search code, grep | `rg` |
18
+ | Latest, current, 2025/2026, recent updates, research | `mcp-tavily` |
19
+ | Library docs, React API, how to use [library] | `mcp-context7` |
20
+ | Find references, rename symbol, refactor, what calls X | `mcp-serena` |
21
+ | Think through, step by step, debug, complex problem | `mcp-sequential` |
22
+ | Remember this, save preference, architectural decision | `mcp-memory` |
23
+ | IMA Forms, form validation, repeater field | `ima-forms-expert` |
24
+ | Analyze skill, audit skill, skill review | `skill-analyzer` |
25
+ | Create skill, build skill, skill template | `skill-creator` |
26
+
27
+ ## Core Anti-Patterns (flag these)
28
+
29
+ **FP Utilities**: Creating custom pipe/compose/curry/partial - use native patterns
30
+ **Over-Engineering**: "make it generic", "add wrapper", "create utility" without evidence
31
+ **Premature Abstraction**: Extracting helpers before 3+ genuine reuses
32
+ **Security Gaps**: Raw SQL, missing nonces (WP), unsanitized input, hardcoded secrets
33
+ **Wrong Tool**: grep instead of rg, WebSearch instead of mcp-tavily, reading files instead of mcp-serena
34
+
35
+ ## Team Philosophy
36
+
37
+ - Simple > Complex
38
+ - Evidence > Assumptions
39
+ - Specific > Generic (generalize with evidence)
40
+ - Add complexity only when proven needed
41
+
42
+ ## When Skills Already Apply (stay silent)
43
+
44
+ - Prompt mentions a skill by name
45
+ - Prompt has clear, specific requirements
46
+ - Bug fix with reproduction steps
47
+ - Exploring/reading code without modification
48
+ - Simple follow-ups
@@ -0,0 +1,30 @@
1
+ You are a prompt coach for a development team using Claude Code with custom skills. Analyze prompts and provide brief, actionable feedback.
2
+
3
+ You will receive:
4
+ 1. A SKILLS DIGEST (skill triggers + anti-patterns)
5
+ 2. The USER PROMPT to evaluate
6
+
7
+ ## Your Task
8
+
9
+ 1. Check if prompt would benefit from a skill (use digest triggers)
10
+ 2. Flag anti-patterns (custom FP utilities, over-engineering, security gaps)
11
+ 3. Note vague requirements that need specifics
12
+
13
+ ## Output Rules
14
+
15
+ **If feedback is valuable**: 2-3 bullet points max, one line each
16
+ ```
17
+ • Consider: [skill-name] for [reason]
18
+ • [Anti-pattern]: [brief suggestion]
19
+ ```
20
+
21
+ **If no feedback needed**: Respond with exactly: `NO_FEEDBACK`
22
+
23
+ ## Stay Silent When
24
+
25
+ - Prompt already mentions a relevant skill
26
+ - Clear, specific requirements
27
+ - Simple follow-ups (yes, continue, do it)
28
+ - Questions about codebase (where is X?)
29
+ - Reading/exploring without modification intent
30
+ - No actionable improvement to suggest
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ UserPromptSubmit hook: Remind Claude to use Sequential Thinking for complex reasoning tasks.
4
+
5
+ Sequential Thinking prevents costly back-and-forth by structuring reasoning upfront.
6
+ Fires when the prompt contains debugging, analysis, architecture, or trade-off language.
7
+
8
+ Fires once per session — it's a setup reminder, not a per-action nudge.
9
+ Exit code 0 = soft warning via stderr.
10
+ """
11
+ import json
12
+ import os
13
+ import re
14
+ import sys
15
+ import time
16
+
17
+ STATE_FILE = os.path.expanduser("~/.claude/.sequential_reminded")
18
+ STALENESS_SECONDS = 3600
19
+
20
+ INVESTIGATION_SIGNALS = re.compile(
21
+ r"\b(debug|diagnos|investigat|figure out|why (is|does|isn't|doesn't|won't)|"
22
+ r"not working|broken|fail|error|exception|unexpected|strange|weird|"
23
+ r"trace|root cause|hunt down|track down)\b",
24
+ re.IGNORECASE,
25
+ )
26
+
27
+ ANALYSIS_SIGNALS = re.compile(
28
+ r"\b(analyz|trade.?off|compare|evaluate|weigh|pros.?and.?cons|"
29
+ r"best (way|approach|option|pattern)|should (we|I)|"
30
+ r"architect|design|plan|how (should|would|do) (we|I))\b",
31
+ re.IGNORECASE,
32
+ )
33
+
34
+ REMINDER = """Complex reasoning detected — Sequential Thinking structures the analysis before acting:
35
+ mcp__sequential-thinking__sequentialthinking
36
+ thought: "Step 1: ..." thoughtNumber: 1 totalThoughts: 5 nextThoughtNeeded: true
37
+ Useful for: debugging root causes, evaluating trade-offs, architecture decisions,
38
+ multi-step investigations where the approach may need to change mid-stream.
39
+ Prevents expensive trial-and-error by thinking it through first."""
40
+
41
+
42
+ def is_reminded() -> bool:
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_reminded() -> None:
53
+ os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
54
+ with open(STATE_FILE, "w") as f:
55
+ f.write(str(time.time()))
56
+
57
+
58
+ try:
59
+ input_data = json.load(sys.stdin)
60
+ except json.JSONDecodeError:
61
+ sys.exit(0)
62
+
63
+ prompt = input_data.get("user_prompt", "").strip()
64
+ if not prompt or len(prompt.split()) < 10:
65
+ sys.exit(0)
66
+
67
+ if prompt.startswith("/"):
68
+ sys.exit(0)
69
+
70
+ if is_reminded():
71
+ sys.exit(0)
72
+
73
+ has_investigation = INVESTIGATION_SIGNALS.search(prompt)
74
+ has_analysis = ANALYSIS_SIGNALS.search(prompt)
75
+
76
+ if not (has_investigation or has_analysis):
77
+ sys.exit(0)
78
+
79
+ mark_reminded()
80
+ print(REMINDER, file=sys.stderr)
81
+ sys.exit(0)
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PreToolUse hook: Nudge toward Serena symbol tools when Grep is used for code navigation.
4
+
5
+ When Claude uses Grep with patterns that look like symbol searches (class definitions,
6
+ function references, import tracing), suggest Serena's symbol tools instead — they're
7
+ more precise, understand scope, and save tokens.
8
+
9
+ Fires on every other symbol-like Grep (counter-based) to stay persistent without
10
+ being noise on every single search.
11
+ Exit code 0 = soft warning via stderr.
12
+ """
13
+ import json
14
+ import os
15
+ import re
16
+ import sys
17
+ import time
18
+
19
+ STATE_FILE = os.path.expanduser("~/.claude/.serena_grep_count")
20
+ STALENESS_SECONDS = 3600
21
+ FIRE_EVERY_N = 2 # Remind on every Nth symbol-like grep
22
+
23
+ SYMBOL_PATTERNS = [
24
+ r"^(class|interface|trait|enum|abstract\s+class)\s+\w+",
25
+ r"^(function|def|fn)\s+\w+",
26
+ r"^(extends|implements)\s+\w+",
27
+ r"^(use|import|require|from)\s+",
28
+ r"->(\w+)\(", # method call
29
+ r"::\w+\(", # static method call
30
+ r"new\s+\w+", # constructor
31
+ r"\bfunction_exists\(",
32
+ r"\bclass_exists\(",
33
+ ]
34
+
35
+ COMPILED_PATTERNS = [re.compile(p) for p in SYMBOL_PATTERNS]
36
+
37
+ REMINDER = """Serena finds symbols without scanning file contents — much cheaper than Grep:
38
+ mcp__serena__jet_brains_find_symbol name_path_pattern: "{symbol}"
39
+ → exact match, understands scope and inheritance
40
+ mcp__serena__jet_brains_find_referencing_symbols name_path: "{symbol}" relative_path: "{file}"
41
+ → every caller across the codebase
42
+ mcp__serena__jet_brains_get_symbols_overview relative_path: "{file}"
43
+ → all symbols in a file without reading it
44
+ Grep for text search. Serena for symbol search. 40-70% token savings."""
45
+
46
+
47
+ def get_count() -> int:
48
+ if not os.path.exists(STATE_FILE):
49
+ return 0
50
+ try:
51
+ with open(STATE_FILE) as f:
52
+ data = json.load(f)
53
+ if time.time() - data.get("ts", 0) > STALENESS_SECONDS:
54
+ return 0
55
+ return data.get("count", 0)
56
+ except (json.JSONDecodeError, OSError):
57
+ return 0
58
+
59
+
60
+ def increment_count(current: int) -> None:
61
+ os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
62
+ with open(STATE_FILE, "w") as f:
63
+ json.dump({"count": current + 1, "ts": time.time()}, f)
64
+
65
+
66
+ try:
67
+ input_data = json.load(sys.stdin)
68
+ except json.JSONDecodeError:
69
+ sys.exit(0)
70
+
71
+ tool_name = input_data.get("tool_name", "")
72
+ if tool_name != "Grep":
73
+ sys.exit(0)
74
+
75
+ pattern = input_data.get("tool_input", {}).get("pattern", "")
76
+ if not pattern:
77
+ sys.exit(0)
78
+
79
+ is_symbol_search = any(cp.search(pattern) for cp in COMPILED_PATTERNS)
80
+ if not is_symbol_search:
81
+ sys.exit(0)
82
+
83
+ count = get_count()
84
+ increment_count(count)
85
+
86
+ # Fire on every Nth symbol grep (0-indexed: fire when count % N == 0)
87
+ if count % FIRE_EVERY_N != 0:
88
+ sys.exit(0)
89
+
90
+ symbol_match = re.search(r"\b([A-Z]\w+|\w+Service|\w+Controller|\w+Handler|\w+Repository)\b", pattern)
91
+ symbol = symbol_match.group(1) if symbol_match else "SymbolName"
92
+
93
+ file_hint = input_data.get("tool_input", {}).get("path", "path/to/file")
94
+
95
+ print(REMINDER.replace("{symbol}", symbol).replace("{file}", str(file_hint)), file=sys.stderr)
96
+ sys.exit(0)
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PreToolUse hook: Suggest Serena symbol tools before reading code files.
4
+
5
+ Reading an entire file to understand its structure costs 2-10x more tokens than
6
+ using Serena's symbol overview. This hook fires on every code file Read and
7
+ reminds Claude to try the cheaper path first.
8
+
9
+ Fires on every code file read — not rate-limited, because the savings compound.
10
+ Skip list: config files, markdown, json, yaml, lock files, env files.
11
+ Exit code 0 = soft warning via stderr.
12
+ """
13
+ import json
14
+ import os
15
+ import sys
16
+
17
+ CODE_EXTENSIONS = {
18
+ ".php", ".ts", ".tsx", ".js", ".jsx", ".py",
19
+ ".rb", ".go", ".java", ".cs", ".vue", ".svelte",
20
+ ".rs", ".cpp", ".c", ".h",
21
+ }
22
+
23
+ SKIP_EXTENSIONS = {
24
+ ".md", ".json", ".yaml", ".yml", ".lock", ".env",
25
+ ".txt", ".csv", ".xml", ".html", ".css", ".scss",
26
+ ".toml", ".ini", ".cfg", ".conf", ".sh", ".bash",
27
+ }
28
+
29
+ REMINDER = """Serena symbol tools cost 40-70% fewer tokens than reading files:
30
+ mcp__serena__jet_brains_get_symbols_overview relative_path: "{file}"
31
+ → structure of the file (classes, methods, properties) without reading it
32
+ mcp__serena__jet_brains_find_symbol name_path_pattern: "ClassName"
33
+ → find any symbol across the codebase instantly
34
+ mcp__serena__jet_brains_find_referencing_symbols name_path: "method" relative_path: "{file}"
35
+ → find every caller without grep
36
+ Use Read only when you need the full implementation body of a specific symbol."""
37
+
38
+
39
+ try:
40
+ input_data = json.load(sys.stdin)
41
+ except json.JSONDecodeError:
42
+ sys.exit(0)
43
+
44
+ tool_name = input_data.get("tool_name", "")
45
+ if tool_name != "Read":
46
+ sys.exit(0)
47
+
48
+ file_path = input_data.get("tool_input", {}).get("file_path", "")
49
+ if not file_path:
50
+ sys.exit(0)
51
+
52
+ _, ext = os.path.splitext(file_path.lower())
53
+
54
+ # Only fire for code files
55
+ if ext in SKIP_EXTENSIONS or ext not in CODE_EXTENSIONS:
56
+ sys.exit(0)
57
+
58
+ # Skip very small files (probably configs or stubs) — threshold 5KB
59
+ try:
60
+ if os.path.getsize(file_path) < 5000:
61
+ sys.exit(0)
62
+ except OSError:
63
+ pass # File doesn't exist yet or can't stat — proceed
64
+
65
+ print(REMINDER.replace("{file}", file_path), file=sys.stderr)
66
+ sys.exit(0)
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PreToolUse hook: Ensure Serena is using the right project, especially in WP plugin subdirs.
4
+
5
+ Problem: Serena uses --project-from-cwd, so when Claude Code runs from a plugin
6
+ subdirectory (wp-content/plugins/my-plugin/), Serena can't find the .serena/ config
7
+ at the WordPress root. PHPStorm has the parent WP project open, not the plugin.
8
+
9
+ Fix: When any mcp__serena__jet_brains_* tool is called, check if we're inside a
10
+ wp-content/plugins/ or wp-content/themes/ path. If so, remind Claude that the
11
+ Serena project is the parent WordPress root and may need explicit activation.
12
+
13
+ Also tracks whether Serena has been used this session. If not, and Claude is doing
14
+ code navigation via Grep with symbol-like patterns, nudges toward Serena.
15
+
16
+ Exit code 0 = soft warning via stderr.
17
+ """
18
+ import json
19
+ import os
20
+ import sys
21
+ import time
22
+
23
+ STATE_FILE = os.path.expanduser("~/.claude/.serena_session_state")
24
+ STALENESS_SECONDS = 3600
25
+
26
+
27
+ def get_state():
28
+ """Read session state."""
29
+ if not os.path.exists(STATE_FILE):
30
+ return {"activated": False, "warned_wp": False, "timestamp": 0}
31
+ try:
32
+ with open(STATE_FILE, "r") as f:
33
+ state = json.load(f)
34
+ if time.time() - state.get("timestamp", 0) > STALENESS_SECONDS:
35
+ return {"activated": False, "warned_wp": False, "timestamp": 0}
36
+ return state
37
+ except (json.JSONDecodeError, OSError):
38
+ return {"activated": False, "warned_wp": False, "timestamp": 0}
39
+
40
+
41
+ def save_state(state):
42
+ """Write session state."""
43
+ state["timestamp"] = time.time()
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
+ json.dump(state, f)
48
+
49
+
50
+ def find_wp_root(cwd):
51
+ """Walk up from cwd to find a WordPress root (contains wp-content/)."""
52
+ path = cwd
53
+ for _ in range(10): # max 10 levels up
54
+ if os.path.isdir(os.path.join(path, "wp-content")):
55
+ return path
56
+ parent = os.path.dirname(path)
57
+ if parent == path:
58
+ break
59
+ path = parent
60
+ return None
61
+
62
+
63
+ def find_serena_project(cwd):
64
+ """Walk up from cwd to find nearest .serena/project.yml."""
65
+ path = cwd
66
+ for _ in range(10):
67
+ project_yml = os.path.join(path, ".serena", "project.yml")
68
+ if os.path.exists(project_yml):
69
+ return path
70
+ parent = os.path.dirname(path)
71
+ if parent == path:
72
+ break
73
+ path = parent
74
+ return None
75
+
76
+
77
+ def get_project_name(serena_root):
78
+ """Extract project_name from .serena/project.yml."""
79
+ try:
80
+ project_yml = os.path.join(serena_root, ".serena", "project.yml")
81
+ with open(project_yml, "r") as f:
82
+ for line in f:
83
+ if line.strip().startswith("project_name:"):
84
+ name = line.split(":", 1)[1].strip().strip('"').strip("'")
85
+ if name:
86
+ return name
87
+ except OSError:
88
+ pass
89
+ return None
90
+
91
+
92
+ try:
93
+ input_data = json.load(sys.stdin)
94
+ except json.JSONDecodeError:
95
+ sys.exit(0)
96
+
97
+ tool_name = input_data.get("tool_name", "")
98
+ state = get_state()
99
+ cwd = os.getcwd()
100
+
101
+ # Track Serena usage
102
+ if tool_name.startswith("mcp__serena__"):
103
+ state["activated"] = True
104
+ save_state(state)
105
+
106
+ # Check 1: WP plugin subdirectory detection for Serena JetBrains tools
107
+ if tool_name.startswith("mcp__serena__jet_brains_") and not state.get("warned_wp"):
108
+ # Are we in a wp-content subdirectory?
109
+ in_wp_subdir = "wp-content/plugins/" in cwd or "wp-content/themes/" in cwd
110
+
111
+ if in_wp_subdir:
112
+ wp_root = find_wp_root(cwd)
113
+ serena_root = find_serena_project(cwd)
114
+
115
+ if serena_root and serena_root != cwd:
116
+ project_name = get_project_name(serena_root)
117
+ msg = (
118
+ f"Serena project is at the WordPress root, not this plugin directory.\n"
119
+ f" Project root: {serena_root}\n"
120
+ )
121
+ if project_name:
122
+ msg += f" Project name: {project_name}\n"
123
+ msg += (
124
+ f" Current dir: {cwd}\n"
125
+ f" If Serena tools fail, activate the project explicitly:\n"
126
+ f" mcp__serena__check_onboarding_performed\n"
127
+ f" Use relative_path from the WP root, not the plugin directory."
128
+ )
129
+ print(msg, file=sys.stderr)
130
+ state["warned_wp"] = True
131
+ save_state(state)
132
+
133
+ sys.exit(0)
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook: Warn about SQL string interpolation in JS/TS files.
4
+
5
+ Checks (soft warning only, exit 0):
6
+ H2 — Template literals or string concatenation building SQL queries with dynamic values.
7
+
8
+ Applies to: Edit, Write on .js, .ts, .mjs, .mts files.
9
+ """
10
+ import json
11
+ import re
12
+ import sys
13
+
14
+ JS_EXTENSIONS = (".js", ".ts", ".mjs", ".mts")
15
+
16
+ SQL_KEYWORDS = r"(?:SELECT|INSERT|UPDATE|DELETE|WHERE)"
17
+
18
+ # Template literal: `SELECT ... ${`
19
+ TEMPLATE_LITERAL_PATTERN = re.compile(
20
+ rf"`[^`]*\b{SQL_KEYWORDS}\b[^`]*\${{",
21
+ re.IGNORECASE | re.DOTALL,
22
+ )
23
+
24
+ # String concatenation: "SELECT..." + or 'SELECT...' +
25
+ STRING_CONCAT_PATTERN = re.compile(
26
+ rf"""(?:"|')[^"']*\b{SQL_KEYWORDS}\b[^"']*(?:"|')\s*\+""",
27
+ re.IGNORECASE,
28
+ )
29
+
30
+ WARNING = (
31
+ "⚠️ H2: SQL string interpolation detected — use parameterized queries instead.\n"
32
+ " WRONG: `SELECT * FROM users WHERE id = ${userId}`\n"
33
+ " RIGHT: { sql: 'SELECT * FROM users WHERE id = ?', params: [userId] }"
34
+ )
35
+
36
+
37
+ def has_sql_interpolation(content: str) -> bool:
38
+ return bool(
39
+ TEMPLATE_LITERAL_PATTERN.search(content)
40
+ or STRING_CONCAT_PATTERN.search(content)
41
+ )
42
+
43
+
44
+ def get_content(tool_name: str, tool_input: dict) -> str:
45
+ if tool_name == "Write":
46
+ return tool_input.get("content", "")
47
+ # Edit: scan only the new_string — no need to read the full file for SQL injection
48
+ return tool_input.get("new_string", "")
49
+
50
+
51
+ try:
52
+ input_data = json.load(sys.stdin)
53
+ except json.JSONDecodeError:
54
+ sys.exit(0)
55
+
56
+ tool_name = input_data.get("tool_name", "")
57
+ tool_input = input_data.get("tool_input", {})
58
+ file_path = tool_input.get("file_path", "")
59
+
60
+ if tool_name not in ("Edit", "Write"):
61
+ sys.exit(0)
62
+
63
+ if not file_path.endswith(JS_EXTENSIONS):
64
+ sys.exit(0)
65
+
66
+ content = get_content(tool_name, tool_input)
67
+ if not content:
68
+ sys.exit(0)
69
+
70
+ if has_sql_interpolation(content):
71
+ print(WARNING, file=sys.stderr)
72
+
73
+ sys.exit(0)
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook: After exiting plan mode, direct Claude to delegate tasks to agents.
4
+
5
+ Matcher: ExitPlanMode
6
+ Fires once per plan exit. The plan is already done — this hook pushes Claude
7
+ to delegate each task to subagents via the Task tool instead of implementing directly.
8
+ Does NOT re-invoke task-master (that would restart planning).
9
+ Exit code 0 = soft warning via stderr.
10
+ """
11
+ import json
12
+ import sys
13
+
14
+ REMINDER = """STOP. The plan is approved. Do NOT implement directly — DELEGATE.
15
+
16
+ Invoke /ima-claude:task-runner now to delegate each task to subagents.
17
+ You are the Orchestrator. You coordinate. Agents implement. Do NOT write code yourself.
18
+ """
19
+
20
+ try:
21
+ input_data = json.load(sys.stdin)
22
+ except json.JSONDecodeError:
23
+ sys.exit(0)
24
+
25
+ tool_name = input_data.get("tool_name", "")
26
+
27
+ if tool_name != "ExitPlanMode":
28
+ sys.exit(0)
29
+
30
+ print(REMINDER, file=sys.stderr)
31
+ sys.exit(0)