@hugo.bastidas/minimax-plugin 0.1.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/.claude-plugin/plugin.json +13 -0
- package/.mcp.json +3 -0
- package/AGENTS.md +37 -0
- package/README.md +263 -0
- package/codex-plugin.toml +3 -0
- package/dist/artifacts/metadata.d.ts +3 -0
- package/dist/artifacts/metadata.d.ts.map +1 -0
- package/dist/artifacts/metadata.js +8 -0
- package/dist/artifacts/metadata.js.map +1 -0
- package/dist/artifacts/runs.d.ts +27 -0
- package/dist/artifacts/runs.d.ts.map +1 -0
- package/dist/artifacts/runs.js +43 -0
- package/dist/artifacts/runs.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +31 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/anthropic.d.ts +3 -0
- package/dist/client/anthropic.d.ts.map +1 -0
- package/dist/client/anthropic.js +31 -0
- package/dist/client/anthropic.js.map +1 -0
- package/dist/client/minimax.d.ts +19 -0
- package/dist/client/minimax.d.ts.map +1 -0
- package/dist/client/minimax.js +16 -0
- package/dist/client/minimax.js.map +1 -0
- package/dist/client/openai.d.ts +3 -0
- package/dist/client/openai.d.ts.map +1 -0
- package/dist/client/openai.js +28 -0
- package/dist/client/openai.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +34 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/claude-routing-reminder.d.ts +2 -0
- package/dist/hooks/claude-routing-reminder.d.ts.map +1 -0
- package/dist/hooks/claude-routing-reminder.js +12 -0
- package/dist/hooks/claude-routing-reminder.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/install/claude-config.d.ts +15 -0
- package/dist/install/claude-config.d.ts.map +1 -0
- package/dist/install/claude-config.js +130 -0
- package/dist/install/claude-config.js.map +1 -0
- package/dist/install/codex-config.d.ts +11 -0
- package/dist/install/codex-config.d.ts.map +1 -0
- package/dist/install/codex-config.js +83 -0
- package/dist/install/codex-config.js.map +1 -0
- package/dist/install/global-instructions.d.ts +9 -0
- package/dist/install/global-instructions.d.ts.map +1 -0
- package/dist/install/global-instructions.js +90 -0
- package/dist/install/global-instructions.js.map +1 -0
- package/dist/install/index.d.ts +6 -0
- package/dist/install/index.d.ts.map +1 -0
- package/dist/install/index.js +6 -0
- package/dist/install/index.js.map +1 -0
- package/dist/install/native-multimodal-config.d.ts +4 -0
- package/dist/install/native-multimodal-config.d.ts.map +1 -0
- package/dist/install/native-multimodal-config.js +50 -0
- package/dist/install/native-multimodal-config.js.map +1 -0
- package/dist/install/opencode-config.d.ts +13 -0
- package/dist/install/opencode-config.d.ts.map +1 -0
- package/dist/install/opencode-config.js +65 -0
- package/dist/install/opencode-config.js.map +1 -0
- package/dist/install/shell-alias.d.ts +9 -0
- package/dist/install/shell-alias.d.ts.map +1 -0
- package/dist/install/shell-alias.js +86 -0
- package/dist/install/shell-alias.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +125 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/ask.d.ts +18 -0
- package/dist/tools/ask.d.ts.map +1 -0
- package/dist/tools/ask.js +18 -0
- package/dist/tools/ask.js.map +1 -0
- package/dist/tools/delegate.d.ts +15 -0
- package/dist/tools/delegate.d.ts.map +1 -0
- package/dist/tools/delegate.js +31 -0
- package/dist/tools/delegate.js.map +1 -0
- package/dist/tools/inspect-run.d.ts +10 -0
- package/dist/tools/inspect-run.d.ts.map +1 -0
- package/dist/tools/inspect-run.js +23 -0
- package/dist/tools/inspect-run.js.map +1 -0
- package/dist/tools/mmx-cli.d.ts +92 -0
- package/dist/tools/mmx-cli.d.ts.map +1 -0
- package/dist/tools/mmx-cli.js +211 -0
- package/dist/tools/mmx-cli.js.map +1 -0
- package/dist/tools/model-status.d.ts +8 -0
- package/dist/tools/model-status.d.ts.map +1 -0
- package/dist/tools/model-status.js +28 -0
- package/dist/tools/model-status.js.map +1 -0
- package/dist/tools/propose-patch.d.ts +14 -0
- package/dist/tools/propose-patch.d.ts.map +1 -0
- package/dist/tools/propose-patch.js +26 -0
- package/dist/tools/propose-patch.js.map +1 -0
- package/dist/tools/review-diff.d.ts +14 -0
- package/dist/tools/review-diff.d.ts.map +1 -0
- package/dist/tools/review-diff.js +52 -0
- package/dist/tools/review-diff.js.map +1 -0
- package/dist/tools/summarize.d.ts +13 -0
- package/dist/tools/summarize.d.ts.map +1 -0
- package/dist/tools/summarize.js +28 -0
- package/dist/tools/summarize.js.map +1 -0
- package/dist/usage/stats.d.ts +34 -0
- package/dist/usage/stats.d.ts.map +1 -0
- package/dist/usage/stats.js +201 -0
- package/dist/usage/stats.js.map +1 -0
- package/dist/usage/tool-usage.d.ts +11 -0
- package/dist/usage/tool-usage.d.ts.map +1 -0
- package/dist/usage/tool-usage.js +24 -0
- package/dist/usage/tool-usage.js.map +1 -0
- package/dist/validation/limits.d.ts +8 -0
- package/dist/validation/limits.d.ts.map +1 -0
- package/dist/validation/limits.js +8 -0
- package/dist/validation/limits.js.map +1 -0
- package/dist/validation/patch.d.ts +7 -0
- package/dist/validation/patch.d.ts.map +1 -0
- package/dist/validation/patch.js +20 -0
- package/dist/validation/patch.js.map +1 -0
- package/dist/workspace/collect-files.d.ts +19 -0
- package/dist/workspace/collect-files.d.ts.map +1 -0
- package/dist/workspace/collect-files.js +60 -0
- package/dist/workspace/collect-files.js.map +1 -0
- package/dist/workspace/ignore.d.ts +3 -0
- package/dist/workspace/ignore.d.ts.map +1 -0
- package/dist/workspace/ignore.js +13 -0
- package/dist/workspace/ignore.js.map +1 -0
- package/dist/workspace/paths.d.ts +3 -0
- package/dist/workspace/paths.d.ts.map +1 -0
- package/dist/workspace/paths.js +18 -0
- package/dist/workspace/paths.js.map +1 -0
- package/dist/workspace/secrets.d.ts +4 -0
- package/dist/workspace/secrets.d.ts.map +1 -0
- package/dist/workspace/secrets.js +32 -0
- package/dist/workspace/secrets.js.map +1 -0
- package/package.json +64 -0
- package/scripts/claude-config.mjs +3 -0
- package/scripts/codex-config.mjs +3 -0
- package/scripts/install.mjs +548 -0
- package/scripts/opencode-config.mjs +3 -0
- package/skills/minimax-delegation/SKILL.md +24 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { select, multiselect, text, confirm, outro, isCancel } from '@clack/prompts';
|
|
3
|
+
import {
|
|
4
|
+
writeClaudeConfig,
|
|
5
|
+
removeClaudeConfig,
|
|
6
|
+
readClaudeMiniMaxEnv,
|
|
7
|
+
writeClaudeHooksConfig,
|
|
8
|
+
removeClaudeHooksConfig,
|
|
9
|
+
writeClaudeGlobalInstructions,
|
|
10
|
+
removeClaudeGlobalInstructions,
|
|
11
|
+
} from '../dist/install/claude-config.js';
|
|
12
|
+
import {
|
|
13
|
+
writeOpenCodeConfig,
|
|
14
|
+
removeOpenCodeConfig,
|
|
15
|
+
writeOpenCodeGlobalInstructions,
|
|
16
|
+
removeOpenCodeGlobalInstructions,
|
|
17
|
+
} from '../dist/install/opencode-config.js';
|
|
18
|
+
import {
|
|
19
|
+
writeCodexConfig,
|
|
20
|
+
removeCodexConfig,
|
|
21
|
+
readCodexMiniMaxEnv,
|
|
22
|
+
writeCodexGlobalInstructions,
|
|
23
|
+
removeCodexGlobalInstructions,
|
|
24
|
+
} from '../dist/install/codex-config.js';
|
|
25
|
+
import { readOpenCodeMiniMaxEnv } from '../dist/install/opencode-config.js';
|
|
26
|
+
import { removeClaudeNativeMultimodalConfig, removeOpenCodeNativeMultimodalConfig, removeCodexNativeMultimodalConfig } from '../dist/install/native-multimodal-config.js';
|
|
27
|
+
import { writeShellAlias, removeShellAlias, isShellAliasInstalled, isLegacyAliasInstalled, shellRcPath, DEFAULT_ALIAS_NAME } from '../dist/install/shell-alias.js';
|
|
28
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
29
|
+
import { homedir } from 'node:os';
|
|
30
|
+
import { join, dirname } from 'node:path';
|
|
31
|
+
import { execSync } from 'node:child_process';
|
|
32
|
+
|
|
33
|
+
const CLAUDE_CONFIG_PATH = join(homedir(), '.claude.json');
|
|
34
|
+
const CLAUDE_SETTINGS_PATH = join(homedir(), '.claude', 'settings.json');
|
|
35
|
+
const CLAUDE_GLOBAL_INSTRUCTIONS_PATH = join(homedir(), '.claude', 'CLAUDE.md');
|
|
36
|
+
const OPENCODE_CONFIG_PATH = join(homedir(), '.config', 'opencode', 'opencode.jsonc');
|
|
37
|
+
const OPENCODE_GLOBAL_INSTRUCTIONS_PATH = join(homedir(), '.config', 'opencode', 'AGENTS.md');
|
|
38
|
+
const CODEX_CONFIG_PATH = join(homedir(), '.codex', 'config.toml');
|
|
39
|
+
const CODEX_GLOBAL_INSTRUCTIONS_PATH = join(homedir(), '.codex', 'AGENTS.md');
|
|
40
|
+
|
|
41
|
+
// Rutas por host, reutilizadas por instalación, desinstalación y estado.
|
|
42
|
+
const HOST_PATHS = {
|
|
43
|
+
claude: { config: CLAUDE_CONFIG_PATH, hooks: CLAUDE_SETTINGS_PATH, instructions: CLAUDE_GLOBAL_INSTRUCTIONS_PATH },
|
|
44
|
+
codex: { config: CODEX_CONFIG_PATH, instructions: CODEX_GLOBAL_INSTRUCTIONS_PATH },
|
|
45
|
+
opencode: { config: OPENCODE_CONFIG_PATH, instructions: OPENCODE_GLOBAL_INSTRUCTIONS_PATH },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Expande el target elegido a la lista concreta de hosts afectados.
|
|
49
|
+
function hostsForTarget(target) {
|
|
50
|
+
if (Array.isArray(target)) return target;
|
|
51
|
+
return target === 'all' ? ['claude', 'codex', 'opencode'] : [target];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getDistPath() {
|
|
55
|
+
return join(import.meta.dirname ?? '.', '..', 'dist', 'index.js');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getClaudeHookPath() {
|
|
59
|
+
return join(import.meta.dirname ?? '.', '..', 'dist', 'hooks', 'claude-routing-reminder.js');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function commandExists(command) {
|
|
63
|
+
try {
|
|
64
|
+
execSync(`command -v ${command}`, { stdio: 'ignore', shell: '/bin/sh' });
|
|
65
|
+
return true;
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function safeCommandVersion(command, args = ['--version']) {
|
|
72
|
+
try {
|
|
73
|
+
return execSync([command, ...args].join(' '), { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() || 'installed';
|
|
74
|
+
} catch {
|
|
75
|
+
return commandExists(command) ? 'installed' : 'missing';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getDotEnvValue(name) {
|
|
80
|
+
try {
|
|
81
|
+
const dotenvPath = join(process.cwd(), '.env');
|
|
82
|
+
if (existsSync(dotenvPath)) {
|
|
83
|
+
const content = readFileSync(dotenvPath, 'utf8');
|
|
84
|
+
const match = content.match(new RegExp(`^${name}=(.+)$`, 'm'));
|
|
85
|
+
if (match) return match[1].trim().replace(/^["']|["']$/g, '');
|
|
86
|
+
}
|
|
87
|
+
} catch { /* ignore */ }
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getApiKey() {
|
|
92
|
+
const existing = process.env.MINIMAX_API_KEY ?? '';
|
|
93
|
+
if (existing) return existing;
|
|
94
|
+
return getDotEnvValue('MINIMAX_API_KEY');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function getExistingEnvForTarget(target) {
|
|
98
|
+
const hosts = hostsForTarget(target);
|
|
99
|
+
const candidates = [
|
|
100
|
+
{
|
|
101
|
+
MINIMAX_API_KEY: getApiKey(),
|
|
102
|
+
MINIMAX_API_MODE: process.env.MINIMAX_API_MODE ?? getDotEnvValue('MINIMAX_API_MODE'),
|
|
103
|
+
MINIMAX_MODEL: process.env.MINIMAX_MODEL ?? getDotEnvValue('MINIMAX_MODEL'),
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
if (hosts.includes('claude')) candidates.push(await readClaudeMiniMaxEnv(CLAUDE_CONFIG_PATH));
|
|
107
|
+
if (hosts.includes('codex')) candidates.push(await readCodexMiniMaxEnv(CODEX_CONFIG_PATH));
|
|
108
|
+
if (hosts.includes('opencode')) candidates.push(await readOpenCodeMiniMaxEnv(OPENCODE_CONFIG_PATH));
|
|
109
|
+
|
|
110
|
+
return candidates.reduce((merged, current) => ({
|
|
111
|
+
...merged,
|
|
112
|
+
...Object.fromEntries(Object.entries(current).filter(([, value]) => typeof value === 'string' && value.trim())),
|
|
113
|
+
}), {});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const MAX_TEXT_MODEL_CHOICES = 1;
|
|
117
|
+
const FALLBACK_TEXT_MODELS = ['MiniMax-M3'];
|
|
118
|
+
|
|
119
|
+
function isAllowedTextModel(model) {
|
|
120
|
+
return typeof model === 'string' && /^MiniMax-M/i.test(model) && !/highspeed/i.test(model);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function uniqueAllowedModels(models) {
|
|
124
|
+
return [...new Set(models.map((model) => model?.toString().trim()).filter(isAllowedTextModel))];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function latestAllowedModels(models) {
|
|
128
|
+
return uniqueAllowedModels(models).slice(0, MAX_TEXT_MODEL_CHOICES);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function listAvailableTextModels(apiKey, mode) {
|
|
132
|
+
const isOpenAi = mode === 'openai';
|
|
133
|
+
const url = isOpenAi ? 'https://api.minimax.io/v1/models' : 'https://api.minimax.io/anthropic/v1/models';
|
|
134
|
+
const headers = isOpenAi ? { Authorization: `Bearer ${apiKey}` } : { 'X-Api-Key': apiKey };
|
|
135
|
+
const response = await fetch(url, { headers, signal: AbortSignal.timeout(5000) });
|
|
136
|
+
if (!response.ok) throw new Error(`MiniMax model list failed: HTTP ${response.status}`);
|
|
137
|
+
const payload = await response.json();
|
|
138
|
+
return latestAllowedModels((payload.data ?? []).map((model) => model.id));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function getModelFromUser(apiKey, mode, existingModel) {
|
|
142
|
+
let models = [];
|
|
143
|
+
try {
|
|
144
|
+
const availableModels = await listAvailableTextModels(apiKey, mode);
|
|
145
|
+
models = latestAllowedModels(availableModels);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.warn(`Could not fetch MiniMax model list; using bundled fallback list (${err?.message ?? err})`);
|
|
148
|
+
models = latestAllowedModels(FALLBACK_TEXT_MODELS);
|
|
149
|
+
}
|
|
150
|
+
if (!models.length) models = FALLBACK_TEXT_MODELS;
|
|
151
|
+
|
|
152
|
+
const initialValue = models.includes(existingModel) ? existingModel : models[0];
|
|
153
|
+
const model = await select({
|
|
154
|
+
message: 'MINIMAX_MODEL (highspeed variants excluded)',
|
|
155
|
+
options: models.map((value) => ({
|
|
156
|
+
value,
|
|
157
|
+
label: value === initialValue ? `${value} (current/default)` : value,
|
|
158
|
+
})),
|
|
159
|
+
initialValue,
|
|
160
|
+
});
|
|
161
|
+
if (isCancel(model)) process.exit(1);
|
|
162
|
+
return model.toString();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function getEnvFromUser(existing = {}) {
|
|
166
|
+
const existingApiKey = existing.MINIMAX_API_KEY ?? '';
|
|
167
|
+
const apiKey = await text({
|
|
168
|
+
message: existingApiKey ? 'MINIMAX_API_KEY (press Enter to keep existing)' : 'MINIMAX_API_KEY',
|
|
169
|
+
placeholder: existingApiKey ? 'keep existing key' : undefined,
|
|
170
|
+
defaultValue: existingApiKey || undefined,
|
|
171
|
+
});
|
|
172
|
+
if (isCancel(apiKey) || !apiKey?.trim()) { outro('API key is required'); process.exit(1); }
|
|
173
|
+
const existingMode = existing.MINIMAX_API_MODE === 'openai' ? 'openai' : 'anthropic';
|
|
174
|
+
const modeOptions = existingMode === 'openai'
|
|
175
|
+
? [{ value: 'openai', label: 'openai (current)' }, { value: 'anthropic', label: 'anthropic' }]
|
|
176
|
+
: [{ value: 'anthropic', label: 'anthropic (default/current)' }, { value: 'openai', label: 'openai' }];
|
|
177
|
+
const mode = await select({ message: 'MINIMAX_API_MODE', options: modeOptions });
|
|
178
|
+
if (isCancel(mode)) process.exit(1);
|
|
179
|
+
const existingModel = existing.MINIMAX_MODEL || 'MiniMax-M3';
|
|
180
|
+
const model = await getModelFromUser(apiKey.trim(), mode, existingModel);
|
|
181
|
+
return { apiKey: apiKey.trim(), mode, model };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
// Garantiza que existan los artefactos compilados que necesita el Core MCP.
|
|
186
|
+
async function ensureBuilt() {
|
|
187
|
+
const distPath = getDistPath();
|
|
188
|
+
const hookPath = getClaudeHookPath();
|
|
189
|
+
if (!existsSync(distPath) || !existsSync(hookPath)) {
|
|
190
|
+
const shouldBuild = await confirm({ message: 'dist files not found. Build now?', default: true });
|
|
191
|
+
if (isCancel(shouldBuild) || !shouldBuild) { outro('Aborted'); process.exit(1); }
|
|
192
|
+
execSync('npm run build', { stdio: 'inherit' });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// --- Operaciones por componente (cada una recorre los hosts del target) ---
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
async function removeLegacyNativeMultimodal(target) {
|
|
200
|
+
const hosts = hostsForTarget(target);
|
|
201
|
+
if (hosts.includes('claude')) await removeClaudeNativeMultimodalConfig(CLAUDE_CONFIG_PATH);
|
|
202
|
+
if (hosts.includes('opencode')) await removeOpenCodeNativeMultimodalConfig(OPENCODE_CONFIG_PATH);
|
|
203
|
+
if (hosts.includes('codex')) await removeCodexNativeMultimodalConfig(CODEX_CONFIG_PATH);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function installCore(target, ctx) {
|
|
207
|
+
await removeLegacyNativeMultimodal(target);
|
|
208
|
+
console.log('Removed legacy native minimax_multimodal MCP entry if present');
|
|
209
|
+
const distPath = getDistPath();
|
|
210
|
+
const { env } = ctx;
|
|
211
|
+
const server = { command: 'node', args: [distPath], env: { MINIMAX_API_KEY: env.apiKey, MINIMAX_API_MODE: env.mode, MINIMAX_MODEL: env.model } };
|
|
212
|
+
const hosts = hostsForTarget(target);
|
|
213
|
+
|
|
214
|
+
if (hosts.includes('claude')) {
|
|
215
|
+
await writeClaudeConfig(CLAUDE_CONFIG_PATH, server);
|
|
216
|
+
await writeClaudeGlobalInstructions(CLAUDE_GLOBAL_INSTRUCTIONS_PATH);
|
|
217
|
+
await writeClaudeHooksConfig(CLAUDE_SETTINGS_PATH, getClaudeHookPath());
|
|
218
|
+
console.log(`Installed minimax in ${CLAUDE_CONFIG_PATH}`);
|
|
219
|
+
console.log(`Installed MiniMax routing reference in ${CLAUDE_GLOBAL_INSTRUCTIONS_PATH}`);
|
|
220
|
+
console.log(`Installed MiniMax routing hooks in ${CLAUDE_SETTINGS_PATH}`);
|
|
221
|
+
}
|
|
222
|
+
if (hosts.includes('opencode')) {
|
|
223
|
+
const dir = dirname(OPENCODE_CONFIG_PATH);
|
|
224
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
225
|
+
await writeOpenCodeConfig(OPENCODE_CONFIG_PATH, server);
|
|
226
|
+
await writeOpenCodeGlobalInstructions(OPENCODE_GLOBAL_INSTRUCTIONS_PATH);
|
|
227
|
+
console.log(`Installed minimax in ${OPENCODE_CONFIG_PATH}`);
|
|
228
|
+
console.log(`Installed MiniMax routing reference in ${OPENCODE_GLOBAL_INSTRUCTIONS_PATH}`);
|
|
229
|
+
}
|
|
230
|
+
if (hosts.includes('codex')) {
|
|
231
|
+
const dir = dirname(CODEX_CONFIG_PATH);
|
|
232
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
233
|
+
await writeCodexConfig(CODEX_CONFIG_PATH, server);
|
|
234
|
+
await writeCodexGlobalInstructions(CODEX_GLOBAL_INSTRUCTIONS_PATH);
|
|
235
|
+
console.log(`Installed minimax in ${CODEX_CONFIG_PATH}`);
|
|
236
|
+
console.log(`Installed MiniMax routing reference in ${CODEX_GLOBAL_INSTRUCTIONS_PATH}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function uninstallCore(target) {
|
|
241
|
+
await removeLegacyNativeMultimodal(target);
|
|
242
|
+
console.log('Removed legacy native minimax_multimodal MCP entry if present');
|
|
243
|
+
const hosts = hostsForTarget(target);
|
|
244
|
+
if (hosts.includes('claude')) {
|
|
245
|
+
if (existsSync(CLAUDE_CONFIG_PATH)) {
|
|
246
|
+
await removeClaudeConfig(CLAUDE_CONFIG_PATH);
|
|
247
|
+
console.log(`Removed minimax from ${CLAUDE_CONFIG_PATH}`);
|
|
248
|
+
}
|
|
249
|
+
if (existsSync(CLAUDE_SETTINGS_PATH)) {
|
|
250
|
+
await removeClaudeHooksConfig(CLAUDE_SETTINGS_PATH);
|
|
251
|
+
console.log(`Removed MiniMax routing hooks from ${CLAUDE_SETTINGS_PATH}`);
|
|
252
|
+
}
|
|
253
|
+
await removeClaudeGlobalInstructions(CLAUDE_GLOBAL_INSTRUCTIONS_PATH);
|
|
254
|
+
console.log(`Removed MiniMax routing references from ${CLAUDE_GLOBAL_INSTRUCTIONS_PATH}`);
|
|
255
|
+
}
|
|
256
|
+
if (hosts.includes('opencode')) {
|
|
257
|
+
if (existsSync(OPENCODE_CONFIG_PATH)) {
|
|
258
|
+
await removeOpenCodeConfig(OPENCODE_CONFIG_PATH);
|
|
259
|
+
console.log(`Removed minimax from ${OPENCODE_CONFIG_PATH}`);
|
|
260
|
+
}
|
|
261
|
+
await removeOpenCodeGlobalInstructions(OPENCODE_GLOBAL_INSTRUCTIONS_PATH);
|
|
262
|
+
console.log(`Removed MiniMax routing references from ${OPENCODE_GLOBAL_INSTRUCTIONS_PATH}`);
|
|
263
|
+
}
|
|
264
|
+
if (hosts.includes('codex')) {
|
|
265
|
+
if (existsSync(CODEX_CONFIG_PATH)) {
|
|
266
|
+
await removeCodexConfig(CODEX_CONFIG_PATH);
|
|
267
|
+
console.log(`Removed minimax from ${CODEX_CONFIG_PATH}`);
|
|
268
|
+
}
|
|
269
|
+
await removeCodexGlobalInstructions(CODEX_GLOBAL_INSTRUCTIONS_PATH);
|
|
270
|
+
console.log(`Removed MiniMax routing references from ${CODEX_GLOBAL_INSTRUCTIONS_PATH}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function installMiniMaxCli() {
|
|
275
|
+
execSync('npm install -g mmx-cli', { stdio: 'inherit' });
|
|
276
|
+
console.log(`Installed MiniMax CLI: ${safeCommandVersion('mmx')}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function ensurePluginLinked() {
|
|
280
|
+
const pluginRoot = join(import.meta.dirname ?? '.', '..');
|
|
281
|
+
const cliDist = join(pluginRoot, 'dist', 'cli.js');
|
|
282
|
+
if (!existsSync(cliDist)) return;
|
|
283
|
+
try {
|
|
284
|
+
execSync('npm link', { stdio: 'inherit', cwd: pluginRoot });
|
|
285
|
+
console.log('Linked minimax CLI globally (minimax gain available from any folder)');
|
|
286
|
+
} catch (err) {
|
|
287
|
+
console.warn('Could not link minimax CLI globally:', err?.message ?? err);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function uninstallMiniMaxCli() {
|
|
292
|
+
execSync('npm uninstall -g mmx-cli', { stdio: 'inherit' });
|
|
293
|
+
console.log('Removed MiniMax CLI (mmx)');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// --- Registro de componentes: única fuente de verdad del instalador ---
|
|
297
|
+
// Cada componente declara sus dependencias, si necesita credenciales/build,
|
|
298
|
+
// y cómo instalarse, desinstalarse y detectarse. Agregar uno nuevo es agregar
|
|
299
|
+
// una entrada aquí, sin tocar el flujo del menú.
|
|
300
|
+
const COMPONENTS = [
|
|
301
|
+
{
|
|
302
|
+
id: 'core',
|
|
303
|
+
label: 'Core MCP',
|
|
304
|
+
hint: 'Coding/delegation MCP, mmx CLI wrapper tools, MINIMAX.md routing, and Claude Code hooks.',
|
|
305
|
+
requires: ['cli'],
|
|
306
|
+
needsEnv: true,
|
|
307
|
+
needsBuild: true,
|
|
308
|
+
install: installCore,
|
|
309
|
+
uninstall: uninstallCore,
|
|
310
|
+
isInstalled: (paths) => hasCoreMcpConfig(paths.config),
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: 'cli',
|
|
314
|
+
label: 'MiniMax CLI (mmx)',
|
|
315
|
+
hint: 'Official mmx CLI installed globally (host-independent).',
|
|
316
|
+
requires: [],
|
|
317
|
+
needsEnv: false,
|
|
318
|
+
hostIndependent: true,
|
|
319
|
+
install: installMiniMaxCli,
|
|
320
|
+
uninstall: uninstallMiniMaxCli,
|
|
321
|
+
isInstalled: () => commandExists('mmx'),
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
id: 'alias',
|
|
325
|
+
label: 'Shell alias (cc-mini)',
|
|
326
|
+
hint: 'Shell function that launches Claude Code pointing to MiniMax API (writes to ~/.zshrc).',
|
|
327
|
+
requires: [],
|
|
328
|
+
needsEnv: true,
|
|
329
|
+
hostIndependent: true,
|
|
330
|
+
install: async (_target, ctx) => {
|
|
331
|
+
const aliasName = await text({
|
|
332
|
+
message: 'Alias name',
|
|
333
|
+
placeholder: DEFAULT_ALIAS_NAME,
|
|
334
|
+
defaultValue: DEFAULT_ALIAS_NAME,
|
|
335
|
+
});
|
|
336
|
+
if (isCancel(aliasName)) { outro('Cancelled'); process.exit(0); }
|
|
337
|
+
const name = (aliasName?.toString().trim() || DEFAULT_ALIAS_NAME);
|
|
338
|
+
if (isLegacyAliasInstalled()) {
|
|
339
|
+
console.log('Migrating legacy claude-minimax alias → removing old block');
|
|
340
|
+
}
|
|
341
|
+
await writeShellAlias(ctx.env.apiKey, ctx.env.model, name);
|
|
342
|
+
console.log(`Installed ${name} function in ${shellRcPath()}`);
|
|
343
|
+
console.log(`Run: source ~/.zshrc (or open a new terminal) to activate ${name}`);
|
|
344
|
+
},
|
|
345
|
+
uninstall: async () => {
|
|
346
|
+
await removeShellAlias();
|
|
347
|
+
console.log(`Removed shell alias from ${shellRcPath()}`);
|
|
348
|
+
},
|
|
349
|
+
isInstalled: () => isShellAliasInstalled(),
|
|
350
|
+
},
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
const COMPONENT_MAP = Object.fromEntries(COMPONENTS.map((component) => [component.id, component]));
|
|
354
|
+
|
|
355
|
+
// Agrega las dependencias requeridas (p. ej. superpowers exige core).
|
|
356
|
+
function withDependencies(ids) {
|
|
357
|
+
const set = new Set(ids);
|
|
358
|
+
let changed = true;
|
|
359
|
+
while (changed) {
|
|
360
|
+
changed = false;
|
|
361
|
+
for (const id of [...set]) {
|
|
362
|
+
for (const dep of COMPONENT_MAP[id].requires) {
|
|
363
|
+
if (!set.has(dep)) { set.add(dep); changed = true; }
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return set;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Reordena ids según el orden declarado en COMPONENTS (core antes que superpowers, etc.).
|
|
371
|
+
function orderComponents(ids) {
|
|
372
|
+
const set = ids instanceof Set ? ids : new Set(ids);
|
|
373
|
+
return COMPONENTS.filter((component) => set.has(component.id)).map((component) => component.id);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function isComponentInstalled(component, target) {
|
|
377
|
+
if (component.hostIndependent) return component.isInstalled();
|
|
378
|
+
return hostsForTarget(target).some((host) => component.isInstalled(HOST_PATHS[host]));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function computeInstalledIds(target) {
|
|
382
|
+
return COMPONENTS.filter((component) => isComponentInstalled(component, target)).map((component) => component.id);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function yesNo(value) {
|
|
386
|
+
return value ? 'yes' : 'no';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function fileContains(path, value) {
|
|
390
|
+
try { return readFileSync(path, 'utf8').includes(value); } catch { return false; }
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function hasCoreMcpConfig(path) {
|
|
394
|
+
return fileContains(path, '"minimax"') || fileContains(path, '[mcp_servers.minimax]');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function hostStatusRow(name, paths) {
|
|
398
|
+
const dir = dirname(paths.instructions);
|
|
399
|
+
return {
|
|
400
|
+
Host: name,
|
|
401
|
+
CoreMCP: yesNo(hasCoreMcpConfig(paths.config)),
|
|
402
|
+
NativeMCP: yesNo(fileContains(paths.config, 'minimax_multimodal')),
|
|
403
|
+
Hooks: paths.hooks ? yesNo(existsSync(paths.hooks)) : '-',
|
|
404
|
+
Global: yesNo(existsSync(paths.instructions)),
|
|
405
|
+
MiniMax: yesNo(existsSync(join(dir, 'MINIMAX.md'))),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function showStatus() {
|
|
410
|
+
const existingKey = getApiKey();
|
|
411
|
+
console.log('MiniMax environment');
|
|
412
|
+
console.table([{
|
|
413
|
+
API_KEY: existingKey ? 'configured' : 'missing',
|
|
414
|
+
API_MODE: process.env.MINIMAX_API_MODE ?? 'anthropic (default)',
|
|
415
|
+
MODEL: process.env.MINIMAX_MODEL ?? 'MiniMax-M3 (default)',
|
|
416
|
+
MMX_CLI: safeCommandVersion('mmx'),
|
|
417
|
+
}]);
|
|
418
|
+
|
|
419
|
+
console.log('Host installation');
|
|
420
|
+
console.table([
|
|
421
|
+
hostStatusRow('Claude', HOST_PATHS.claude),
|
|
422
|
+
hostStatusRow('Codex', HOST_PATHS.codex),
|
|
423
|
+
hostStatusRow('OpenCode', HOST_PATHS.opencode),
|
|
424
|
+
]);
|
|
425
|
+
|
|
426
|
+
console.log('Shell alias');
|
|
427
|
+
const hasLegacy = isLegacyAliasInstalled();
|
|
428
|
+
console.table([{
|
|
429
|
+
alias: isShellAliasInstalled() ? 'installed' : 'missing',
|
|
430
|
+
'legacy (claude-minimax)': hasLegacy ? 'yes → run install to migrate' : 'no',
|
|
431
|
+
'rc file': shellRcPath(),
|
|
432
|
+
}]);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async function selectTarget() {
|
|
436
|
+
const targets = await multiselect({
|
|
437
|
+
message: 'Target host? (space to select, enter to confirm)',
|
|
438
|
+
options: [
|
|
439
|
+
{ value: 'claude', label: 'Claude Code' },
|
|
440
|
+
{ value: 'codex', label: 'Codex' },
|
|
441
|
+
{ value: 'opencode', label: 'OpenCode' },
|
|
442
|
+
],
|
|
443
|
+
required: true,
|
|
444
|
+
});
|
|
445
|
+
if (isCancel(targets)) process.exit(1);
|
|
446
|
+
return targets;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Flujo unificado de instalar/actualizar: una sola lista de componentes.
|
|
450
|
+
async function runInstallOrUpdate(action, target) {
|
|
451
|
+
const isUpdate = action === 'update';
|
|
452
|
+
const installed = computeInstalledIds(target);
|
|
453
|
+
const initialValues = isUpdate ? installed : (installed.length ? installed : ['core']);
|
|
454
|
+
|
|
455
|
+
const picked = await multiselect({
|
|
456
|
+
message: isUpdate ? 'Components to refresh' : 'Components to install',
|
|
457
|
+
options: COMPONENTS.map((component) => ({ value: component.id, label: component.label, hint: component.hint })),
|
|
458
|
+
initialValues,
|
|
459
|
+
required: false,
|
|
460
|
+
});
|
|
461
|
+
if (isCancel(picked)) process.exit(1);
|
|
462
|
+
|
|
463
|
+
const resolved = withDependencies(picked);
|
|
464
|
+
const added = [...resolved].filter((id) => !picked.includes(id));
|
|
465
|
+
if (added.length) console.log(`Added required components: ${orderComponents(added).join(', ')}`);
|
|
466
|
+
|
|
467
|
+
const selected = orderComponents(resolved);
|
|
468
|
+
if (selected.length === 0) { outro('Nothing selected'); return; }
|
|
469
|
+
|
|
470
|
+
// CLI es global sin env: se maneja una sola vez antes de todo.
|
|
471
|
+
if (selected.includes('cli')) await installMiniMaxCli();
|
|
472
|
+
const nonCliComponents = selected.filter((id) => id !== 'cli');
|
|
473
|
+
|
|
474
|
+
if (nonCliComponents.length === 0) { outro(isUpdate ? 'Updated' : 'Installed'); return; }
|
|
475
|
+
|
|
476
|
+
const needsEnv = nonCliComponents.some((id) => COMPONENT_MAP[id].needsEnv);
|
|
477
|
+
const ctx = {};
|
|
478
|
+
if (needsEnv) ctx.env = await getEnvFromUser(await getExistingEnvForTarget(target));
|
|
479
|
+
if (nonCliComponents.some((id) => COMPONENT_MAP[id].needsBuild)) await ensureBuilt();
|
|
480
|
+
|
|
481
|
+
// Componentes host-independientes con env (alias) se instalan una sola vez.
|
|
482
|
+
for (const id of nonCliComponents.filter((id) => COMPONENT_MAP[id].hostIndependent)) {
|
|
483
|
+
await COMPONENT_MAP[id].install(null, ctx);
|
|
484
|
+
}
|
|
485
|
+
// Componentes específicos de host.
|
|
486
|
+
for (const id of nonCliComponents.filter((id) => !COMPONENT_MAP[id].hostIndependent)) {
|
|
487
|
+
await COMPONENT_MAP[id].install(target, ctx);
|
|
488
|
+
}
|
|
489
|
+
// Si el alias está instalado y se recolectó env pero el alias no fue seleccionado
|
|
490
|
+
// explícitamente, sincronizarlo automáticamente con las nuevas credenciales.
|
|
491
|
+
if (ctx.env && isShellAliasInstalled() && !selected.includes('alias')) {
|
|
492
|
+
await writeShellAlias(ctx.env.apiKey, ctx.env.model);
|
|
493
|
+
console.log(`Synced shell alias credentials in ${shellRcPath()}`);
|
|
494
|
+
}
|
|
495
|
+
ensurePluginLinked();
|
|
496
|
+
outro(isUpdate ? 'Updated' : 'Installed');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async function runUninstall(target) {
|
|
500
|
+
const installed = computeInstalledIds(target);
|
|
501
|
+
if (installed.length === 0) { outro('No MiniMax components installed for this target'); return; }
|
|
502
|
+
|
|
503
|
+
const picked = await multiselect({
|
|
504
|
+
message: 'Components to remove',
|
|
505
|
+
options: COMPONENTS.filter((component) => installed.includes(component.id)).map((component) => ({ value: component.id, label: component.label, hint: component.hint })),
|
|
506
|
+
initialValues: installed,
|
|
507
|
+
required: false,
|
|
508
|
+
});
|
|
509
|
+
if (isCancel(picked)) process.exit(1);
|
|
510
|
+
if (picked.length === 0) { outro('Nothing selected'); return; }
|
|
511
|
+
|
|
512
|
+
// Core en desinstalación arrastra la rutina Superpowers dependiente.
|
|
513
|
+
for (const id of orderComponents(picked)) {
|
|
514
|
+
await COMPONENT_MAP[id].uninstall(target);
|
|
515
|
+
}
|
|
516
|
+
outro('Uninstalled');
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async function main() {
|
|
520
|
+
const action = await select({ message: 'Choose an action', options: [
|
|
521
|
+
{ value: 'install', label: 'Install', hint: 'Pick components to add to a host.' },
|
|
522
|
+
{ value: 'update', label: 'Update', hint: 'Refresh credentials and re-write installed components.' },
|
|
523
|
+
{ value: 'uninstall', label: 'Uninstall', hint: 'Pick MiniMax components to remove from a host.' },
|
|
524
|
+
{ value: 'status', label: 'Status', hint: 'Show installed configs, docs, and credential presence.' },
|
|
525
|
+
{ value: 'exit', label: 'Exit', hint: 'Quit the installer.' },
|
|
526
|
+
] });
|
|
527
|
+
if (isCancel(action) || action === 'exit') { outro('Goodbye'); process.exit(0); }
|
|
528
|
+
|
|
529
|
+
if (action === 'status') {
|
|
530
|
+
await showStatus();
|
|
531
|
+
outro('Done');
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const target = await selectTarget();
|
|
536
|
+
|
|
537
|
+
if (action === 'uninstall') {
|
|
538
|
+
await runUninstall(target);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
await runInstallOrUpdate(action, target);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
main().catch((err) => {
|
|
546
|
+
console.error(err);
|
|
547
|
+
process.exit(1);
|
|
548
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: minimax-delegation
|
|
3
|
+
description: Use when Claude/Codex already has a plan and wants MiniMax to perform bounded implementation-worker tasks through MCP without taking over planning, debugging, architecture, or final review.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MiniMax Worker Delegation
|
|
7
|
+
|
|
8
|
+
Use MiniMax as a secondary implementation worker, not as the primary agent.
|
|
9
|
+
|
|
10
|
+
Claude/Codex must remain responsible for planning, architecture, debugging, review, verification, and final authority. Delegate to MiniMax only after the host has defined the task, constraints, target paths, and expected output.
|
|
11
|
+
|
|
12
|
+
Good MiniMax work:
|
|
13
|
+
|
|
14
|
+
- Draft implementation changes from a host-approved plan
|
|
15
|
+
- Mechanical refactors with explicit instructions
|
|
16
|
+
- Documentation updates
|
|
17
|
+
- Test scaffold drafts when the host specifies behavior
|
|
18
|
+
- Migration or boilerplate generation
|
|
19
|
+
- Context compression/intake summaries
|
|
20
|
+
- Optional cheap second opinions, never authoritative decisions
|
|
21
|
+
|
|
22
|
+
Do not delegate secrets, `.env` files, `.git`, dependency folders, root-cause debugging, architecture decisions, or final review. Treat MiniMax output as a draft artifact that Claude/Codex must inspect before applying.
|
|
23
|
+
|
|
24
|
+
If a MiniMax tool call fails, times out, or returns unusable/unsafe output, report that failure to the user and stop the delegated path. Do not silently continue as if MiniMax succeeded; switch to local work only after making the fallback explicit.
|