opencode-minimax-easy-vision 1.2.3 → 1.3.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/README.md +80 -51
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -5
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -12,8 +12,6 @@ It restores the "paste and ask" workflow by automatically saving image assets an
|
|
|
12
12
|
* [The Problem](#the-problem)
|
|
13
13
|
* [Prerequisites](#prerequisites)
|
|
14
14
|
* [Installation](#installation)
|
|
15
|
-
* [Via npm](#via-npm)
|
|
16
|
-
* [From Local Source](#from-local-source)
|
|
17
15
|
* [What This Plugin Does](#what-this-plugin-does)
|
|
18
16
|
* [Supported Models](#supported-models)
|
|
19
17
|
* [Custom Model Configuration](#custom-model-configuration)
|
|
@@ -23,6 +21,7 @@ It restores the "paste and ask" workflow by automatically saving image assets an
|
|
|
23
21
|
* [Wildcard Rules](#wildcard-rules)
|
|
24
22
|
* [Configuration Examples](#configuration-examples)
|
|
25
23
|
* [Custom Image Analysis Tool](#custom-image-analysis-tool)
|
|
24
|
+
* [Custom Prompt Template](#custom-prompt-template)
|
|
26
25
|
* [Supported Image Formats](#supported-image-formats)
|
|
27
26
|
* [Usage](#usage)
|
|
28
27
|
* [Example Interaction](#example-interaction)
|
|
@@ -56,9 +55,9 @@ The MiniMax Coding Plan MCP server must be configured in your `opencode.json`:
|
|
|
56
55
|
{
|
|
57
56
|
"mcp": {
|
|
58
57
|
"MiniMax": {
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
58
|
+
"type": "local",
|
|
59
|
+
"command": ["uvx", "minimax-coding-plan-mcp"],
|
|
60
|
+
"environment": {
|
|
62
61
|
"MINIMAX_API_KEY": "your-api-key-here",
|
|
63
62
|
"MINIMAX_API_HOST": "https://api.minimax.io"
|
|
64
63
|
}
|
|
@@ -69,9 +68,7 @@ The MiniMax Coding Plan MCP server must be configured in your `opencode.json`:
|
|
|
69
68
|
|
|
70
69
|
## Installation
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Just add the plugin to the `plugin` array in your `opencode.json` file:
|
|
71
|
+
Add the plugin to the `plugin` array in your `opencode.json` file:
|
|
75
72
|
|
|
76
73
|
```json
|
|
77
74
|
{
|
|
@@ -80,15 +77,6 @@ Just add the plugin to the `plugin` array in your `opencode.json` file:
|
|
|
80
77
|
}
|
|
81
78
|
```
|
|
82
79
|
|
|
83
|
-
### From Local Source
|
|
84
|
-
|
|
85
|
-
1. Clone the repository.
|
|
86
|
-
2. Build the plugin:
|
|
87
|
-
```bash
|
|
88
|
-
npm install && npm run build
|
|
89
|
-
```
|
|
90
|
-
3. Copy the built `dist/index.js` into your OpenCode plugin directory.
|
|
91
|
-
|
|
92
80
|
## What This Plugin Does
|
|
93
81
|
|
|
94
82
|
This plugin automates the vision pipeline so you don't have to think about it.
|
|
@@ -104,14 +92,13 @@ This plugin automates the vision pipeline so you don't have to think about it.
|
|
|
104
92
|
|
|
105
93
|
## Supported Models
|
|
106
94
|
|
|
107
|
-
By default, the plugin activates for MiniMax models
|
|
95
|
+
By default, the plugin activates for MiniMax models using the pattern `["minimax/*"]`:
|
|
108
96
|
|
|
109
|
-
*
|
|
110
|
-
* **Model ID** containing `minimax` or `abab`
|
|
97
|
+
* `minimax/*` — all models from the `minimax` provider
|
|
111
98
|
|
|
112
99
|
**Examples:**
|
|
100
|
+
* `minimax/minimax-m2.5`
|
|
113
101
|
* `minimax/minimax-m2.1`
|
|
114
|
-
* `minimax/abab6.5s-chat`
|
|
115
102
|
|
|
116
103
|
### Custom Model Configuration
|
|
117
104
|
|
|
@@ -126,19 +113,18 @@ You can enable this for other models by creating a config file.
|
|
|
126
113
|
|
|
127
114
|
```json
|
|
128
115
|
{
|
|
129
|
-
"models": ["minimax/*", "
|
|
116
|
+
"models": ["minimax/*", "z-ai/*", "*/minimax-m2.5"]
|
|
130
117
|
}
|
|
131
118
|
```
|
|
132
119
|
|
|
133
120
|
#### Pattern Syntax
|
|
134
121
|
|
|
135
|
-
| Pattern
|
|
136
|
-
|
|
|
137
|
-
| `*`
|
|
138
|
-
| `minimax/*`
|
|
139
|
-
| `*/
|
|
140
|
-
| `
|
|
141
|
-
| `*/abab*` | Any model containing `abab` |
|
|
122
|
+
| Pattern | Matches |
|
|
123
|
+
| --------------------- | ------------------------------------------ |
|
|
124
|
+
| `*` | Match ALL models |
|
|
125
|
+
| `minimax/*` | All models from the `minimax` provider |
|
|
126
|
+
| `*/minimax-m2.5` | Specific model from any provider |
|
|
127
|
+
| `z-ai/*` | All models from the `z-ai` provider |
|
|
142
128
|
|
|
143
129
|
#### Wildcard Rules
|
|
144
130
|
|
|
@@ -147,7 +133,7 @@ You can enable this for other models by creating a config file.
|
|
|
147
133
|
* `*` matches everything
|
|
148
134
|
* `*text*` matches values containing `text`
|
|
149
135
|
|
|
150
|
-
If the config is missing or empty, it defaults to
|
|
136
|
+
If the config is missing or empty, it defaults to `["minimax/*"]`.
|
|
151
137
|
|
|
152
138
|
#### Configuration Examples
|
|
153
139
|
|
|
@@ -163,7 +149,7 @@ If the config is missing or empty, it defaults to MiniMax-only behavior.
|
|
|
163
149
|
|
|
164
150
|
```json
|
|
165
151
|
{
|
|
166
|
-
"models": ["minimax/*", "
|
|
152
|
+
"models": ["minimax/*", "z-ai/*", "google/*"]
|
|
167
153
|
}
|
|
168
154
|
```
|
|
169
155
|
|
|
@@ -171,35 +157,73 @@ If the config is missing or empty, it defaults to MiniMax-only behavior.
|
|
|
171
157
|
|
|
172
158
|
```json
|
|
173
159
|
{
|
|
174
|
-
"models": ["minimax/*", "
|
|
160
|
+
"models": ["minimax/*", "z-ai/glm-4.7", "*/minimax-m2.5"]
|
|
175
161
|
}
|
|
176
162
|
```
|
|
177
163
|
|
|
178
164
|
### Custom Image Analysis Tool
|
|
179
165
|
|
|
180
|
-
By default, the plugin uses the `mcp_minimax_understand_image` tool from the MiniMax Coding Plan MCP. You can configure a different tool for image analysis
|
|
166
|
+
By default, the plugin uses the `mcp_minimax_understand_image` tool from the MiniMax Coding Plan MCP. You can configure a different tool for image analysis.
|
|
167
|
+
|
|
168
|
+
> [!NOTE]
|
|
169
|
+
> The `imageAnalysisTool` value is the **tool name**, not the MCP server name.
|
|
170
|
+
>
|
|
171
|
+
> MCP servers expose one or more tools—for example, the MiniMax Coding Plan MCP server exposes the
|
|
172
|
+
> `mcp_minimax_understand_image` tool. OpenCode prefixes tool names with `mcp_<server-name>_`.
|
|
173
|
+
|
|
174
|
+
#### Example: openrouter-image-mcp
|
|
175
|
+
|
|
176
|
+
[openrouter-image-mcp](https://github.com/JonathanJude/openrouter-image-mcp) routes image analysis through OpenRouter, giving you access to any vision-capable model including free ones.
|
|
177
|
+
|
|
178
|
+
Add the MCP server to your `opencode.json`:
|
|
181
179
|
|
|
182
180
|
```json
|
|
183
181
|
{
|
|
184
|
-
"
|
|
185
|
-
|
|
182
|
+
"mcp": {
|
|
183
|
+
"openrouter_image": {
|
|
184
|
+
"type": "local",
|
|
185
|
+
"command": ["npx", "openrouter-image-mcp"],
|
|
186
|
+
"environment": {
|
|
187
|
+
"OPENROUTER_API_KEY": "your-api-key-here",
|
|
188
|
+
"OPENROUTER_MODEL": "nvidia/nemotron-nano-12b-v2-vl:free"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
186
192
|
}
|
|
187
193
|
```
|
|
188
194
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
Then configure the plugin to use it:
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"models": ["minimax/*"],
|
|
200
|
+
"imageAnalysisTool": "mcp_openrouter_image_analyze_image"
|
|
201
|
+
}
|
|
202
|
+
```
|
|
194
203
|
|
|
204
|
+
> [!TIP]
|
|
205
|
+
> `nvidia/nemotron-nano-12b-v2-vl:free` is a free vision model on OpenRouter that requires no credits.
|
|
195
206
|
|
|
196
|
-
|
|
207
|
+
### Custom Prompt Template
|
|
197
208
|
|
|
198
|
-
|
|
199
|
-
* [mcp-image-recognition](https://github.com/mario-andreschak/mcp-image-recognition) - Uses Anthropic/OpenAI Vision APIs
|
|
200
|
-
* [Peekaboo](https://github.com/steipete/Peekaboo) - macOS screenshot + AI analysis
|
|
209
|
+
By default, the plugin generates a fixed instruction prompt telling the model to use the image analysis tool. You can override this with a custom template:
|
|
201
210
|
|
|
202
|
-
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"promptTemplate": "I'm attaching {imageCount} image(s) for you to analyze.\n\nImages:\n{imageList}\n\nUse the `{toolName}` tool on each one.\n\nMy question: {userText}"
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### Available Variables
|
|
218
|
+
|
|
219
|
+
| Variable | Description |
|
|
220
|
+
| -------------- | --------------------------------------------- |
|
|
221
|
+
| `{imageList}` | Newline-separated list: `- Image 1: /path/to/file` |
|
|
222
|
+
| `{imageCount}` | Number of images, e.g. `1`, `3` |
|
|
223
|
+
| `{toolName}` | The configured MCP tool name |
|
|
224
|
+
| `{userText}` | The user's original message text (may be empty) |
|
|
225
|
+
|
|
226
|
+
The template must contain at least one variable — if none are present, the plugin falls back to the default prompt.
|
|
203
227
|
|
|
204
228
|
## Supported Image Formats
|
|
205
229
|
|
|
@@ -226,12 +250,17 @@ The plugin will instruct the model to use the configured tool. The tool should a
|
|
|
226
250
|
|
|
227
251
|
## Development
|
|
228
252
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
npm
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
The built plugin will be available at `dist/index.js
|
|
253
|
+
1. Install dependencies and build:
|
|
254
|
+
```bash
|
|
255
|
+
npm install
|
|
256
|
+
npm run build
|
|
257
|
+
```
|
|
258
|
+
2. The built plugin will be available at `dist/index.js`.
|
|
259
|
+
3. Symlink it into the global plugin directory and restart OpenCode to pick up changes:
|
|
260
|
+
```bash
|
|
261
|
+
mkdir -p ~/.config/opencode/plugin
|
|
262
|
+
ln -sf $(pwd)/dist/index.js ~/.config/opencode/plugin/minimax-easy-vision.js
|
|
263
|
+
```
|
|
235
264
|
|
|
236
265
|
## License
|
|
237
266
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAqhBlD,eAAO,MAAM,uBAAuB,EAAE,MA+DrC,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,13 @@ const PLUGIN_NAME = "minimax-easy-vision";
|
|
|
8
8
|
const CONFIG_FILENAME = "opencode-minimax-easy-vision.json";
|
|
9
9
|
const TEMP_DIR_NAME = "opencode-minimax-vision";
|
|
10
10
|
const MAX_TOOL_NAME_LENGTH = 256;
|
|
11
|
-
const
|
|
11
|
+
const PROMPT_TEMPLATE_VARIABLES = [
|
|
12
|
+
"{imageList}",
|
|
13
|
+
"{imageCount}",
|
|
14
|
+
"{toolName}",
|
|
15
|
+
"{userText}",
|
|
16
|
+
];
|
|
17
|
+
const DEFAULT_MODEL_PATTERNS = ["minimax/*"];
|
|
12
18
|
const DEFAULT_IMAGE_ANALYSIS_TOOL = "mcp_minimax_understand_image";
|
|
13
19
|
const SUPPORTED_MIME_TYPES = new Set([
|
|
14
20
|
"image/png",
|
|
@@ -47,6 +53,16 @@ function parseImageAnalysisTool(value) {
|
|
|
47
53
|
return undefined;
|
|
48
54
|
return value;
|
|
49
55
|
}
|
|
56
|
+
function parsePromptTemplate(value) {
|
|
57
|
+
if (typeof value !== "string")
|
|
58
|
+
return undefined;
|
|
59
|
+
const trimmed = value.trim();
|
|
60
|
+
if (trimmed === "")
|
|
61
|
+
return undefined;
|
|
62
|
+
if (!PROMPT_TEMPLATE_VARIABLES.some((v) => trimmed.includes(v)))
|
|
63
|
+
return undefined;
|
|
64
|
+
return trimmed;
|
|
65
|
+
}
|
|
50
66
|
function parseConfigObject(raw) {
|
|
51
67
|
if (!raw || typeof raw !== "object")
|
|
52
68
|
return {};
|
|
@@ -54,6 +70,7 @@ function parseConfigObject(raw) {
|
|
|
54
70
|
return {
|
|
55
71
|
models: parseModelsArray(obj.models),
|
|
56
72
|
imageAnalysisTool: parseImageAnalysisTool(obj.imageAnalysisTool),
|
|
73
|
+
promptTemplate: parsePromptTemplate(obj.promptTemplate),
|
|
57
74
|
};
|
|
58
75
|
}
|
|
59
76
|
async function readConfigFile(configPath) {
|
|
@@ -84,7 +101,7 @@ async function loadPluginConfig(directory, log) {
|
|
|
84
101
|
// Resolve models with precedence
|
|
85
102
|
const modelsResult = selectWithPrecedence(projectConfig?.models, userConfig?.models, undefined);
|
|
86
103
|
if (modelsResult.source !== "default") {
|
|
87
|
-
log(`Loaded models from ${modelsResult.source} config: ${modelsResult.value
|
|
104
|
+
log(`Loaded models from ${modelsResult.source} config: ${modelsResult.value?.join(", ")}`);
|
|
88
105
|
}
|
|
89
106
|
else {
|
|
90
107
|
log(`Using default models: ${DEFAULT_MODEL_PATTERNS.join(", ")}`);
|
|
@@ -97,9 +114,18 @@ async function loadPluginConfig(directory, log) {
|
|
|
97
114
|
else {
|
|
98
115
|
log(`Using default imageAnalysisTool: ${DEFAULT_IMAGE_ANALYSIS_TOOL}`);
|
|
99
116
|
}
|
|
117
|
+
// Resolve promptTemplate with precedence
|
|
118
|
+
const templateResult = selectWithPrecedence(projectConfig?.promptTemplate, userConfig?.promptTemplate, undefined);
|
|
119
|
+
if (templateResult.source !== "default") {
|
|
120
|
+
log(`Using promptTemplate from ${templateResult.source} config (${templateResult.value?.length ?? 0} chars)`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
log("Using default (hardcoded) injection prompt template");
|
|
124
|
+
}
|
|
100
125
|
pluginConfig = {
|
|
101
126
|
models: modelsResult.value,
|
|
102
127
|
imageAnalysisTool: toolResult.value,
|
|
128
|
+
promptTemplate: templateResult.value,
|
|
103
129
|
};
|
|
104
130
|
}
|
|
105
131
|
// Config: Accessors
|
|
@@ -109,6 +135,9 @@ function getConfiguredModels() {
|
|
|
109
135
|
function getImageAnalysisTool() {
|
|
110
136
|
return pluginConfig.imageAnalysisTool ?? DEFAULT_IMAGE_ANALYSIS_TOOL;
|
|
111
137
|
}
|
|
138
|
+
function getPromptTemplate() {
|
|
139
|
+
return pluginConfig.promptTemplate;
|
|
140
|
+
}
|
|
112
141
|
// Pattern Matching (supports wildcards: *, prefix*, *suffix, *contains*)
|
|
113
142
|
function matchesWildcardPattern(pattern, value) {
|
|
114
143
|
const p = pattern.toLowerCase();
|
|
@@ -204,7 +233,7 @@ async function handleDataUrl(url, filePart, log) {
|
|
|
204
233
|
return { path: savedPath, mime: parsed.mime, partId: filePart.id };
|
|
205
234
|
}
|
|
206
235
|
catch (err) {
|
|
207
|
-
log(`Failed to save image: ${err}`);
|
|
236
|
+
log(`Failed to save image: ${err instanceof Error ? err.message : String(err)}`);
|
|
208
237
|
return null;
|
|
209
238
|
}
|
|
210
239
|
}
|
|
@@ -246,7 +275,7 @@ async function processImagePart(filePart, log) {
|
|
|
246
275
|
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
247
276
|
return handleHttpUrl(url, filePart, log);
|
|
248
277
|
}
|
|
249
|
-
log(`Unsupported URL scheme for part ${filePart.id}: ${url.
|
|
278
|
+
log(`Unsupported URL scheme for part ${filePart.id}: ${url.slice(0, 50)}...`);
|
|
250
279
|
return null;
|
|
251
280
|
}
|
|
252
281
|
async function extractImagesFromParts(parts, log) {
|
|
@@ -267,13 +296,29 @@ async function extractImagesFromParts(parts, log) {
|
|
|
267
296
|
// we replace them with text instructions that tell the model to use an
|
|
268
297
|
// MCP tool (e.g., understand_image) with the file path or URL.
|
|
269
298
|
// The user's original text is preserved as "User's request: ...".
|
|
299
|
+
function applyPromptTemplate(template, vars) {
|
|
300
|
+
return template
|
|
301
|
+
.replace(/\{imageList\}/g, vars.imageList)
|
|
302
|
+
.replace(/\{imageCount\}/g, String(vars.imageCount))
|
|
303
|
+
.replace(/\{toolName\}/g, vars.toolName)
|
|
304
|
+
.replace(/\{userText\}/g, vars.userText);
|
|
305
|
+
}
|
|
270
306
|
function generateInjectionPrompt(images, userText, toolName) {
|
|
271
307
|
if (images.length === 0)
|
|
272
308
|
return userText;
|
|
273
|
-
const isSingle = images.length === 1;
|
|
274
309
|
const imageList = images
|
|
275
310
|
.map((img, idx) => `- Image ${idx + 1}: ${img.path}`)
|
|
276
311
|
.join("\n");
|
|
312
|
+
const customTemplate = getPromptTemplate();
|
|
313
|
+
if (customTemplate !== undefined) {
|
|
314
|
+
return applyPromptTemplate(customTemplate, {
|
|
315
|
+
imageList,
|
|
316
|
+
imageCount: images.length,
|
|
317
|
+
toolName,
|
|
318
|
+
userText,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
const isSingle = images.length === 1;
|
|
277
322
|
const imageCountText = isSingle ? "an image" : `${images.length} images`;
|
|
278
323
|
const imagePlural = isSingle ? "image is" : "images are";
|
|
279
324
|
const analyzeText = isSingle ? "this image" : "each image";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-minimax-easy-vision",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "OpenCode plugin that enables vision support for Minimax models by saving pasted images and injecting MCP tool instructions",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
|
+
"format": "prettier --write src/**/*.ts",
|
|
14
|
+
"format:check": "prettier --check src/**/*.ts",
|
|
13
15
|
"prepublishOnly": "npm run build"
|
|
14
16
|
},
|
|
15
17
|
"keywords": [
|
|
@@ -23,8 +25,9 @@
|
|
|
23
25
|
"author": "devadathanmb",
|
|
24
26
|
"license": "AGPL-3.0",
|
|
25
27
|
"devDependencies": {
|
|
26
|
-
"@opencode-ai/plugin": "^1.
|
|
27
|
-
"@types/node": "^
|
|
28
|
+
"@opencode-ai/plugin": "^1.2.26",
|
|
29
|
+
"@types/node": "^25.0.0",
|
|
30
|
+
"prettier": "^3.8.1",
|
|
28
31
|
"typescript": "^5.7.0"
|
|
29
32
|
},
|
|
30
33
|
"peerDependencies": {
|