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 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
- "command": "uvx",
60
- "args": ["minimax-coding-plan-mcp"],
61
- "env": {
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
- ### Via npm
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
- * **Provider ID** containing `minimax`
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/*", "opencode/*", "*/glm-4.7-free"]
116
+ "models": ["minimax/*", "z-ai/*", "*/minimax-m2.5"]
130
117
  }
131
118
  ```
132
119
 
133
120
  #### Pattern Syntax
134
121
 
135
- | Pattern | Matches |
136
- | ---------------- | --------------------------------------- |
137
- | `*` | Match ALL models |
138
- | `minimax/*` | All models from the `minimax` provider |
139
- | `*/glm-4.7-free` | Specific model from any provider |
140
- | `opencode/*` | All models from the `opencode` provider |
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 MiniMax-only behavior.
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/*", "opencode/*", "google/*"]
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/*", "opencode/gpt-5-nano", "*/claude-3-7-sonnet*"]
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
- "models": ["*"],
185
- "imageAnalysisTool": "mcp_openrouter_analyze_image"
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
- > [!NOTE]
190
- > The `imageAnalysisTool` value is the **tool name**, not the MCP server name.
191
- >
192
- > MCP servers expose one or more tools—for example, the MiniMax Coding Plan MCP server exposes the
193
- > `mcp_minimax_understand_image` tool.
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
- This allows you to use tools from other MCP servers that provide image analysis capabilities, such as:
207
+ ### Custom Prompt Template
197
208
 
198
- * [openrouter-image-mcp](https://github.com/JonathanJude/openrouter-image-mcp) - Uses OpenRouter with GPT-4V, Claude, Gemini
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
- The plugin will instruct the model to use the configured tool. The tool should accept an image file path as input.
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
- ```bash
230
- npm install
231
- npm run build
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
 
@@ -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;AAqdlD,eAAO,MAAM,uBAAuB,EAAE,MA+DrC,CAAC;AAEF,eAAe,uBAAuB,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 DEFAULT_MODEL_PATTERNS = ["minimax/*", "*/abab*"];
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.join(", ")}`);
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.substring(0, 50)}...`);
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.2.3",
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.1.25",
27
- "@types/node": "^22.0.0",
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": {