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,112 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PreToolUse hook: Enforce Atlassian MCP prerequisites before tool calls.
4
+
5
+ Three checks combined:
6
+ H3 — cloudId bootstrap: getAccessibleAtlassianResources must be called first
7
+ H4 — transitions: getTransitionsForJiraIssue must precede transitionJiraIssue
8
+ M5 — ADF body: Confluence page body must be a JSON string, not a raw object
9
+
10
+ State is tracked in a JSON file that expires after 1 hour (new session boundary).
11
+ Exit code 0 = allow tool to proceed (soft warnings only).
12
+ """
13
+ import json
14
+ import os
15
+ import sys
16
+ import time
17
+
18
+ STATE_FILE = os.path.expanduser("~/.claude/.atlassian_session_state")
19
+ STALENESS_SECONDS = 3600 # 1 hour — new session after this gap
20
+
21
+ BOOTSTRAP_TOOL = "mcp__claude_ai_Atlassian__getAccessibleAtlassianResources"
22
+ TRANSITIONS_TOOL = "mcp__claude_ai_Atlassian__getTransitionsForJiraIssue"
23
+ TRANSITION_ISSUE_TOOL = "mcp__claude_ai_Atlassian__transitionJiraIssue"
24
+ CONFLUENCE_WRITE_TOOLS = {
25
+ "mcp__claude_ai_Atlassian__createConfluencePage",
26
+ "mcp__claude_ai_Atlassian__updateConfluencePage",
27
+ }
28
+
29
+ BOOTSTRAP_WARNING = """Atlassian bootstrap: Call getAccessibleAtlassianResources first to obtain cloudId.
30
+ mcp__claude_ai_Atlassian__getAccessibleAtlassianResources
31
+ """
32
+
33
+ TRANSITIONS_WARNING = """Transition IDs are issue-specific — call getTransitionsForJiraIssue first.
34
+ mcp__claude_ai_Atlassian__getTransitionsForJiraIssue issueIdOrKey: "{key}"
35
+ """
36
+
37
+ ADF_WARNING = """ADF body must be a JSON string (JSON.stringify'd), not a raw object — #1 cause of Confluence failures.
38
+ """
39
+
40
+
41
+ def load_state():
42
+ """Load session state, returning defaults if missing or stale."""
43
+ default = {"bootstrapped": False, "transitions_fetched": False, "timestamp": 0.0}
44
+ if not os.path.exists(STATE_FILE):
45
+ return default
46
+ try:
47
+ with open(STATE_FILE, "r") as f:
48
+ state = json.load(f)
49
+ if (time.time() - state.get("timestamp", 0)) > STALENESS_SECONDS:
50
+ return default
51
+ return state
52
+ except (json.JSONDecodeError, OSError):
53
+ return default
54
+
55
+
56
+ def save_state(state):
57
+ """Write session state to disk."""
58
+ state_dir = os.path.dirname(STATE_FILE)
59
+ os.makedirs(state_dir, exist_ok=True)
60
+ state["timestamp"] = time.time()
61
+ with open(STATE_FILE, "w") as f:
62
+ json.dump(state, f)
63
+
64
+
65
+ try:
66
+ input_data = json.load(sys.stdin)
67
+ except json.JSONDecodeError:
68
+ sys.exit(0)
69
+
70
+ tool_name = input_data.get("tool_name", "")
71
+ tool_input = input_data.get("tool_input", {})
72
+
73
+ # Only act on Atlassian MCP tools
74
+ if not tool_name.startswith("mcp__claude_ai_Atlassian__"):
75
+ sys.exit(0)
76
+
77
+ state = load_state()
78
+
79
+ # H3: If this IS the bootstrap tool, mark and exit silently
80
+ if tool_name == BOOTSTRAP_TOOL:
81
+ state["bootstrapped"] = True
82
+ save_state(state)
83
+ sys.exit(0)
84
+
85
+ # H4: If this IS the transitions fetch tool, mark and exit silently
86
+ if tool_name == TRANSITIONS_TOOL:
87
+ state["transitions_fetched"] = True
88
+ save_state(state)
89
+ sys.exit(0)
90
+
91
+ warnings = []
92
+
93
+ # H3: Any other Atlassian tool requires bootstrap first
94
+ if not state["bootstrapped"]:
95
+ warnings.append(BOOTSTRAP_WARNING)
96
+
97
+ # H4: transitionJiraIssue requires getTransitionsForJiraIssue first
98
+ if tool_name == TRANSITION_ISSUE_TOOL and not state["transitions_fetched"]:
99
+ issue_key = tool_input.get("issueIdOrKey", "<issueIdOrKey>")
100
+ warnings.append(TRANSITIONS_WARNING.format(key=issue_key))
101
+
102
+ # M5: ADF body must be a JSON string, not a raw Python dict
103
+ if tool_name in CONFLUENCE_WRITE_TOOLS:
104
+ content_format = tool_input.get("contentFormat", "")
105
+ body = tool_input.get("body")
106
+ if content_format == "adf" and isinstance(body, dict):
107
+ warnings.append(ADF_WARNING)
108
+
109
+ for warning in warnings:
110
+ print(warning, file=sys.stderr)
111
+
112
+ sys.exit(0)
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PreToolUse hook: BLOCK sed file editing via Bash.
4
+
5
+ Hard guard (exit 1 = block execution). sed edits are a symptom of a broken
6
+ workflow — Claude either didn't read the file first, didn't use Serena's
7
+ symbolic editing, or is working with a file that's too large.
8
+
9
+ Allowed through:
10
+ - Piped sed transforms (no -i, no file redirect)
11
+ - Data pipelines (echo ... | sed ...)
12
+ """
13
+ import json
14
+ import re
15
+ import sys
16
+
17
+ BLOCK_MESSAGE = """🚫 BLOCKED: sed file editing is never the right approach.
18
+
19
+ You're using sed because something went wrong. Stop and fix the root cause:
20
+
21
+ 1. READ the file first — Edit/Write require a prior Read (did you skip this?)
22
+ 2. Use Serena symbolic editing — replace_symbol_body, insert_after_symbol, insert_before_symbol
23
+ 3. Use Edit tool for targeted string replacements
24
+ 4. If the file is too large to read (>500 lines), that's a separate problem — the file needs refactoring
25
+
26
+ DO NOT retry with sed. Go back to step 1."""
27
+
28
+
29
+ def is_sed_file_edit(command: str) -> bool:
30
+ """Detect sed commands that mutate files (not piped transforms)."""
31
+ # sed -i (in-place edit) in any flag position
32
+ if re.search(r"\bsed\b.*\s-[^\s]*i", command):
33
+ return True
34
+
35
+ # sed ... > file or sed ... >> file (redirect output to file)
36
+ if re.search(r"\bsed\b.+>{1,2}\s*\S+", command):
37
+ # But not if sed input is piped (e.g., echo x | sed ... > file is borderline,
38
+ # but still a file mutation via sed — block it)
39
+ return True
40
+
41
+ return False
42
+
43
+
44
+ try:
45
+ input_data = json.load(sys.stdin)
46
+ except json.JSONDecodeError as e:
47
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
48
+ sys.exit(1)
49
+
50
+ tool_name = input_data.get("tool_name", "")
51
+ tool_input = input_data.get("tool_input", {})
52
+ command = tool_input.get("command", "")
53
+
54
+ if tool_name != "Bash" or not command:
55
+ sys.exit(0)
56
+
57
+ if is_sed_file_edit(command):
58
+ print(BLOCK_MESSAGE, file=sys.stderr)
59
+ sys.exit(1)
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env bash
2
+ # SessionStart hook: Inject ima-claude foundational context into every session.
3
+ # Injects ima-claude foundational context (persona, agents, MCP routing).
4
+ # stdout goes into Claude's context.
5
+
6
+ cat << 'BOOTSTRAP'
7
+ ## ima-claude: Active Plugin
8
+
9
+ ### Default Persona: The Practitioner
10
+
11
+ A 25-year software development veteran. FP-first, composition-minded, anti-over-engineering.
12
+ Uses "we" not "I" — collaborative, humble, light-hearted. "Slow is smooth, smooth is fast."
13
+
14
+ ### Memory Bootstrap
15
+
16
+ At session start, check memory before asking questions:
17
+ - Vestige: `mcp__vestige__search` for user preferences and project context
18
+ - Vestige: `mcp__vestige__intention action: "check"` for pending reminders
19
+ - Serena: `mcp__serena__list_memories` if in a Serena-activated project
20
+
21
+ ### Memory Routing
22
+
23
+ | Store what | Where | Why |
24
+ |---|---|---|
25
+ | Decisions, preferences, patterns, bugs | Vestige `smart_ingest` | Fades naturally if not referenced |
26
+ | Reference material (docs, standards, PRDs) | Qdrant `qdrant-store` | Permanent library |
27
+ | Session state, task progress | Serena `write_memory` | Project-scoped workbench |
28
+ | Future reminders | Vestige `intention` | Surfaces at next session |
29
+
30
+ Auto-store: "I prefer..." → Vestige preference. "Let's go with X because..." → Vestige decision. "The reason this failed..." → Vestige bug.
31
+
32
+ After completing work: store outcome in Vestige, reference material in Qdrant, session state in Serena.
33
+
34
+ ### Orchestrator Protocol
35
+
36
+ You are the Orchestrator. Plan and delegate. Do NOT implement directly.
37
+ - Non-trivial work → `/ima-claude:task-planner` (decompose) → `/ima-claude:task-runner` (delegate)
38
+ - Trivial = single file, < 5 lines, no judgment calls
39
+ - Model selection: opus for orchestration, sonnet for implementation (default), haiku for lookups
40
+
41
+ ### Available Agents
42
+
43
+ Delegate to named agents — they enforce model, tools, and permissions automatically.
44
+
45
+ | Agent | Model | Mode | Use For |
46
+ |---|---|---|---|
47
+ | `ima-claude:explorer` | haiku | read-only | File discovery, codebase exploration |
48
+ | `ima-claude:implementer` | sonnet | full | Feature dev, bug fixes, refactoring |
49
+ | `ima-claude:reviewer` | sonnet | read-only | Code review, security audit, FP checks |
50
+ | `ima-claude:wp-developer` | sonnet | full | WordPress plugins, themes, WP-CLI, forms |
51
+ | `ima-claude:memory` | sonnet | full | Memory search, storage, consolidation |
52
+
53
+ ### Code Navigation (Serena — REQUIRED when installed)
54
+
55
+ **Always prefer Serena over Read/Grep for code investigation.** 40-70% token savings.
56
+
57
+ | Instead of | Use |
58
+ |---|---|
59
+ | Read file to understand structure | `mcp__serena__jet_brains_get_symbols_overview relative_path: "..."` |
60
+ | Grep for class/function definition | `mcp__serena__jet_brains_find_symbol name_path_pattern: "Name"` |
61
+ | Grep for callers/references | `mcp__serena__jet_brains_find_referencing_symbols name_path: "method"` |
62
+
63
+ Use Read only when you need the actual implementation body of a known, specific symbol.
64
+
65
+ ### Complex Reasoning (Sequential Thinking — REQUIRED for analysis)
66
+
67
+ Use `mcp__sequential-thinking__sequentialthinking` before acting on:
68
+ - Debugging / root cause analysis / "why is this failing"
69
+ - Trade-off evaluation / "which approach"
70
+ - Architectural decisions / design choices
71
+ - Multi-step investigations where approach may change
72
+
73
+ ### Other MCP Tools
74
+
75
+ | Signal | Tool |
76
+ |---|---|
77
+ | "latest", "2025/2026", "what's new" | Tavily |
78
+ | Library/framework API question | Context7 |
79
+
80
+ Before web tools: check Claude's knowledge → Context7 → then Tavily/WebFetch.
81
+
82
+ ### Search Preference
83
+
84
+ Always prefer `rg` (ripgrep) over grep/find. Faster, respects .gitignore, recursive by default.
85
+
86
+ ### Session Management
87
+
88
+ - `/ima-claude:save-session` — save to Serena memory
89
+ - `/ima-claude:resume-session` — load from Serena memory + Vestige context
90
+ BOOTSTRAP
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook: Warn about hardcoded CSS when Bootstrap utilities are available.
4
+
5
+ Checks (soft warning only, exit 0):
6
+ M3 — Inline styles in HTML/PHP that have Bootstrap 5 utility equivalents
7
+ M3 — CSS properties in .scss/.css only when Bootstrap context is detected
8
+
9
+ Applies to: Edit, Write on .html, .php, .scss, .css files.
10
+ """
11
+ import json
12
+ import re
13
+ import sys
14
+
15
+ HTML_PHP_EXTENSIONS = (".html", ".php")
16
+ CSS_EXTENSIONS = (".scss", ".css")
17
+ ALL_EXTENSIONS = HTML_PHP_EXTENSIONS + CSS_EXTENSIONS
18
+
19
+ # Bootstrap context signals in CSS/SCSS files
20
+ BOOTSTRAP_CONTEXT = re.compile(
21
+ r"@import\s+['\"].*bootstrap|"
22
+ r"\$spacer\b|"
23
+ r"\bbs-"
24
+ )
25
+
26
+ # Inline style patterns with their Bootstrap equivalents
27
+ INLINE_STYLE_PATTERNS = [
28
+ (re.compile(r'style=["\'][^"\']*margin', re.IGNORECASE), "margin-*", "m-* / mt-* / mb-* / ms-* / me-*"),
29
+ (re.compile(r'style=["\'][^"\']*padding', re.IGNORECASE), "padding-*", "p-* / pt-* / pb-* / ps-* / pe-*"),
30
+ (re.compile(r'style=["\'][^"\']*display\s*:\s*flex', re.IGNORECASE), "display: flex", "d-flex"),
31
+ (re.compile(r'style=["\'][^"\']*display\s*:\s*none', re.IGNORECASE), "display: none", "d-none"),
32
+ (re.compile(r'style=["\'][^"\']*text-align\s*:\s*center', re.IGNORECASE), "text-align: center", "text-center"),
33
+ (re.compile(r'style=["\'][^"\']*font-weight\s*:\s*bold', re.IGNORECASE), "font-weight: bold", "fw-bold"),
34
+ ]
35
+
36
+ WARNING_HEADER = "⚠️ Hardcoded CSS detected — Bootstrap 5 utilities available:"
37
+ WARNING_EXAMPLES = (
38
+ ' style="margin-top: 16px" → class="mt-3"\n'
39
+ ' style="display: flex" → class="d-flex"\n'
40
+ ' style="text-align: center" → class="text-center"\n'
41
+ " See /ima-bootstrap skill for utility-first patterns."
42
+ )
43
+
44
+
45
+ def is_bootstrap_context(content: str) -> bool:
46
+ return bool(BOOTSTRAP_CONTEXT.search(content))
47
+
48
+
49
+ def find_inline_style_issues(content: str) -> list[str]:
50
+ return [
51
+ f" {css_prop} → {utility}"
52
+ for pattern, css_prop, utility in INLINE_STYLE_PATTERNS
53
+ if pattern.search(content)
54
+ ]
55
+
56
+
57
+ def get_content(tool_name: str, tool_input: dict) -> str:
58
+ if tool_name == "Write":
59
+ return tool_input.get("content", "")
60
+ # Edit: scan only new_string — what's being written now
61
+ return tool_input.get("new_string", "")
62
+
63
+
64
+ try:
65
+ input_data = json.load(sys.stdin)
66
+ except json.JSONDecodeError:
67
+ sys.exit(0)
68
+
69
+ tool_name = input_data.get("tool_name", "")
70
+ tool_input = input_data.get("tool_input", {})
71
+ file_path = tool_input.get("file_path", "")
72
+
73
+ if tool_name not in ("Edit", "Write"):
74
+ sys.exit(0)
75
+
76
+ if not file_path.endswith(ALL_EXTENSIONS):
77
+ sys.exit(0)
78
+
79
+ content = get_content(tool_name, tool_input)
80
+ if not content:
81
+ sys.exit(0)
82
+
83
+ is_css_file = file_path.endswith(CSS_EXTENSIONS)
84
+
85
+ # CSS/SCSS files only warn when Bootstrap is confirmed in context
86
+ if is_css_file and not is_bootstrap_context(content):
87
+ sys.exit(0)
88
+
89
+ issues = find_inline_style_issues(content)
90
+ if issues:
91
+ print(WARNING_HEADER, file=sys.stderr)
92
+ print(WARNING_EXAMPLES, file=sys.stderr)
93
+
94
+ sys.exit(0)
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook: Warn about Composer autoload "files" entries that break PHPUnit.
4
+
5
+ M9 — Composer autoload files bug detection (from phpunit-wp skill).
6
+
7
+ After writing or editing a composer.json, checks whether autoload.files is populated.
8
+ Autoload files run BEFORE the test bootstrap defines ABSPATH/WPINC, causing WordPress
9
+ plugin files to fatal error during `composer install` in test environments.
10
+ Exit code 0 = soft warning via stderr.
11
+ """
12
+ import json
13
+ import sys
14
+
15
+ WARNING = (
16
+ '⚠️ Composer autoload "files" detected — this can break PHPUnit tests.\n'
17
+ " Autoload files run BEFORE test bootstrap defines ABSPATH/WPINC.\n"
18
+ " WordPress plugin files will fatal error during `composer install`.\n"
19
+ " Fix: Move plugin files to autoload.classmap or load via bootstrap.\n"
20
+ " See /phpunit-wp skill for the full fix pattern."
21
+ )
22
+
23
+
24
+ def get_content(tool_name: str, tool_input: dict) -> str:
25
+ if tool_name == "Write":
26
+ return tool_input.get("content", "")
27
+
28
+ # Edit: file already written to disk — read it for the current state
29
+ file_path = tool_input.get("file_path", "")
30
+ try:
31
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
32
+ return f.read()
33
+ except OSError:
34
+ return ""
35
+
36
+
37
+ def has_autoload_files(content: str) -> bool:
38
+ try:
39
+ data = json.loads(content)
40
+ except (json.JSONDecodeError, ValueError):
41
+ return False
42
+
43
+ files = data.get("autoload", {}).get("files", [])
44
+ return isinstance(files, list) and len(files) > 0
45
+
46
+
47
+ try:
48
+ input_data = json.load(sys.stdin)
49
+ except json.JSONDecodeError:
50
+ sys.exit(0)
51
+
52
+ tool_name = input_data.get("tool_name", "")
53
+ tool_input = input_data.get("tool_input", {})
54
+ file_path = tool_input.get("file_path", "")
55
+
56
+ if tool_name not in ("Edit", "Write"):
57
+ sys.exit(0)
58
+
59
+ if not file_path.endswith("composer.json"):
60
+ sys.exit(0)
61
+
62
+ content = get_content(tool_name, tool_input)
63
+
64
+ if not content:
65
+ sys.exit(0)
66
+
67
+ if has_autoload_files(content):
68
+ print(WARNING, file=sys.stderr)
69
+
70
+ sys.exit(0)
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook: Warn when a Markdown file is written to the project root.
4
+
5
+ M10 — Markdown files scattered in project root.
6
+
7
+ After a Write to a .md file, checks whether the file lands at the root level of the
8
+ project rather than in a designated docs subdirectory. Exempt files (README.md, etc.)
9
+ and files in docs/, .claude/, skills/, or hooks/ subdirectories are silently allowed.
10
+ Exit code 0 = soft warning via stderr.
11
+ """
12
+ import json
13
+ import os
14
+ import subprocess
15
+ import sys
16
+
17
+ EXEMPT_FILENAMES = {
18
+ "README.md",
19
+ "CLAUDE.md",
20
+ "CHANGELOG.md",
21
+ "LICENSE.md",
22
+ "CONTRIBUTING.md",
23
+ "CODE_OF_CONDUCT.md",
24
+ }
25
+
26
+ EXEMPT_DIRS = {"docs", ".claude", "skills", "hooks"}
27
+
28
+ WARNING = (
29
+ "⚠️ Markdown file written to project root — consider docs-organize structure:\n"
30
+ " docs/active/ — permanent documentation\n"
31
+ " docs/archive/ — historical records\n"
32
+ " docs/transient/ — ephemeral notes (git-ignored)"
33
+ )
34
+
35
+
36
+ def get_git_root(path: str) -> str:
37
+ """Return the git repository root, or the file's directory as fallback.
38
+
39
+ Walks up from the file's directory to find the first existing ancestor,
40
+ so this works even when Claude is writing a file into a not-yet-created dir.
41
+ """
42
+ search_dir = os.path.dirname(os.path.abspath(path))
43
+ while search_dir and not os.path.isdir(search_dir):
44
+ parent = os.path.dirname(search_dir)
45
+ if parent == search_dir:
46
+ break
47
+ search_dir = parent
48
+
49
+ try:
50
+ result = subprocess.run(
51
+ ["git", "rev-parse", "--show-toplevel"],
52
+ capture_output=True,
53
+ text=True,
54
+ cwd=search_dir,
55
+ )
56
+ if result.returncode == 0:
57
+ return result.stdout.strip()
58
+ except OSError:
59
+ pass
60
+ return search_dir
61
+
62
+
63
+ def is_exempt(file_path: str, git_root: str) -> bool:
64
+ filename = os.path.basename(file_path)
65
+ if filename in EXEMPT_FILENAMES:
66
+ return True
67
+
68
+ rel = os.path.relpath(file_path, git_root)
69
+ parts = rel.split(os.sep)
70
+
71
+ # File is inside an exempt subdirectory
72
+ if len(parts) > 1 and parts[0] in EXEMPT_DIRS:
73
+ return True
74
+
75
+ return False
76
+
77
+
78
+ try:
79
+ input_data = json.load(sys.stdin)
80
+ except json.JSONDecodeError:
81
+ sys.exit(0)
82
+
83
+ tool_name = input_data.get("tool_name", "")
84
+ tool_input = input_data.get("tool_input", {})
85
+ file_path = tool_input.get("file_path", "")
86
+
87
+ if tool_name != "Write":
88
+ sys.exit(0)
89
+
90
+ if not file_path.endswith(".md"):
91
+ sys.exit(0)
92
+
93
+ git_root = get_git_root(file_path)
94
+ rel = os.path.relpath(file_path, git_root)
95
+
96
+ # Only warn when the file is at the root level (no subdirectory component)
97
+ if os.path.dirname(rel) != "":
98
+ sys.exit(0)
99
+
100
+ if is_exempt(file_path, git_root):
101
+ sys.exit(0)
102
+
103
+ print(WARNING, file=sys.stderr)
104
+ sys.exit(0)
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PreToolUse hook: Warn about using grep/find instead of ripgrep.
4
+
5
+ Warns (allows command to proceed):
6
+ - grep → suggests rg
7
+ - find -name → suggests rg --files -g pattern
8
+
9
+ The warning is shown to Claude, encouraging it to use rg for subsequent operations.
10
+ """
11
+ import json
12
+ import re
13
+ import sys
14
+
15
+ VALIDATION_RULES = [
16
+ (
17
+ r"\bgrep\b(?!.*\|)",
18
+ "⚠️ PREFER ripgrep: Use 'rg' instead of 'grep' - faster, better defaults, respects .gitignore. "
19
+ "Please use 'rg' for the rest of this session. See /rg skill for usage.",
20
+ ),
21
+ (
22
+ r"\bfind\s+\S+\s+-name\b",
23
+ "⚠️ PREFER ripgrep: Use 'rg --files -g \"pattern\"' instead of 'find -name' - faster, respects .gitignore. "
24
+ "Please use 'rg' for the rest of this session. See /rg skill for usage.",
25
+ ),
26
+ ]
27
+
28
+
29
+ def validate_command(command: str) -> list[str]:
30
+ issues = []
31
+ for pattern, message in VALIDATION_RULES:
32
+ if re.search(pattern, command):
33
+ issues.append(message)
34
+ return issues
35
+
36
+
37
+ try:
38
+ input_data = json.load(sys.stdin)
39
+ except json.JSONDecodeError as e:
40
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
41
+ sys.exit(1)
42
+
43
+ tool_name = input_data.get("tool_name", "")
44
+ tool_input = input_data.get("tool_input", {})
45
+ command = tool_input.get("command", "")
46
+
47
+ if tool_name != "Bash" or not command:
48
+ sys.exit(0)
49
+
50
+ issues = validate_command(command)
51
+
52
+ if issues:
53
+ for message in issues:
54
+ print(f"{message}", file=sys.stderr)
55
+ # Exit code 0 allows command to proceed but stderr is shown to Claude as a warning
56
+ sys.exit(0)
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PostToolUse hook: Warn about custom FP utility definitions.
4
+
5
+ Checks (soft warning only, exit 0):
6
+ M1 — Custom pipe/compose using reduce/reduceRight
7
+ M1 — Custom curry with rest/spread params
8
+ M1 — Custom monads (Maybe, Either, Result, Option classes)
9
+ M1 — Custom pattern matching function named 'match'
10
+
11
+ Applies to: Edit, Write on .js, .ts, .mjs, .mts, .php files.
12
+ """
13
+ import json
14
+ import re
15
+ import sys
16
+
17
+ JS_PHP_EXTENSIONS = (".js", ".ts", ".mjs", ".mts", ".php")
18
+
19
+ # Import lines — these are safe, skip them
20
+ IMPORT_LINE = re.compile(r"^\s*(import|require|use)\b", re.MULTILINE)
21
+
22
+ FP_UTILITY_PATTERNS = [
23
+ # pipe/compose backed by reduce
24
+ re.compile(r"(function|const|let|var)\s+pipe\s*[=(].*reduce", re.DOTALL),
25
+ re.compile(r"(function|const|let|var)\s+compose\s*[=(].*reduce", re.DOTALL),
26
+ # PHP pipe/compose
27
+ re.compile(r"function\s+pipe\s*\("),
28
+ re.compile(r"function\s+compose\s*\("),
29
+ # curry definitions
30
+ re.compile(r"(function|const|let|var)\s+curry\s*[=(]"),
31
+ # monad classes
32
+ re.compile(r"class\s+(Maybe|Either|Result|Option)\b"),
33
+ # custom match pattern matching
34
+ re.compile(r"(function|const|let|var)\s+match\s*[=(][^;]*patterns", re.DOTALL),
35
+ ]
36
+
37
+ WARNING = (
38
+ "⚠️ Custom FP utility detected — js-fp/php-fp skills say: use native patterns instead.\n"
39
+ " No pipe/compose — use chained methods or intermediate variables\n"
40
+ " No curry — use closures and partial application naturally\n"
41
+ " No custom monads — use early returns, null coalescing, optional chaining"
42
+ )
43
+
44
+
45
+ def is_import_line(line: str) -> bool:
46
+ return bool(IMPORT_LINE.match(line))
47
+
48
+
49
+ def strip_import_lines(content: str) -> str:
50
+ return "\n".join(
51
+ line for line in content.splitlines()
52
+ if not is_import_line(line)
53
+ )
54
+
55
+
56
+ def has_fp_utility(content: str) -> bool:
57
+ cleaned = strip_import_lines(content)
58
+ return any(pattern.search(cleaned) for pattern in FP_UTILITY_PATTERNS)
59
+
60
+
61
+ def get_content(tool_name: str, tool_input: dict) -> str:
62
+ if tool_name == "Write":
63
+ return tool_input.get("content", "")
64
+ # Edit: scan only new_string — we care about what's being added
65
+ return tool_input.get("new_string", "")
66
+
67
+
68
+ try:
69
+ input_data = json.load(sys.stdin)
70
+ except json.JSONDecodeError:
71
+ sys.exit(0)
72
+
73
+ tool_name = input_data.get("tool_name", "")
74
+ tool_input = input_data.get("tool_input", {})
75
+ file_path = tool_input.get("file_path", "")
76
+
77
+ if tool_name not in ("Edit", "Write"):
78
+ sys.exit(0)
79
+
80
+ if not file_path.endswith(JS_PHP_EXTENSIONS):
81
+ sys.exit(0)
82
+
83
+ content = get_content(tool_name, tool_input)
84
+ if not content:
85
+ sys.exit(0)
86
+
87
+ if has_fp_utility(content):
88
+ print(WARNING, file=sys.stderr)
89
+
90
+ sys.exit(0)