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
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
|
+
});
|