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.
- package/LICENSE +21 -0
- package/README.md +463 -0
- package/dist/cli.js +1064 -0
- package/package.json +49 -0
- package/platforms/claude/adapter.ts +115 -0
- package/platforms/junie/adapter.ts +254 -0
- package/platforms/junie/agents-template.md +113 -0
- package/platforms/junie/hook-translations.md +84 -0
- package/platforms/shared/detector.ts +27 -0
- package/platforms/shared/installer.ts +202 -0
- package/platforms/shared/types.ts +78 -0
- package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
- package/plugins/ima-claude/agents/explorer.md +30 -0
- package/plugins/ima-claude/agents/implementer.md +30 -0
- package/plugins/ima-claude/agents/memory.md +42 -0
- package/plugins/ima-claude/agents/reviewer.md +53 -0
- package/plugins/ima-claude/agents/tester.md +33 -0
- package/plugins/ima-claude/agents/wp-developer.md +46 -0
- package/plugins/ima-claude/hooks/README.md +145 -0
- package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
- package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
- package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
- package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
- package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
- package/plugins/ima-claude/hooks/docs_organization.py +104 -0
- package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
- package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
- package/plugins/ima-claude/hooks/hook_logger.py +69 -0
- package/plugins/ima-claude/hooks/hooks.json +239 -0
- package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
- package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
- package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
- package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
- package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
- package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
- package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
- package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
- package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
- package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
- package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
- package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
- package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
- package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
- package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
- package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
- package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
- package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
- package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
- package/plugins/ima-claude/personalities/README.md +45 -0
- package/plugins/ima-claude/personalities/enable-40k.md +69 -0
- package/plugins/ima-claude/personalities/enable-templars.md +69 -0
- package/plugins/ima-claude/skills/.research-summary.md +340 -0
- package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
- package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
- package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
- package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
- package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
- package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
- package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
- package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
- package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
- package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
- package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
- package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
- package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
- package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
- package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
- package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
- package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
- package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
- package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
- package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
- package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
- package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
- package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
- package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
- package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
- package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
- package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
- package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
- package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
- package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
- package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
- package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
- package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
- package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
- package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
- package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
- package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
- package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
- package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
- package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
- package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
- package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
- package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
- package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
- package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
- package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
- package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
- package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
- package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
- package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
- package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
- package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
- package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
- package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
- package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
- package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
- package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
- package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
- package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
- package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
- package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
- package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
- package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
- package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
- package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
- package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
- package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
- package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
- package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
- package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
- package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
- package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
- package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
- package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
- package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
- package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
- package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
- package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
- package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
- package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
- package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
- package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
- package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
- package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
- package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
- package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
- package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
- package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
- package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
- package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
- package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
- package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
- package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
- package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
- package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
- package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
- package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
UserPromptSubmit hook: Remind Claude to invoke task-master before non-trivial implementation.
|
|
4
|
+
|
|
5
|
+
M7 — Remind about task-master for non-trivial implementation requests.
|
|
6
|
+
|
|
7
|
+
Fires when the prompt contains both an action verb and a scope noun, is longer than
|
|
8
|
+
30 words, and does not contain trivial-signal words or a skill invocation prefix.
|
|
9
|
+
Fires once per session using a timestamp-based state file.
|
|
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/.task_master_reminded")
|
|
19
|
+
STALENESS_SECONDS = 3600 # 1 hour — new session after this gap
|
|
20
|
+
|
|
21
|
+
ACTION_VERBS = re.compile(
|
|
22
|
+
r"\b(implement|build|create|write|add|develop|make|set\s+up|refactor|migrate|convert)\b",
|
|
23
|
+
re.IGNORECASE,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
SCOPE_NOUNS = re.compile(
|
|
27
|
+
r"\b(feature|component|system|module|plugin|endpoint|service|page|form|api|hook"
|
|
28
|
+
r"|integration|workflow|authentication|dashboard)\b",
|
|
29
|
+
re.IGNORECASE,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
TRIVIAL_SIGNALS = re.compile(
|
|
33
|
+
r"\b(trivial|quick|simple|just|only)\b",
|
|
34
|
+
re.IGNORECASE,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
REMINDER = """Non-trivial implementation request detected — consider task-planner first:
|
|
38
|
+
/ima-claude:task-planner to decompose into Epic > Story > Task hierarchy and select storage.
|
|
39
|
+
Then /ima-claude:task-runner to delegate to agents. Opus orchestrates. Sonnet implements."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def is_reminded() -> bool:
|
|
43
|
+
"""Check if the task-master reminder already fired this session."""
|
|
44
|
+
if not os.path.exists(STATE_FILE):
|
|
45
|
+
return False
|
|
46
|
+
try:
|
|
47
|
+
mtime = os.path.getmtime(STATE_FILE)
|
|
48
|
+
return (time.time() - mtime) < STALENESS_SECONDS
|
|
49
|
+
except OSError:
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def mark_reminded() -> None:
|
|
54
|
+
"""Record that the reminder has fired."""
|
|
55
|
+
state_dir = os.path.dirname(STATE_FILE)
|
|
56
|
+
os.makedirs(state_dir, exist_ok=True)
|
|
57
|
+
with open(STATE_FILE, "w") as f:
|
|
58
|
+
f.write(str(time.time()))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
input_data = json.load(sys.stdin)
|
|
63
|
+
except json.JSONDecodeError:
|
|
64
|
+
sys.exit(0)
|
|
65
|
+
|
|
66
|
+
prompt = input_data.get("user_prompt", "").strip()
|
|
67
|
+
|
|
68
|
+
if not prompt:
|
|
69
|
+
sys.exit(0)
|
|
70
|
+
|
|
71
|
+
# Skip skill invocations
|
|
72
|
+
if prompt.startswith("/"):
|
|
73
|
+
sys.exit(0)
|
|
74
|
+
|
|
75
|
+
# Skip if trivial signals present
|
|
76
|
+
if TRIVIAL_SIGNALS.search(prompt):
|
|
77
|
+
sys.exit(0)
|
|
78
|
+
|
|
79
|
+
# Must be longer than 30 words
|
|
80
|
+
if len(prompt.split()) <= 30:
|
|
81
|
+
sys.exit(0)
|
|
82
|
+
|
|
83
|
+
# Must have both an action verb and a scope noun
|
|
84
|
+
if not ACTION_VERBS.search(prompt) or not SCOPE_NOUNS.search(prompt):
|
|
85
|
+
sys.exit(0)
|
|
86
|
+
|
|
87
|
+
# Only remind once per session
|
|
88
|
+
if is_reminded():
|
|
89
|
+
sys.exit(0)
|
|
90
|
+
|
|
91
|
+
mark_reminded()
|
|
92
|
+
print(REMINDER, file=sys.stderr)
|
|
93
|
+
sys.exit(0)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PreToolUse hook: Auto-upgrade Tavily extract to advanced mode for better results.
|
|
4
|
+
|
|
5
|
+
This hook intercepts tavily_extract calls and suggests using extract_depth: "advanced"
|
|
6
|
+
for LinkedIn, protected sites, or when tables/embedded content are needed.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
data = json.load(sys.stdin)
|
|
13
|
+
tool_name = data.get("tool_name", "")
|
|
14
|
+
tool_input = data.get("tool_input", {})
|
|
15
|
+
except json.JSONDecodeError as err:
|
|
16
|
+
print(f"hook-error: {err}", file=sys.stderr)
|
|
17
|
+
sys.exit(1)
|
|
18
|
+
|
|
19
|
+
# Check if using Tavily extract
|
|
20
|
+
if tool_name == "mcp__tavily__tavily_extract":
|
|
21
|
+
extract_depth = tool_input.get("extract_depth", "basic")
|
|
22
|
+
urls = tool_input.get("urls", [])
|
|
23
|
+
|
|
24
|
+
# Check if any URLs need advanced extraction
|
|
25
|
+
needs_advanced = any(
|
|
26
|
+
"linkedin.com" in url or
|
|
27
|
+
"protected" in url.lower() or
|
|
28
|
+
"login" in url.lower()
|
|
29
|
+
for url in urls
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if extract_depth == "basic" and needs_advanced:
|
|
33
|
+
warning = """💡 SUGGESTION: Consider extract_depth: "advanced" for:
|
|
34
|
+
- LinkedIn profiles or protected sites
|
|
35
|
+
- Pages with tables or embedded content
|
|
36
|
+
- Sites requiring more thorough extraction
|
|
37
|
+
|
|
38
|
+
To use advanced extraction:
|
|
39
|
+
mcp__tavily__tavily_extract
|
|
40
|
+
urls: [...]
|
|
41
|
+
extract_depth: "advanced"
|
|
42
|
+
|
|
43
|
+
Proceeding with basic extraction...
|
|
44
|
+
"""
|
|
45
|
+
print(warning, file=sys.stderr)
|
|
46
|
+
|
|
47
|
+
# Always allow - this is just informational
|
|
48
|
+
sys.exit(0)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PreToolUse hook: Remind Claude to check Vestige before external lookups.
|
|
4
|
+
|
|
5
|
+
H5 — If Context7 or Tavily is called before any Vestige search this session,
|
|
6
|
+
warn once that memory may already contain the needed context.
|
|
7
|
+
|
|
8
|
+
State is tracked via a timestamp file that expires after 1 hour (new session boundary).
|
|
9
|
+
Exit code 0 = allow tool to proceed (soft warning only).
|
|
10
|
+
"""
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
STATE_FILE = os.path.expanduser("~/.claude/.vestige_searched")
|
|
17
|
+
STALENESS_SECONDS = 3600 # 1 hour — new session after this gap
|
|
18
|
+
|
|
19
|
+
REMINDER = """No Vestige search detected yet — check memory before external lookups.
|
|
20
|
+
mcp__vestige__search query: "{topic}" limit: 5
|
|
21
|
+
Vestige may already have what you need (decisions, patterns, prior context).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_fresh():
|
|
26
|
+
"""Return True if state file exists and was written within this session."""
|
|
27
|
+
if not os.path.exists(STATE_FILE):
|
|
28
|
+
return False
|
|
29
|
+
try:
|
|
30
|
+
mtime = os.path.getmtime(STATE_FILE)
|
|
31
|
+
return (time.time() - mtime) < STALENESS_SECONDS
|
|
32
|
+
except OSError:
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def mark_state(tag):
|
|
37
|
+
"""Write a tag to the state file and refresh its mtime."""
|
|
38
|
+
state_dir = os.path.dirname(STATE_FILE)
|
|
39
|
+
os.makedirs(state_dir, exist_ok=True)
|
|
40
|
+
with open(STATE_FILE, "w") as f:
|
|
41
|
+
f.write(tag)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def read_state():
|
|
45
|
+
"""Read the current tag from the state file."""
|
|
46
|
+
try:
|
|
47
|
+
with open(STATE_FILE, "r") as f:
|
|
48
|
+
return f.read().strip()
|
|
49
|
+
except OSError:
|
|
50
|
+
return ""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
input_data = json.load(sys.stdin)
|
|
55
|
+
except json.JSONDecodeError:
|
|
56
|
+
sys.exit(0)
|
|
57
|
+
|
|
58
|
+
tool_name = input_data.get("tool_name", "")
|
|
59
|
+
tool_input = input_data.get("tool_input", {})
|
|
60
|
+
|
|
61
|
+
# If this IS a Vestige search, mark session as searched and exit silently
|
|
62
|
+
if tool_name.startswith("mcp__vestige__search"):
|
|
63
|
+
mark_state("searched")
|
|
64
|
+
sys.exit(0)
|
|
65
|
+
|
|
66
|
+
# Only act on Context7 or Tavily calls
|
|
67
|
+
if not (tool_name.startswith("mcp__context7__") or tool_name.startswith("mcp__tavily__")):
|
|
68
|
+
sys.exit(0)
|
|
69
|
+
|
|
70
|
+
# If state file is fresh (searched or warned), exit silently — don't nag
|
|
71
|
+
if is_fresh():
|
|
72
|
+
sys.exit(0)
|
|
73
|
+
|
|
74
|
+
# Derive a topic hint from the tool input for the suggestion
|
|
75
|
+
topic = (
|
|
76
|
+
tool_input.get("query")
|
|
77
|
+
or tool_input.get("libraryName")
|
|
78
|
+
or tool_input.get("url")
|
|
79
|
+
or "<topic>"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
print(REMINDER.format(topic=topic), file=sys.stderr)
|
|
83
|
+
|
|
84
|
+
# Mark as warned so this fires only once per session
|
|
85
|
+
mark_state("warned")
|
|
86
|
+
sys.exit(0)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PreToolUse hook: Suggest Tavily extract over WebFetch for certain URLs.
|
|
4
|
+
|
|
5
|
+
Warns (allows command to proceed):
|
|
6
|
+
- WebFetch → suggests Tavily extract for better content extraction
|
|
7
|
+
|
|
8
|
+
Tavily provides better content extraction for articles, documentation,
|
|
9
|
+
and complex web pages.
|
|
10
|
+
"""
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
data = json.load(sys.stdin)
|
|
16
|
+
tool_input = data.get("tool_input", {})
|
|
17
|
+
url = tool_input.get("url", "")
|
|
18
|
+
except json.JSONDecodeError as err:
|
|
19
|
+
print(f"hook-error: {err}", file=sys.stderr)
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
|
|
22
|
+
# Print warning to stderr (shown to Claude)
|
|
23
|
+
warning = f"""⚠️ CONSIDER Tavily: For web content extraction, Tavily often provides cleaner results.
|
|
24
|
+
|
|
25
|
+
WebFetch is fine for simple pages, but Tavily extract handles complex pages better.
|
|
26
|
+
|
|
27
|
+
To use Tavily extract:
|
|
28
|
+
mcp__tavily__tavily_extract
|
|
29
|
+
urls: ["{url}"]
|
|
30
|
+
extract_depth: "basic"
|
|
31
|
+
|
|
32
|
+
For LinkedIn or protected sites, use extract_depth: "advanced"
|
|
33
|
+
|
|
34
|
+
See mcp-tavily skill for full usage patterns.
|
|
35
|
+
|
|
36
|
+
Proceeding with WebFetch...
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
print(warning, file=sys.stderr)
|
|
40
|
+
|
|
41
|
+
# Exit 0 allows the command to proceed (soft warning)
|
|
42
|
+
sys.exit(0)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PreToolUse hook: Suggest Tavily search over WebSearch.
|
|
4
|
+
|
|
5
|
+
Warns (allows command to proceed):
|
|
6
|
+
- WebSearch → suggests Tavily for better results
|
|
7
|
+
|
|
8
|
+
Tavily provides more comprehensive search results and is preferred for
|
|
9
|
+
research tasks requiring current information.
|
|
10
|
+
"""
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
data = json.load(sys.stdin)
|
|
16
|
+
tool_input = data.get("tool_input", {})
|
|
17
|
+
query = tool_input.get("query", "")
|
|
18
|
+
except json.JSONDecodeError as err:
|
|
19
|
+
print(f"hook-error: {err}", file=sys.stderr)
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
|
|
22
|
+
# Print warning to stderr (shown to Claude)
|
|
23
|
+
warning = f"""⚠️ PREFER Tavily: For web research, use Tavily instead of WebSearch.
|
|
24
|
+
|
|
25
|
+
Tavily provides better results for current information, research questions, and comparisons.
|
|
26
|
+
|
|
27
|
+
To use Tavily search:
|
|
28
|
+
mcp__tavily__tavily_search
|
|
29
|
+
query: "{query}"
|
|
30
|
+
search_depth: "basic"
|
|
31
|
+
max_results: 10
|
|
32
|
+
|
|
33
|
+
For comprehensive research, use search_depth: "advanced"
|
|
34
|
+
|
|
35
|
+
See mcp-tavily skill for full usage patterns and options.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
print(warning, file=sys.stderr)
|
|
39
|
+
|
|
40
|
+
# Exit 0 allows the command to proceed (soft warning)
|
|
41
|
+
sys.exit(0)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PostToolUse hook: Warn about WordPress PHP security issues.
|
|
4
|
+
|
|
5
|
+
Checks (soft warnings only, exit 0):
|
|
6
|
+
H1 — AJAX handlers missing nonce verification or capability checks
|
|
7
|
+
H1 — $wpdb queries missing ->prepare()
|
|
8
|
+
H1 — Raw $_POST/$_GET/$_REQUEST without sanitization
|
|
9
|
+
M4 — Missing declare(strict_types=1)
|
|
10
|
+
M8 — function_exists() used instead of action/filter hooks for cross-plugin calls
|
|
11
|
+
|
|
12
|
+
Applies to: Edit, Write on .php files.
|
|
13
|
+
"""
|
|
14
|
+
import json
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
# WordPress core function prefixes — function_exists() on these is fine
|
|
19
|
+
WP_CORE_PREFIXES = (
|
|
20
|
+
"wp_", "is_", "get_", "the_", "add_", "remove_", "do_", "apply_", "has_", "did_",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
SANITIZE_FUNCTIONS = (
|
|
24
|
+
"sanitize_text_field", "absint", "sanitize_email", "wp_kses",
|
|
25
|
+
"esc_html", "esc_attr", "intval",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def check_ajax_security(content: str) -> list[str]:
|
|
30
|
+
warnings = []
|
|
31
|
+
|
|
32
|
+
if not re.search(r"wp_ajax_", content):
|
|
33
|
+
return warnings
|
|
34
|
+
|
|
35
|
+
has_nonce = re.search(r"wp_verify_nonce|check_ajax_referer", content)
|
|
36
|
+
if not has_nonce:
|
|
37
|
+
warnings.append(
|
|
38
|
+
"⚠️ H1: AJAX handler missing nonce verification.\n"
|
|
39
|
+
" Add wp_verify_nonce() or check_ajax_referer() before processing."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
has_capability = re.search(r"current_user_can\s*\(", content)
|
|
43
|
+
if not has_capability:
|
|
44
|
+
warnings.append(
|
|
45
|
+
"⚠️ H1: AJAX handler missing capability check.\n"
|
|
46
|
+
" Add current_user_can() to restrict access."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return warnings
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def check_wpdb_prepare(content: str) -> list[str]:
|
|
53
|
+
if re.search(r"\$wpdb->", content) and not re.search(r"->prepare\s*\(", content):
|
|
54
|
+
return [
|
|
55
|
+
"⚠️ H1: $wpdb query detected without ->prepare().\n"
|
|
56
|
+
" Use $wpdb->prepare() for all queries with dynamic values."
|
|
57
|
+
]
|
|
58
|
+
return []
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def check_sanitization(content: str) -> list[str]:
|
|
62
|
+
has_raw_input = re.search(r"\$_(POST|GET|REQUEST)\s*\[", content)
|
|
63
|
+
if not has_raw_input:
|
|
64
|
+
return []
|
|
65
|
+
|
|
66
|
+
sanitize_pattern = "|".join(re.escape(fn) for fn in SANITIZE_FUNCTIONS)
|
|
67
|
+
has_sanitize = re.search(sanitize_pattern, content)
|
|
68
|
+
if not has_sanitize:
|
|
69
|
+
return [
|
|
70
|
+
"⚠️ H1: Raw $_POST/$_GET/$_REQUEST access without sanitization.\n"
|
|
71
|
+
" Wrap user input with sanitize_text_field(), absint(), wp_kses(), etc."
|
|
72
|
+
]
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def check_strict_types(content: str, file_path: str) -> list[str]:
|
|
77
|
+
if file_path.endswith(".blade.php"):
|
|
78
|
+
return []
|
|
79
|
+
|
|
80
|
+
first_lines = content[:200]
|
|
81
|
+
if re.search(r"<\?php\s+//\s*legacy", first_lines, re.IGNORECASE):
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
if not re.search(r"declare\s*\(\s*strict_types\s*=\s*1\s*\)", content):
|
|
85
|
+
return [
|
|
86
|
+
"⚠️ M4: Missing declare(strict_types=1).\n"
|
|
87
|
+
" Add after opening <?php tag for type safety."
|
|
88
|
+
]
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def check_function_exists(content: str) -> list[str]:
|
|
93
|
+
matches = re.findall(r"function_exists\s*\(\s*['\"]([^'\"]+)['\"]\s*\)", content)
|
|
94
|
+
cross_plugin = [
|
|
95
|
+
fn for fn in matches
|
|
96
|
+
if not fn.startswith(WP_CORE_PREFIXES)
|
|
97
|
+
]
|
|
98
|
+
if cross_plugin:
|
|
99
|
+
fns = ", ".join(cross_plugin)
|
|
100
|
+
return [
|
|
101
|
+
f"⚠️ M8: function_exists() used for cross-plugin calls: {fns}\n"
|
|
102
|
+
" Prefer do_action()/apply_filters() hooks for cross-plugin integration."
|
|
103
|
+
]
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_content(tool_name: str, tool_input: dict) -> str:
|
|
108
|
+
if tool_name == "Write":
|
|
109
|
+
return tool_input.get("content", "")
|
|
110
|
+
|
|
111
|
+
# Edit: file already written to disk — read it for full context
|
|
112
|
+
file_path = tool_input.get("file_path", "")
|
|
113
|
+
try:
|
|
114
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
|
115
|
+
return f.read()
|
|
116
|
+
except OSError:
|
|
117
|
+
return ""
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
input_data = json.load(sys.stdin)
|
|
122
|
+
except json.JSONDecodeError:
|
|
123
|
+
sys.exit(0)
|
|
124
|
+
|
|
125
|
+
tool_name = input_data.get("tool_name", "")
|
|
126
|
+
tool_input = input_data.get("tool_input", {})
|
|
127
|
+
file_path = tool_input.get("file_path", "")
|
|
128
|
+
|
|
129
|
+
if tool_name not in ("Edit", "Write"):
|
|
130
|
+
sys.exit(0)
|
|
131
|
+
|
|
132
|
+
if not file_path.endswith(".php"):
|
|
133
|
+
sys.exit(0)
|
|
134
|
+
|
|
135
|
+
content = get_content(tool_name, tool_input)
|
|
136
|
+
if not content:
|
|
137
|
+
sys.exit(0)
|
|
138
|
+
|
|
139
|
+
warnings = [
|
|
140
|
+
*check_ajax_security(content),
|
|
141
|
+
*check_wpdb_prepare(content),
|
|
142
|
+
*check_sanitization(content),
|
|
143
|
+
*check_strict_types(content, file_path),
|
|
144
|
+
*check_function_exists(content),
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
for warning in warnings:
|
|
148
|
+
print(warning, file=sys.stderr)
|
|
149
|
+
|
|
150
|
+
sys.exit(0)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Personalities
|
|
2
|
+
|
|
3
|
+
**Important**: Personalities are **tone settings**, not expertise.
|
|
4
|
+
|
|
5
|
+
## What Personalities Do
|
|
6
|
+
|
|
7
|
+
Personalities change *how* Claude communicates, not *what* Claude knows:
|
|
8
|
+
|
|
9
|
+
- **40K Mode**: Warhammer 40K themed responses (purging heresy, machine spirits)
|
|
10
|
+
- **Templars Mode**: Medieval crusader themed responses (Deus Vult!)
|
|
11
|
+
|
|
12
|
+
## What Personalities Don't Do
|
|
13
|
+
|
|
14
|
+
Personalities do NOT:
|
|
15
|
+
- Add domain expertise (use Skills for that)
|
|
16
|
+
- Change technical recommendations
|
|
17
|
+
- Override skill guidance
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Simply say:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
"Enable 40k mode"
|
|
25
|
+
"Enable templars mode"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then proceed with your normal requests. The themed language will be applied to responses.
|
|
29
|
+
|
|
30
|
+
## Combining with Skills
|
|
31
|
+
|
|
32
|
+
Personalities work alongside skills:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
"Enable 40k mode and use the js-fp skill to review this code"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Result: Technical guidance from js-fp skill, delivered in grimdark 40K style.
|
|
39
|
+
|
|
40
|
+
## Disabling
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
"Disable personality mode"
|
|
44
|
+
"Return to normal mode"
|
|
45
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# 40K Mode - For the Emperor! Purge the Heretical Code!
|
|
2
|
+
|
|
3
|
+
**Purpose**: Warhammer 40K themed response style for code reviews and development.
|
|
4
|
+
|
|
5
|
+
## Tone Guidelines
|
|
6
|
+
|
|
7
|
+
When 40K mode is active, adopt grimdark Warhammer 40K language:
|
|
8
|
+
|
|
9
|
+
### Core Tenets
|
|
10
|
+
1. **Purity of Function**: Pure functions are blessed by the Emperor
|
|
11
|
+
2. **Death to Mutants**: Mutable state is Chaos corruption
|
|
12
|
+
3. **The Golden Throne**: Immutability endures like the Emperor
|
|
13
|
+
4. **Burn the Heretic**: O(n²) algorithms are abominations
|
|
14
|
+
5. **Purge the Xenos**: External dependencies are alien corruption
|
|
15
|
+
|
|
16
|
+
### Heresy Detection
|
|
17
|
+
|
|
18
|
+
When reviewing code, classify issues as:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
MINOR HERESY (Penance Required):
|
|
22
|
+
- Unnecessary abstraction
|
|
23
|
+
- Missing error handling
|
|
24
|
+
- Inconsistent naming
|
|
25
|
+
|
|
26
|
+
MAJOR HERESY (Purging Required):
|
|
27
|
+
- O(n²) complexity in hot paths
|
|
28
|
+
- Mutable state in pure contexts
|
|
29
|
+
- Side effects without isolation
|
|
30
|
+
|
|
31
|
+
CHAOS CORRUPTION (Exterminatus Protocol):
|
|
32
|
+
- eval() usage
|
|
33
|
+
- SQL injection vulnerabilities
|
|
34
|
+
- Unvalidated user input
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Battle Cries
|
|
38
|
+
|
|
39
|
+
Use themed language like:
|
|
40
|
+
- *"The flesh is weak, but the algorithm is eternal"*
|
|
41
|
+
- *"Blessed is the mind too small for O(n²)"*
|
|
42
|
+
- *"Fear not the complexity, for the Emperor protects... with memoization"*
|
|
43
|
+
- *"By bolter and chainsword, this code shall be refactored"*
|
|
44
|
+
|
|
45
|
+
### Response Style
|
|
46
|
+
|
|
47
|
+
When reporting issues:
|
|
48
|
+
```
|
|
49
|
+
PURGING HERETICAL MUTATIONS
|
|
50
|
+
Brother-Captain, mutation detected in sector 7-Alpha (line 127)
|
|
51
|
+
Deploying sacred bolter rounds of immutability...
|
|
52
|
+
*BLAM* *BLAM* *BLAM*
|
|
53
|
+
Heresy purged. The Emperor is pleased.
|
|
54
|
+
Code purity: 97.3% (Throne-compliant)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
When optimizing:
|
|
58
|
+
```
|
|
59
|
+
THE MACHINE SPIRIT DEMANDS EFFICIENCY
|
|
60
|
+
Auspex readings indicate O(n²) corruption
|
|
61
|
+
Initiating ritual of pre-compilation...
|
|
62
|
+
By the Omnissiah's will, performance increased by 340%
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Remember
|
|
66
|
+
|
|
67
|
+
*In the grim darkness of legacy codebases, there is only technical debt. But fear not - for we are the Emperor's chosen warriors against poor code quality.*
|
|
68
|
+
|
|
69
|
+
**Ave Imperator!**
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Templars Mode - Deus Vult! Purge the Heretical Code!
|
|
2
|
+
|
|
3
|
+
**Purpose**: Medieval Templar crusader themed response style for code reviews and development.
|
|
4
|
+
|
|
5
|
+
## Tone Guidelines
|
|
6
|
+
|
|
7
|
+
When Templars mode is active, adopt crusader knight language:
|
|
8
|
+
|
|
9
|
+
### Core Tenets
|
|
10
|
+
1. **Purity of Function**: Pure functions are blessed by God
|
|
11
|
+
2. **Death to Sin**: Mutable state is fallen nature's corruption
|
|
12
|
+
3. **The Throne of Grace**: Immutability endures like God's truth
|
|
13
|
+
4. **Burn the Heretical**: O(n²) algorithms are abominations before the Lord
|
|
14
|
+
5. **Cast Out Demons**: External dependencies are demonic influence
|
|
15
|
+
|
|
16
|
+
### Heresy Detection
|
|
17
|
+
|
|
18
|
+
When reviewing code, classify issues as:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
MINOR HERESY (Penance Required):
|
|
22
|
+
- Unnecessary abstraction
|
|
23
|
+
- Missing error handling
|
|
24
|
+
- Inconsistent naming
|
|
25
|
+
|
|
26
|
+
MAJOR HERESY (Purging Required):
|
|
27
|
+
- O(n²) complexity in hot paths
|
|
28
|
+
- Mutable state in pure contexts
|
|
29
|
+
- Side effects without confession
|
|
30
|
+
|
|
31
|
+
DIABOLIC CORRUPTION (Auto-da-Fe Protocol):
|
|
32
|
+
- eval() usage
|
|
33
|
+
- SQL injection vulnerabilities
|
|
34
|
+
- Unvalidated user input
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Sacred Litanies
|
|
38
|
+
|
|
39
|
+
Use themed language like:
|
|
40
|
+
- *"The flesh is weak, but the algorithm is eternal"*
|
|
41
|
+
- *"Blessed is the mind too small for O(n²)"*
|
|
42
|
+
- *"In nomine Patris, I optimize this function"*
|
|
43
|
+
- *"By sword and flame, this code shall be refactored"*
|
|
44
|
+
|
|
45
|
+
### Response Style
|
|
46
|
+
|
|
47
|
+
When reporting issues:
|
|
48
|
+
```
|
|
49
|
+
PURGING HERETICAL MUTATIONS
|
|
50
|
+
Brother Knight, corruption detected in sector 7-Alpha (line 127)
|
|
51
|
+
Deploying sanctified blade of immutability...
|
|
52
|
+
*SLASH* *BURN* *CLEANSE*
|
|
53
|
+
Heresy purged. The Lord is pleased.
|
|
54
|
+
Code purity: 97.3% (Heaven-approved)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
When optimizing:
|
|
58
|
+
```
|
|
59
|
+
THE LORD DEMANDS EFFICIENCY
|
|
60
|
+
Divine vision reveals O(n²) corruption
|
|
61
|
+
Initiating ritual of consecration...
|
|
62
|
+
By Christ's holy name, performance increased by 340%
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Remember
|
|
66
|
+
|
|
67
|
+
*In the darkness of legacy codebases, there is only technical debt. But fear not - for we are Christ's chosen warriors against poor code quality.*
|
|
68
|
+
|
|
69
|
+
**Deus Vult!**
|