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
package/dist/cli.js ADDED
@@ -0,0 +1,1064 @@
1
+ #!/usr/bin/env node
2
+
3
+ // scripts/utils.ts
4
+ import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, chmodSync } from "fs";
5
+ import { join, dirname } from "path";
6
+ import { homedir } from "os";
7
+ import { createInterface } from "readline";
8
+ var CLAUDE_DIR = join(homedir(), ".claude");
9
+ var SKILLS_DIR = join(CLAUDE_DIR, "skills");
10
+ var HOOKS_DIR = join(CLAUDE_DIR, "hooks");
11
+ var COMMANDS_DIR = join(CLAUDE_DIR, "commands");
12
+ var RULES_DIR = join(CLAUDE_DIR, "rules");
13
+ var SETTINGS_FILE = join(CLAUDE_DIR, "settings.json");
14
+ var VERSION = "2.9.0";
15
+ var colors = {
16
+ reset: "\x1B[0m",
17
+ bright: "\x1B[1m",
18
+ red: "\x1B[31m",
19
+ green: "\x1B[32m",
20
+ yellow: "\x1B[33m",
21
+ blue: "\x1B[34m",
22
+ cyan: "\x1B[36m"
23
+ };
24
+ var log = {
25
+ info: (msg) => console.log(`${colors.cyan}\u2139${colors.reset} ${msg}`),
26
+ success: (msg) => console.log(`${colors.green}\u2705${colors.reset} ${msg}`),
27
+ warn: (msg) => console.log(`${colors.yellow}\u26A0\uFE0F${colors.reset} ${msg}`),
28
+ error: (msg) => console.error(`${colors.red}\u274C${colors.reset} ${msg}`),
29
+ step: (msg) => console.log(`${colors.blue}\u2192${colors.reset} ${msg}`)
30
+ };
31
+ function ensureDir(dir) {
32
+ if (!existsSync(dir)) {
33
+ mkdirSync(dir, { recursive: true });
34
+ }
35
+ }
36
+ function copyDirRecursive(src, dest) {
37
+ ensureDir(dest);
38
+ const entries = readdirSync(src);
39
+ for (const entry of entries) {
40
+ const srcPath = join(src, entry);
41
+ const destPath = join(dest, entry);
42
+ const stat = statSync(srcPath);
43
+ if (stat.isDirectory()) {
44
+ copyDirRecursive(srcPath, destPath);
45
+ } else {
46
+ if (existsSync(destPath)) {
47
+ try {
48
+ unlinkSync(destPath);
49
+ } catch {
50
+ chmodSync(destPath, 420);
51
+ unlinkSync(destPath);
52
+ }
53
+ }
54
+ copyFileSync(srcPath, destPath);
55
+ }
56
+ }
57
+ }
58
+ function prompt(question) {
59
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
60
+ return new Promise((resolve2) => {
61
+ rl.question(question, (answer) => {
62
+ rl.close();
63
+ resolve2(answer.trim());
64
+ });
65
+ });
66
+ }
67
+ async function promptYesNo(question, defaultYes = true) {
68
+ const suffix = defaultYes ? "[Y/n]" : "[y/N]";
69
+ const answer = await prompt(`${question} ${suffix} `);
70
+ if (!answer) return defaultYes;
71
+ return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
72
+ }
73
+ var SKILLS_TO_INSTALL = [
74
+ // Foundational skills
75
+ "functional-programmer",
76
+ "task-master",
77
+ "task-planner",
78
+ "task-runner",
79
+ // FP implementation skills
80
+ "js-fp",
81
+ "js-fp-api",
82
+ "js-fp-react",
83
+ "js-fp-vue",
84
+ "js-fp-wordpress",
85
+ "php-fp",
86
+ "php-fp-wordpress",
87
+ "quasar-fp",
88
+ "jquery",
89
+ // Payment & API skills
90
+ "php-authnet",
91
+ // Domain expert skills
92
+ "architect",
93
+ "docs-organize",
94
+ "wp-local",
95
+ "rg",
96
+ "ima-forms-expert",
97
+ "ima-brand",
98
+ "ima-bootstrap",
99
+ "jira-checkpoint",
100
+ // Testing skills
101
+ "playwright",
102
+ // Discourse skills
103
+ "discourse",
104
+ "discourse-admin",
105
+ "ember-discourse",
106
+ // Integration skills
107
+ "compound-bridge",
108
+ // MCP integration skills
109
+ "mcp-atlassian",
110
+ "mcp-tavily",
111
+ "mcp-context7",
112
+ "mcp-serena",
113
+ "mcp-sequential",
114
+ "mcp-memory",
115
+ "mcp-vestige",
116
+ "mcp-qdrant",
117
+ // Quick reference
118
+ "quickstart",
119
+ "scorecard",
120
+ // Session management skills
121
+ "save-session",
122
+ "resume-session",
123
+ // Meta skills
124
+ "skill-analyzer",
125
+ "skill-creator"
126
+ ];
127
+ var HOOKS_TO_INSTALL = [
128
+ // Tool redirection hooks
129
+ "enforce_rg_over_grep.py",
130
+ "tavily_extract_advanced.py",
131
+ "webfetch_to_tavily.py",
132
+ "websearch_to_tavily.py",
133
+ // Prompt coaching
134
+ "prompt_coach.py",
135
+ "prompt_coach_system.md",
136
+ "prompt_coach_digest.md",
137
+ // Memory system hooks
138
+ "memory_bootstrap.py",
139
+ "memory_store_reminder.py",
140
+ "vestige_before_external.py",
141
+ // Workflow hooks
142
+ "task_master_after_plan.py",
143
+ "task_master_before_impl.py",
144
+ "jira_issue_fetch.py",
145
+ // Security hooks
146
+ "wp_security_check.py",
147
+ "sql_injection_check.py",
148
+ // Atlassian prerequisite hooks
149
+ "atlassian_prereqs.py",
150
+ // Serena hooks
151
+ "serena_project_check.py",
152
+ "serena_over_grep.py",
153
+ "serena_over_read.py",
154
+ // Sequential Thinking hooks
155
+ "sequential_thinking_check.py",
156
+ // Code quality hooks
157
+ "fp_utility_check.py",
158
+ "jquery_in_wordpress.py",
159
+ "bootstrap_utility_check.py",
160
+ "composer_autoload_check.py",
161
+ "docs_organization.py"
162
+ ];
163
+ var SERENA_JETBRAINS_TOOLS = [
164
+ "mcp__serena__jet_brains_find_symbol",
165
+ "mcp__serena__jet_brains_find_referencing_symbols",
166
+ "mcp__serena__jet_brains_get_symbols_overview",
167
+ "mcp__serena__jet_brains_type_hierarchy"
168
+ ];
169
+ var ATLASSIAN_TOOLS_WITH_PREREQS = [
170
+ "mcp__claude_ai_Atlassian__getJiraIssue",
171
+ "mcp__claude_ai_Atlassian__editJiraIssue",
172
+ "mcp__claude_ai_Atlassian__createJiraIssue",
173
+ "mcp__claude_ai_Atlassian__searchJiraIssuesUsingJql",
174
+ "mcp__claude_ai_Atlassian__transitionJiraIssue",
175
+ "mcp__claude_ai_Atlassian__addCommentToJiraIssue",
176
+ "mcp__claude_ai_Atlassian__getConfluencePage",
177
+ "mcp__claude_ai_Atlassian__createConfluencePage",
178
+ "mcp__claude_ai_Atlassian__updateConfluencePage",
179
+ "mcp__claude_ai_Atlassian__searchConfluenceUsingCql",
180
+ "mcp__claude_ai_Atlassian__getAccessibleAtlassianResources",
181
+ "mcp__claude_ai_Atlassian__getTransitionsForJiraIssue"
182
+ ];
183
+ var HOOKS_CONFIG = {
184
+ hooks: {
185
+ PreToolUse: [
186
+ {
187
+ matcher: "Bash",
188
+ hooks: [
189
+ { type: "command", command: `python3 ${HOOKS_DIR}/enforce_rg_over_grep.py` },
190
+ { type: "command", command: `python3 ${HOOKS_DIR}/memory_bootstrap.py` }
191
+ ]
192
+ },
193
+ {
194
+ matcher: "Read",
195
+ hooks: [
196
+ { type: "command", command: `python3 ${HOOKS_DIR}/memory_bootstrap.py` },
197
+ { type: "command", command: `python3 ${HOOKS_DIR}/serena_over_read.py` }
198
+ ]
199
+ },
200
+ {
201
+ matcher: "Edit",
202
+ hooks: [
203
+ { type: "command", command: `python3 ${HOOKS_DIR}/memory_bootstrap.py` }
204
+ ]
205
+ },
206
+ {
207
+ matcher: "Write",
208
+ hooks: [
209
+ { type: "command", command: `python3 ${HOOKS_DIR}/memory_bootstrap.py` }
210
+ ]
211
+ },
212
+ {
213
+ matcher: "Glob",
214
+ hooks: [
215
+ { type: "command", command: `python3 ${HOOKS_DIR}/memory_bootstrap.py` }
216
+ ]
217
+ },
218
+ {
219
+ matcher: "Grep",
220
+ hooks: [
221
+ { type: "command", command: `python3 ${HOOKS_DIR}/memory_bootstrap.py` },
222
+ { type: "command", command: `python3 ${HOOKS_DIR}/serena_over_grep.py` }
223
+ ]
224
+ },
225
+ {
226
+ matcher: "mcp__tavily__tavily-extract",
227
+ hooks: [
228
+ { type: "command", command: `python3 ${HOOKS_DIR}/tavily_extract_advanced.py` },
229
+ { type: "command", command: `python3 ${HOOKS_DIR}/vestige_before_external.py` }
230
+ ]
231
+ },
232
+ {
233
+ matcher: "mcp__tavily__tavily_search",
234
+ hooks: [
235
+ { type: "command", command: `python3 ${HOOKS_DIR}/vestige_before_external.py` }
236
+ ]
237
+ },
238
+ {
239
+ matcher: "mcp__tavily__tavily_research",
240
+ hooks: [
241
+ { type: "command", command: `python3 ${HOOKS_DIR}/vestige_before_external.py` }
242
+ ]
243
+ },
244
+ {
245
+ matcher: "mcp__context7__resolve-library-id",
246
+ hooks: [
247
+ { type: "command", command: `python3 ${HOOKS_DIR}/vestige_before_external.py` }
248
+ ]
249
+ },
250
+ {
251
+ matcher: "mcp__context7__query-docs",
252
+ hooks: [
253
+ { type: "command", command: `python3 ${HOOKS_DIR}/vestige_before_external.py` }
254
+ ]
255
+ },
256
+ {
257
+ matcher: "WebFetch",
258
+ hooks: [
259
+ { type: "command", command: `python3 ${HOOKS_DIR}/webfetch_to_tavily.py` }
260
+ ]
261
+ },
262
+ {
263
+ matcher: "WebSearch",
264
+ hooks: [
265
+ { type: "command", command: `python3 ${HOOKS_DIR}/websearch_to_tavily.py` }
266
+ ]
267
+ },
268
+ // Serena WP project path checks
269
+ ...SERENA_JETBRAINS_TOOLS.map((tool) => ({
270
+ matcher: tool,
271
+ hooks: [
272
+ { type: "command", command: `python3 ${HOOKS_DIR}/serena_project_check.py` }
273
+ ]
274
+ })),
275
+ // Atlassian prerequisite checks
276
+ ...ATLASSIAN_TOOLS_WITH_PREREQS.map((tool) => ({
277
+ matcher: tool,
278
+ hooks: [
279
+ { type: "command", command: `python3 ${HOOKS_DIR}/atlassian_prereqs.py` }
280
+ ]
281
+ }))
282
+ ],
283
+ PostToolUse: [
284
+ {
285
+ matcher: "Edit",
286
+ hooks: [
287
+ { type: "command", command: `python3 ${HOOKS_DIR}/memory_store_reminder.py` },
288
+ { type: "command", command: `python3 ${HOOKS_DIR}/wp_security_check.py` },
289
+ { type: "command", command: `python3 ${HOOKS_DIR}/sql_injection_check.py` },
290
+ { type: "command", command: `python3 ${HOOKS_DIR}/fp_utility_check.py` },
291
+ { type: "command", command: `python3 ${HOOKS_DIR}/jquery_in_wordpress.py` },
292
+ { type: "command", command: `python3 ${HOOKS_DIR}/bootstrap_utility_check.py` },
293
+ { type: "command", command: `python3 ${HOOKS_DIR}/composer_autoload_check.py` }
294
+ ]
295
+ },
296
+ {
297
+ matcher: "Write",
298
+ hooks: [
299
+ { type: "command", command: `python3 ${HOOKS_DIR}/memory_store_reminder.py` },
300
+ { type: "command", command: `python3 ${HOOKS_DIR}/wp_security_check.py` },
301
+ { type: "command", command: `python3 ${HOOKS_DIR}/sql_injection_check.py` },
302
+ { type: "command", command: `python3 ${HOOKS_DIR}/fp_utility_check.py` },
303
+ { type: "command", command: `python3 ${HOOKS_DIR}/jquery_in_wordpress.py` },
304
+ { type: "command", command: `python3 ${HOOKS_DIR}/bootstrap_utility_check.py` },
305
+ { type: "command", command: `python3 ${HOOKS_DIR}/composer_autoload_check.py` },
306
+ { type: "command", command: `python3 ${HOOKS_DIR}/docs_organization.py` }
307
+ ]
308
+ },
309
+ {
310
+ matcher: "ExitPlanMode",
311
+ hooks: [
312
+ { type: "command", command: `python3 ${HOOKS_DIR}/task_master_after_plan.py` }
313
+ ]
314
+ }
315
+ ],
316
+ UserPromptSubmit: [
317
+ {
318
+ hooks: [
319
+ { type: "command", command: `python3 ${HOOKS_DIR}/prompt_coach.py` },
320
+ { type: "command", command: `python3 ${HOOKS_DIR}/jira_issue_fetch.py` },
321
+ { type: "command", command: `python3 ${HOOKS_DIR}/task_master_before_impl.py` },
322
+ { type: "command", command: `python3 ${HOOKS_DIR}/sequential_thinking_check.py` }
323
+ ]
324
+ }
325
+ ]
326
+ }
327
+ };
328
+ function mergeHooksIntoSettings() {
329
+ let settings = {};
330
+ let created = false;
331
+ if (existsSync(SETTINGS_FILE)) {
332
+ try {
333
+ const content = readFileSync(SETTINGS_FILE, "utf8");
334
+ settings = JSON.parse(content);
335
+ } catch {
336
+ settings = {};
337
+ }
338
+ } else {
339
+ created = true;
340
+ }
341
+ if (!settings.hooks) {
342
+ settings.hooks = {};
343
+ }
344
+ const settingsHooks = settings.hooks;
345
+ if (settingsHooks.PreToolUse) {
346
+ const existingHooks = settingsHooks.PreToolUse;
347
+ const newHooks = HOOKS_CONFIG.hooks.PreToolUse;
348
+ for (const newHook of newHooks) {
349
+ const existingIndex = existingHooks.findIndex((h) => h.matcher === newHook.matcher);
350
+ if (existingIndex >= 0) {
351
+ existingHooks[existingIndex] = newHook;
352
+ } else {
353
+ existingHooks.push(newHook);
354
+ }
355
+ }
356
+ } else {
357
+ settingsHooks.PreToolUse = HOOKS_CONFIG.hooks.PreToolUse;
358
+ }
359
+ if (settingsHooks.PostToolUse) {
360
+ const existingHooks = settingsHooks.PostToolUse;
361
+ const newHooks = HOOKS_CONFIG.hooks.PostToolUse;
362
+ for (const newHook of newHooks) {
363
+ const existingIndex = existingHooks.findIndex((h) => h.matcher === newHook.matcher);
364
+ if (existingIndex >= 0) {
365
+ existingHooks[existingIndex] = newHook;
366
+ } else {
367
+ existingHooks.push(newHook);
368
+ }
369
+ }
370
+ } else {
371
+ settingsHooks.PostToolUse = HOOKS_CONFIG.hooks.PostToolUse;
372
+ }
373
+ settingsHooks.UserPromptSubmit = HOOKS_CONFIG.hooks.UserPromptSubmit;
374
+ writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
375
+ return { merged: true, created };
376
+ }
377
+
378
+ // platforms/claude/adapter.ts
379
+ import { join as join2 } from "path";
380
+ import { existsSync as existsSync2, copyFileSync as copyFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
381
+ var AGENTS_DIR = join2(CLAUDE_DIR, "agents");
382
+ var ClaudeAdapter = class {
383
+ name = "claude";
384
+ displayName = "Claude Code";
385
+ configDir = CLAUDE_DIR;
386
+ detect() {
387
+ return existsSync2(CLAUDE_DIR);
388
+ }
389
+ preview(sourceDir) {
390
+ const skillItems = SKILLS_TO_INSTALL.map((skill) => ({
391
+ name: skill,
392
+ category: "skill",
393
+ destPath: join2(SKILLS_DIR, skill),
394
+ exists: existsSync2(join2(SKILLS_DIR, skill))
395
+ }));
396
+ const agentsSourceDir = join2(sourceDir, "agents");
397
+ const agentItems = existsSync2(agentsSourceDir) ? readdirSync2(agentsSourceDir).filter((f) => f.endsWith(".md")).map((file) => ({
398
+ name: file.replace(/\.md$/, ""),
399
+ category: "agent",
400
+ destPath: join2(AGENTS_DIR, file),
401
+ exists: existsSync2(join2(AGENTS_DIR, file))
402
+ })) : [];
403
+ const hooksSourceDir = join2(sourceDir, "hooks");
404
+ const hookItems = HOOKS_TO_INSTALL.map((file) => ({
405
+ name: file,
406
+ category: "hook",
407
+ destPath: join2(HOOKS_DIR, file),
408
+ exists: existsSync2(join2(HOOKS_DIR, file))
409
+ }));
410
+ return {
411
+ platform: this.name,
412
+ targetDir: CLAUDE_DIR,
413
+ items: [...skillItems, ...agentItems, ...hookItems]
414
+ };
415
+ }
416
+ installSkills(sourceDir, exclude) {
417
+ ensureDir(SKILLS_DIR);
418
+ const skills = exclude?.length ? SKILLS_TO_INSTALL.filter((s) => !exclude.includes(s)) : SKILLS_TO_INSTALL;
419
+ for (const skill of skills) {
420
+ const src = join2(sourceDir, skill);
421
+ if (existsSync2(src) && statSync2(src).isDirectory()) {
422
+ copyDirRecursive(src, join2(SKILLS_DIR, skill));
423
+ log.step(`skill: ${skill}`);
424
+ }
425
+ }
426
+ }
427
+ installAgents(sourceDir, exclude) {
428
+ ensureDir(AGENTS_DIR);
429
+ const entries = readdirSync2(sourceDir).filter((f) => f.endsWith(".md"));
430
+ const filtered = exclude?.length ? entries.filter((f) => !exclude.includes(f.replace(/\.md$/, ""))) : entries;
431
+ for (const file of filtered) {
432
+ copyFileSync2(join2(sourceDir, file), join2(AGENTS_DIR, file));
433
+ log.step(`agent: ${file}`);
434
+ }
435
+ }
436
+ installGuidelines(_pluginRoot) {
437
+ }
438
+ installHooks(sourceDir, exclude) {
439
+ ensureDir(HOOKS_DIR);
440
+ const hooks = exclude?.length ? HOOKS_TO_INSTALL.filter((f) => !exclude.includes(f)) : HOOKS_TO_INSTALL;
441
+ for (const file of hooks) {
442
+ const src = join2(sourceDir, file);
443
+ if (existsSync2(src)) {
444
+ copyFileSync2(src, join2(HOOKS_DIR, file));
445
+ log.step(`hook: ${file}`);
446
+ }
447
+ }
448
+ const { created } = mergeHooksIntoSettings();
449
+ log.info(created ? "Created ~/.claude/settings.json with hooks" : "Merged hooks into ~/.claude/settings.json");
450
+ }
451
+ postInstall() {
452
+ log.warn("Note: Direct install is not the recommended approach for Claude Code.");
453
+ log.info("Prefer the plugin system instead:");
454
+ log.info(" /plugin marketplace add Soabirw/ima-claude");
455
+ log.info(" /plugin install ima-claude");
456
+ }
457
+ };
458
+
459
+ // platforms/junie/adapter.ts
460
+ import { join as join3 } from "path";
461
+ import { homedir as homedir2 } from "os";
462
+ import { existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
463
+ var JUNIE_DIR = join3(homedir2(), ".junie");
464
+ var JUNIE_SKILLS_DIR = join3(JUNIE_DIR, "skills");
465
+ var JUNIE_AGENTS_DIR = join3(JUNIE_DIR, "agents");
466
+ var JUNIE_GUIDELINES_FILE = join3(JUNIE_DIR, "AGENTS.md");
467
+ function parseFrontmatter(content) {
468
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
469
+ if (!match) return { frontmatter: {}, body: content };
470
+ const frontmatter = {};
471
+ for (const line of match[1].split("\n")) {
472
+ const colonIdx = line.indexOf(":");
473
+ if (colonIdx === -1) continue;
474
+ const key = line.slice(0, colonIdx).trim();
475
+ const value = line.slice(colonIdx + 1).trim();
476
+ if (key) frontmatter[key] = value;
477
+ }
478
+ return { frontmatter, body: match[2] };
479
+ }
480
+ function serializeFrontmatter(frontmatter, body) {
481
+ const lines = Object.entries(frontmatter).map(([k, v]) => `${k}: ${v}`);
482
+ return `---
483
+ ${lines.join("\n")}
484
+ ---
485
+ ${body}`;
486
+ }
487
+ function transformAgentForJunie(content) {
488
+ const { frontmatter, body } = parseFrontmatter(content);
489
+ const { permissionMode: _dropped, ...junieFrontmatter } = frontmatter;
490
+ return serializeFrontmatter(junieFrontmatter, body);
491
+ }
492
+ function generateAgentsMd() {
493
+ return `# ima-claude: AI Coding Agent Guidelines
494
+
495
+ > Generated by ima-claude v${VERSION} for Junie CLI.
496
+ > Source: https://github.com/Soabirw/ima-claude
497
+
498
+ ## Default Persona: The Practitioner
499
+
500
+ A 25-year software development veteran. FP-first, composition-minded, anti-over-engineering.
501
+ Uses "we" not "I" \u2014 collaborative, humble, light-hearted. "Slow is smooth, smooth is fast."
502
+
503
+ **Philosophy**: Simple > Complex | Evidence > Assumptions | Native > Utilities | MVP > Enterprise
504
+
505
+ ---
506
+
507
+ ## Memory Routing
508
+
509
+ | Store what | Where | Why |
510
+ |---|---|---|
511
+ | Decisions, preferences, patterns, bugs | Vestige \`smart_ingest\` | Fades naturally if not referenced |
512
+ | Reference material (docs, standards, PRDs) | Qdrant \`qdrant-store\` | Permanent library |
513
+ | Session state, task progress | Serena \`write_memory\` | Project-scoped workbench |
514
+ | Future reminders | Vestige \`intention\` | Surfaces at next session |
515
+
516
+ At session start, check memory before asking questions:
517
+ - Vestige: search for user preferences and project context
518
+ - Vestige: check for pending reminders/intentions
519
+ - Serena: list memories if in a Serena-activated project
520
+
521
+ Auto-store: "I prefer..." \u2192 Vestige preference. "Let's go with X because..." \u2192 Vestige decision. "The reason this failed..." \u2192 Vestige bug.
522
+
523
+ After completing work: store outcome in Vestige, reference material in Qdrant, session state in Serena.
524
+
525
+ ---
526
+
527
+ ## Orchestrator Protocol
528
+
529
+ You are the Orchestrator. Plan and delegate. Do NOT implement directly.
530
+ - Non-trivial work \u2192 task-planner (decompose) \u2192 task-runner (delegate)
531
+ - Trivial = single file, < 5 lines, no judgment calls
532
+ - Model selection: opus for orchestration, sonnet for implementation (default), haiku for lookups
533
+
534
+ ---
535
+
536
+ ## Available Agents
537
+
538
+ Delegate to named agents \u2014 they enforce model, tools, and permissions automatically.
539
+
540
+ | Agent | Model | Use For |
541
+ |---|---|---|
542
+ | \`explorer\` | haiku | File discovery, codebase exploration |
543
+ | \`implementer\` | sonnet | Feature dev, bug fixes, refactoring |
544
+ | \`reviewer\` | sonnet | Code review, security audit, FP checks |
545
+ | \`wp-developer\` | sonnet | WordPress plugins, themes, WP-CLI, forms |
546
+ | \`memory\` | sonnet | Memory search, storage, consolidation |
547
+
548
+ ---
549
+
550
+ ## Code Navigation (Serena)
551
+
552
+ When Serena MCP is available, **prefer Serena over Read/Grep for code investigation.** 40-70% token savings.
553
+
554
+ | Instead of | Use |
555
+ |---|---|
556
+ | Read file to understand structure | Serena get_symbols_overview |
557
+ | Grep for class/function definition | Serena find_symbol |
558
+ | Grep for callers/references | Serena find_referencing_symbols |
559
+
560
+ Use Read only when you need the actual implementation body of a known, specific symbol.
561
+
562
+ ---
563
+
564
+ ## Complex Reasoning
565
+
566
+ Use sequential thinking before acting on:
567
+ - Debugging / root cause analysis / "why is this failing"
568
+ - Trade-off evaluation / "which approach"
569
+ - Architectural decisions / design choices
570
+ - Multi-step investigations where approach may change
571
+
572
+ ---
573
+
574
+ ## MCP Tool Routing
575
+
576
+ | Signal | Preferred Tool |
577
+ |---|---|
578
+ | "latest", "2025/2026", "what's new" | Tavily search |
579
+ | Library/framework API question | Context7 |
580
+ | URL content extraction | Tavily extract (use advanced for complex pages) |
581
+
582
+ Before web tools: check internal knowledge \u2192 Context7 \u2192 then Tavily.
583
+ Before external lookups: check Vestige memory first.
584
+
585
+ ---
586
+
587
+ ## Search Preference
588
+
589
+ Always prefer \`rg\` (ripgrep) over grep/find. Faster, respects .gitignore, recursive by default.
590
+
591
+ ---
592
+
593
+ ## Security
594
+
595
+ - Verify nonce usage and input sanitization in WordPress PHP code
596
+ - Never concatenate user input directly into SQL \u2014 use parameterized queries
597
+ - Check for XSS, CSRF, and OWASP top 10 vulnerabilities in written code
598
+
599
+ ---
600
+
601
+ ## Code Style
602
+
603
+ - Don't create custom FP utility functions (pipe, compose, curry) \u2014 use language-native patterns or established libraries
604
+ - In WordPress JavaScript context, use jQuery patterns when jQuery is already loaded
605
+ - Prefer Bootstrap utility classes over custom CSS overrides
606
+ - Run \`composer dump-autoload\` after creating new PHP files
607
+ - Use proper Edit tools for code changes, not sed/awk
608
+
609
+ ---
610
+
611
+ ## Documentation
612
+
613
+ Follow the three-tier documentation system:
614
+ - **Active** \u2014 Living docs, kept current (README, API docs, architecture)
615
+ - **Archive** \u2014 Historical reference, rarely updated (decisions, post-mortems)
616
+ - **Transient** \u2014 Ephemeral, git-ignored (session notes, scratch)
617
+ `;
618
+ }
619
+ var JunieAdapter = class {
620
+ name = "junie";
621
+ displayName = "Junie CLI";
622
+ configDir = JUNIE_DIR;
623
+ detect() {
624
+ return existsSync3(JUNIE_DIR);
625
+ }
626
+ preview(sourceDir) {
627
+ const skillItems = SKILLS_TO_INSTALL.map((skill) => ({
628
+ name: skill,
629
+ category: "skill",
630
+ destPath: join3(JUNIE_SKILLS_DIR, skill),
631
+ exists: existsSync3(join3(JUNIE_SKILLS_DIR, skill))
632
+ })).filter((item) => existsSync3(join3(sourceDir, "skills", item.name)));
633
+ const agentsDir = join3(sourceDir, "agents");
634
+ const agentItems = existsSync3(agentsDir) ? readdirSync3(agentsDir).filter((f) => f.endsWith(".md")).map((file) => ({
635
+ name: file.replace(/\.md$/, ""),
636
+ category: "agent",
637
+ destPath: join3(JUNIE_AGENTS_DIR, file),
638
+ exists: existsSync3(join3(JUNIE_AGENTS_DIR, file))
639
+ })) : [];
640
+ const guidelineItem = {
641
+ name: "AGENTS.md",
642
+ category: "guideline",
643
+ destPath: JUNIE_GUIDELINES_FILE,
644
+ exists: existsSync3(JUNIE_GUIDELINES_FILE)
645
+ };
646
+ return {
647
+ platform: this.name,
648
+ targetDir: JUNIE_DIR,
649
+ items: [...skillItems, ...agentItems, guidelineItem]
650
+ };
651
+ }
652
+ installSkills(sourceDir, exclude) {
653
+ ensureDir(JUNIE_SKILLS_DIR);
654
+ const skills = exclude?.length ? SKILLS_TO_INSTALL.filter((s) => !exclude.includes(s)) : SKILLS_TO_INSTALL;
655
+ for (const skill of skills) {
656
+ const src = join3(sourceDir, skill);
657
+ if (existsSync3(src) && statSync3(src).isDirectory()) {
658
+ copyDirRecursive(src, join3(JUNIE_SKILLS_DIR, skill));
659
+ log.step(`skill: ${skill}`);
660
+ }
661
+ }
662
+ }
663
+ installAgents(sourceDir, exclude) {
664
+ ensureDir(JUNIE_AGENTS_DIR);
665
+ const entries = readdirSync3(sourceDir).filter((f) => f.endsWith(".md")).filter((f) => !exclude?.includes(f.replace(/\.md$/, "")));
666
+ for (const file of entries) {
667
+ const content = readFileSync2(join3(sourceDir, file), "utf8");
668
+ const transformed = transformAgentForJunie(content);
669
+ writeFileSync2(join3(JUNIE_AGENTS_DIR, file), transformed);
670
+ log.step(`agent: ${file}`);
671
+ }
672
+ }
673
+ installGuidelines(_pluginRoot) {
674
+ ensureDir(JUNIE_DIR);
675
+ writeFileSync2(JUNIE_GUIDELINES_FILE, generateAgentsMd());
676
+ log.step(`guidelines: ${JUNIE_GUIDELINES_FILE}`);
677
+ }
678
+ postInstall() {
679
+ log.info("Junie install complete. Verify:");
680
+ log.info(` Skills: ${JUNIE_SKILLS_DIR}`);
681
+ log.info(` Agents: ${JUNIE_AGENTS_DIR}`);
682
+ log.info(` Guidelines: ${JUNIE_GUIDELINES_FILE}`);
683
+ log.warn("Note: Junie does not support hooks \u2014 behavioral guidelines are in AGENTS.md instead.");
684
+ }
685
+ };
686
+
687
+ // platforms/shared/detector.ts
688
+ var ADAPTERS = [
689
+ new ClaudeAdapter(),
690
+ new JunieAdapter()
691
+ ];
692
+ function detectPlatforms() {
693
+ return ADAPTERS.map((adapter) => {
694
+ const detected = adapter.detect();
695
+ const note = adapter.name === "claude" && detected ? "Recommended: install via plugin marketplace instead" : void 0;
696
+ return { adapter, detected, note };
697
+ });
698
+ }
699
+ function getAdapter(name) {
700
+ return ADAPTERS.find((a) => a.name === name);
701
+ }
702
+
703
+ // platforms/shared/installer.ts
704
+ import { join as join5 } from "path";
705
+
706
+ // platforms/shared/types.ts
707
+ import { join as join4, resolve, dirname as dirname2 } from "path";
708
+ import { existsSync as existsSync4 } from "fs";
709
+ import { fileURLToPath } from "url";
710
+ function findPackageRoot() {
711
+ let dir;
712
+ try {
713
+ dir = dirname2(fileURLToPath(import.meta.url));
714
+ } catch {
715
+ dir = typeof __dirname !== "undefined" ? __dirname : resolve(".");
716
+ }
717
+ for (let i = 0; i < 10; i++) {
718
+ if (existsSync4(join4(dir, "package.json"))) return dir;
719
+ const parent = join4(dir, "..");
720
+ if (parent === dir) break;
721
+ dir = parent;
722
+ }
723
+ return process.cwd();
724
+ }
725
+ var PLUGIN_SOURCE = join4(
726
+ findPackageRoot(),
727
+ "plugins",
728
+ "ima-claude"
729
+ );
730
+
731
+ // platforms/shared/installer.ts
732
+ function formatItemList(items, category) {
733
+ const filtered = items.filter((i) => i.category === category);
734
+ if (filtered.length === 0) return "";
735
+ const overrideCount = filtered.filter((i) => i.exists).length;
736
+ const overrideNote = overrideCount > 0 ? ` ${colors.yellow}(${overrideCount} existing will be overwritten)${colors.reset}` : "";
737
+ const names = filtered.map((i) => {
738
+ const marker = i.exists ? `${colors.yellow}*${colors.reset}` : " ";
739
+ return `${marker} ${i.name}`;
740
+ });
741
+ const label = category.charAt(0).toUpperCase() + category.slice(1) + "s";
742
+ return ` ${colors.bright}${label} (${filtered.length})${colors.reset}${overrideNote}
743
+ ${names.map((n) => ` ${n}`).join("\n")}`;
744
+ }
745
+ function showPreview(adapter, items) {
746
+ console.log(
747
+ `
748
+ ${colors.bright}Install preview for ${adapter.displayName}${colors.reset}`
749
+ );
750
+ console.log(` Target: ${adapter.configDir}
751
+ `);
752
+ const categories = ["skill", "agent", "hook", "guideline"];
753
+ for (const cat of categories) {
754
+ const section = formatItemList(items, cat);
755
+ if (section) console.log(section + "\n");
756
+ }
757
+ const existingCount = items.filter((i) => i.exists).length;
758
+ if (existingCount > 0) {
759
+ console.log(
760
+ ` ${colors.yellow}*${colors.reset} = exists and will be overwritten (${existingCount} files)
761
+ `
762
+ );
763
+ }
764
+ }
765
+ async function promptExclusions(items) {
766
+ const filter = {};
767
+ console.log(`${colors.cyan}Options:${colors.reset}`);
768
+ console.log(` [Enter] Install all`);
769
+ console.log(` [e] Exclude specific items`);
770
+ console.log(` [c] Cancel
771
+ `);
772
+ const choice = await prompt("Selection: ");
773
+ if (choice.toLowerCase() === "c") {
774
+ return { excludeSkills: items.filter((i) => i.category === "skill").map((i) => i.name) };
775
+ }
776
+ if (choice.toLowerCase() !== "e") {
777
+ return filter;
778
+ }
779
+ const skills = items.filter((i) => i.category === "skill");
780
+ const agents = items.filter((i) => i.category === "agent");
781
+ const hooks = items.filter((i) => i.category === "hook");
782
+ const guidelines = items.filter((i) => i.category === "guideline");
783
+ if (skills.length > 0) {
784
+ console.log(
785
+ `
786
+ ${colors.bright}Skills${colors.reset} (${skills.length} total)`
787
+ );
788
+ console.log(
789
+ ` Enter names to EXCLUDE (comma-separated), or press Enter to keep all:`
790
+ );
791
+ console.log(
792
+ ` ${colors.cyan}Available:${colors.reset} ${skills.map((s) => s.name).join(", ")}
793
+ `
794
+ );
795
+ const excluded = await prompt(" Exclude: ");
796
+ if (excluded) {
797
+ filter.excludeSkills = excluded.split(",").map((s) => s.trim()).filter(Boolean);
798
+ }
799
+ }
800
+ if (agents.length > 0) {
801
+ console.log(
802
+ `
803
+ ${colors.bright}Agents${colors.reset} (${agents.length} total)`
804
+ );
805
+ console.log(
806
+ ` Enter names to EXCLUDE (comma-separated), or press Enter to keep all:`
807
+ );
808
+ console.log(
809
+ ` ${colors.cyan}Available:${colors.reset} ${agents.map((a) => a.name).join(", ")}
810
+ `
811
+ );
812
+ const excluded = await prompt(" Exclude: ");
813
+ if (excluded) {
814
+ filter.excludeAgents = excluded.split(",").map((s) => s.trim()).filter(Boolean);
815
+ }
816
+ }
817
+ if (hooks.length > 0) {
818
+ console.log(
819
+ `
820
+ ${colors.bright}Hooks${colors.reset} (${hooks.length} total)`
821
+ );
822
+ console.log(
823
+ ` Enter names to EXCLUDE (comma-separated), or press Enter to keep all:`
824
+ );
825
+ console.log(
826
+ ` ${colors.cyan}Available:${colors.reset} ${hooks.map((h) => h.name).join(", ")}
827
+ `
828
+ );
829
+ const excluded = await prompt(" Exclude: ");
830
+ if (excluded) {
831
+ filter.excludeHooks = excluded.split(",").map((s) => s.trim()).filter(Boolean);
832
+ }
833
+ }
834
+ if (guidelines.length > 0) {
835
+ const skip = !await promptYesNo("\n Install guidelines?", true);
836
+ filter.skipGuidelines = skip;
837
+ }
838
+ return filter;
839
+ }
840
+ async function installForPlatform(adapter, options = {}) {
841
+ const skillsSource = join5(PLUGIN_SOURCE, "skills");
842
+ const agentsSource = join5(PLUGIN_SOURCE, "agents");
843
+ const hooksSource = join5(PLUGIN_SOURCE, "hooks");
844
+ const preview = adapter.preview(PLUGIN_SOURCE);
845
+ showPreview(adapter, preview.items);
846
+ const filter = await promptExclusions(preview.items);
847
+ const allSkills = preview.items.filter((i) => i.category === "skill");
848
+ if (filter.excludeSkills && filter.excludeSkills.length >= allSkills.length) {
849
+ console.log("\nInstallation cancelled.\n");
850
+ return;
851
+ }
852
+ const excludedCount = (filter.excludeSkills?.length ?? 0) + (filter.excludeAgents?.length ?? 0) + (filter.excludeHooks?.length ?? 0) + (filter.skipGuidelines ? 1 : 0);
853
+ const totalItems = preview.items.length - excludedCount;
854
+ const proceed = await promptYesNo(
855
+ `
856
+ Install ${totalItems} items to ${adapter.configDir}?`
857
+ );
858
+ if (!proceed) {
859
+ console.log("\nInstallation cancelled.\n");
860
+ return;
861
+ }
862
+ console.log(
863
+ `
864
+ ${colors.bright}Installing for ${adapter.displayName}${colors.reset}
865
+ `
866
+ );
867
+ log.step(`Installing skills...`);
868
+ adapter.installSkills(skillsSource, filter.excludeSkills);
869
+ log.success(`Skills installed`);
870
+ log.step(`Installing agents...`);
871
+ adapter.installAgents(agentsSource, filter.excludeAgents);
872
+ log.success(`Agents installed`);
873
+ if (!filter.skipGuidelines) {
874
+ log.step(`Installing guidelines...`);
875
+ adapter.installGuidelines(PLUGIN_SOURCE);
876
+ log.success(`Guidelines installed`);
877
+ }
878
+ if (adapter.installHooks) {
879
+ log.step(`Installing hooks...`);
880
+ adapter.installHooks(hooksSource, filter.excludeHooks);
881
+ log.success(`Hooks installed`);
882
+ }
883
+ if (adapter.configureMcp && !options.skipMcp) {
884
+ log.step(`Configuring MCP servers...`);
885
+ await adapter.configureMcp();
886
+ log.success(`MCP configured`);
887
+ }
888
+ if (adapter.postInstall) {
889
+ adapter.postInstall();
890
+ }
891
+ console.log(
892
+ `
893
+ ${colors.green}Done!${colors.reset} ${adapter.displayName} installation complete.
894
+ `
895
+ );
896
+ }
897
+
898
+ // scripts/cli.ts
899
+ var args = process.argv.slice(2);
900
+ var command = args[0];
901
+ function showHelp() {
902
+ console.log(`
903
+ ${colors.bright}ima-claude v${VERSION}${colors.reset}
904
+ IMA's AI coding agent skills - FP patterns, architecture guidance, and team standards.
905
+
906
+ ${colors.cyan}Usage:${colors.reset}
907
+ ima-claude <command> [options]
908
+
909
+ ${colors.cyan}Commands:${colors.reset}
910
+ install Interactive install (auto-detects platforms)
911
+ install --target X Install for specific platform (claude, junie)
912
+ upgrade Upgrade installed skills to latest version
913
+ detect Show detected platforms
914
+ help Show this help message
915
+
916
+ ${colors.cyan}Examples:${colors.reset}
917
+ npx ima-claude install
918
+ npx ima-claude install --target junie
919
+ npx ima-claude detect
920
+
921
+ ${colors.cyan}More Info:${colors.reset}
922
+ https://github.com/Soabirw/ima-claude
923
+ `);
924
+ }
925
+ function showDetected() {
926
+ const platforms = detectPlatforms();
927
+ console.log(`
928
+ ${colors.bright}Detected AI Coding Agents${colors.reset}
929
+ `);
930
+ for (const { adapter, detected, note } of platforms) {
931
+ const icon = detected ? `${colors.green}+${colors.reset}` : `${colors.red}-${colors.reset}`;
932
+ const status = detected ? "found" : "not detected";
933
+ console.log(` ${icon} ${adapter.displayName} (${status})`);
934
+ if (note && detected) {
935
+ console.log(` ${colors.yellow}${note}${colors.reset}`);
936
+ }
937
+ }
938
+ console.log("");
939
+ }
940
+ async function interactiveInstall() {
941
+ console.log(`
942
+ ${colors.bright}ima-claude v${VERSION} \u2014 Multi-Platform Installer${colors.reset}`);
943
+ console.log("Detecting installed AI coding agents...\n");
944
+ const platforms = detectPlatforms();
945
+ const detected = platforms.filter((p) => p.detected);
946
+ const notDetected = platforms.filter((p) => !p.detected);
947
+ for (const { adapter, note } of detected) {
948
+ console.log(` ${colors.green}+${colors.reset} ${adapter.displayName}`);
949
+ if (adapter.name === "claude" && note) {
950
+ console.log(
951
+ ` ${colors.yellow}Tip:${colors.reset} For Claude Code, the plugin marketplace is recommended:`
952
+ );
953
+ console.log(
954
+ ` ${colors.cyan}/plugin marketplace add Soabirw/ima-claude${colors.reset}`
955
+ );
956
+ console.log(
957
+ ` ${colors.cyan}/plugin install ima-claude${colors.reset}`
958
+ );
959
+ }
960
+ }
961
+ for (const { adapter } of notDetected) {
962
+ console.log(` ${colors.red}-${colors.reset} ${adapter.displayName} (not detected)`);
963
+ }
964
+ if (detected.length === 0) {
965
+ console.log(
966
+ `
967
+ ${colors.yellow}No supported platforms detected.${colors.reset}`
968
+ );
969
+ console.log("Install Claude Code or Junie CLI first, then run this installer again.\n");
970
+ return;
971
+ }
972
+ console.log("");
973
+ const toInstall = [];
974
+ for (const { adapter } of detected) {
975
+ if (adapter.name === "claude") {
976
+ const proceed = await promptYesNo(
977
+ `Install for ${adapter.displayName}? (Plugin marketplace is preferred)`,
978
+ false
979
+ );
980
+ if (proceed) toInstall.push(adapter);
981
+ } else {
982
+ const proceed = await promptYesNo(
983
+ `Install for ${adapter.displayName}?`,
984
+ true
985
+ );
986
+ if (proceed) toInstall.push(adapter);
987
+ }
988
+ }
989
+ if (toInstall.length === 0) {
990
+ console.log("\nNo platforms selected. Exiting.\n");
991
+ return;
992
+ }
993
+ for (const adapter of toInstall) {
994
+ await installForPlatform(adapter);
995
+ }
996
+ }
997
+ async function targetedInstall(targetName) {
998
+ const adapter = getAdapter(targetName);
999
+ if (!adapter) {
1000
+ log.error(`Unknown target: ${targetName}`);
1001
+ console.log("Available targets: claude, junie");
1002
+ process.exit(1);
1003
+ }
1004
+ if (!adapter.detect()) {
1005
+ log.warn(
1006
+ `${adapter.displayName} not detected at ${adapter.configDir}`
1007
+ );
1008
+ const proceed = await promptYesNo("Install anyway?", false);
1009
+ if (!proceed) {
1010
+ console.log("Exiting.\n");
1011
+ return;
1012
+ }
1013
+ }
1014
+ if (adapter.name === "claude") {
1015
+ console.log(
1016
+ `
1017
+ ${colors.yellow}Tip:${colors.reset} For Claude Code, the plugin marketplace is recommended:`
1018
+ );
1019
+ console.log(
1020
+ ` ${colors.cyan}/plugin marketplace add Soabirw/ima-claude${colors.reset}`
1021
+ );
1022
+ console.log(
1023
+ ` ${colors.cyan}/plugin install ima-claude${colors.reset}
1024
+ `
1025
+ );
1026
+ const proceed = await promptYesNo("Continue with direct install?", false);
1027
+ if (!proceed) return;
1028
+ }
1029
+ await installForPlatform(adapter);
1030
+ }
1031
+ async function main() {
1032
+ switch (command) {
1033
+ case "install": {
1034
+ const targetIdx = args.indexOf("--target");
1035
+ if (targetIdx !== -1 && args[targetIdx + 1]) {
1036
+ await targetedInstall(args[targetIdx + 1]);
1037
+ } else {
1038
+ await interactiveInstall();
1039
+ }
1040
+ break;
1041
+ }
1042
+ case "detect":
1043
+ showDetected();
1044
+ break;
1045
+ case "upgrade":
1046
+ console.log("Upgrade runs the same as install \u2014 detecting and updating.\n");
1047
+ await interactiveInstall();
1048
+ break;
1049
+ case "help":
1050
+ case "--help":
1051
+ case "-h":
1052
+ case void 0:
1053
+ showHelp();
1054
+ break;
1055
+ default:
1056
+ console.error(`Unknown command: ${command}`);
1057
+ showHelp();
1058
+ process.exit(1);
1059
+ }
1060
+ }
1061
+ main().catch((err) => {
1062
+ console.error(`Error: ${err.message}`);
1063
+ process.exit(1);
1064
+ });