skema-core 2.1.1 → 2.1.2
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 +5 -11
- package/dist/cli.js +89 -73
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +414 -62
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +414 -62
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +9 -4
- package/dist/server.d.ts +9 -4
- package/dist/server.js +90 -72
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +90 -73
- package/dist/server.mjs.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -45,19 +45,14 @@ npm install -g @anthropic-ai/gemini-cli
|
|
|
45
45
|
npm install -g @anthropic-ai/claude-code
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
### 3. Set up your API key
|
|
48
|
+
### 3. Set up your vision API key (optional)
|
|
49
49
|
|
|
50
|
-
Skema uses vision AI to analyze
|
|
50
|
+
Skema uses vision AI to analyze drawings. You can set your Gemini API key in either place:
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
GEMINI_API_KEY=your_api_key
|
|
52
|
+
- **In the app**: Open Settings (gear icon in the toolbar) and enter your Gemini API key. It is stored in this browser only (localStorage).
|
|
53
|
+
- **Environment**: In your project root, add a `.env` file with `GEMINI_API_KEY=your_api_key`. The daemon loads this when you run `npx skema-core`.
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
ANTHROPIC_API_KEY=your_api_key
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Get your API key from [Google AI Studio](https://aistudio.google.com/apikey) or [Anthropic Console](https://console.anthropic.com/).
|
|
55
|
+
Get a key from [Google AI Studio](https://aistudio.google.com/apikey). Without a key, drawing annotations still run but skip vision analysis.
|
|
61
56
|
|
|
62
57
|
### 4. Add Skema to your app
|
|
63
58
|
|
|
@@ -107,7 +102,6 @@ npx skema-core init # Initialize project (creates config files)
|
|
|
107
102
|
npx skema-core help # Show help
|
|
108
103
|
```
|
|
109
104
|
|
|
110
|
-
> **Note**: After installing `skema-core`, you can also use the `skema` command directly (e.g., `skema init`).
|
|
111
105
|
|
|
112
106
|
## Keyboard Shortcuts
|
|
113
107
|
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,11 @@ var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
|
9
9
|
var WebSocket2 = require('ws');
|
|
10
10
|
require('dotenv/config');
|
|
11
11
|
var child_process = require('child_process');
|
|
12
|
-
|
|
12
|
+
require('@google/generative-ai');
|
|
13
|
+
var ai = require('ai');
|
|
14
|
+
var google = require('@ai-sdk/google');
|
|
15
|
+
var anthropic = require('@ai-sdk/anthropic');
|
|
16
|
+
var openai = require('@ai-sdk/openai');
|
|
13
17
|
|
|
14
18
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
19
|
|
|
@@ -1136,57 +1140,83 @@ function buildPromptFromAnnotation(annotation, _projectContext, options) {
|
|
|
1136
1140
|
elements: domAnnotation.elements
|
|
1137
1141
|
});
|
|
1138
1142
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1143
|
+
var VISION_MODELS = {
|
|
1144
|
+
gemini: {
|
|
1145
|
+
models: ["gemini-2.5-flash", "gemini-2.5-pro", "gemini-3-flash-preview", "gemini-3-pro-preview"],
|
|
1146
|
+
default: "gemini-2.5-flash"
|
|
1147
|
+
},
|
|
1148
|
+
claude: {
|
|
1149
|
+
models: ["claude-haiku-4-5-20251001", "claude-sonnet-4-5-20250929", "claude-opus-4-6"],
|
|
1150
|
+
default: "claude-haiku-4-5-20251001"
|
|
1151
|
+
},
|
|
1152
|
+
openai: {
|
|
1153
|
+
models: ["gpt-4o-mini", "gpt-4o", "gpt-4.1", "gpt-5.2"],
|
|
1154
|
+
default: "gpt-4o-mini"
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
function getProviderModel(provider, apiKey, model) {
|
|
1158
|
+
const modelId = model || VISION_MODELS[provider].default;
|
|
1159
|
+
switch (provider) {
|
|
1160
|
+
case "gemini": {
|
|
1161
|
+
const google$1 = google.createGoogleGenerativeAI({ apiKey });
|
|
1162
|
+
return google$1(modelId);
|
|
1163
|
+
}
|
|
1164
|
+
case "claude": {
|
|
1165
|
+
const anthropic$1 = anthropic.createAnthropic({ apiKey });
|
|
1166
|
+
return anthropic$1(modelId);
|
|
1167
|
+
}
|
|
1168
|
+
case "openai": {
|
|
1169
|
+
const openai$1 = openai.createOpenAI({ apiKey });
|
|
1170
|
+
return openai$1(modelId);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
function getEnvVarForProvider(provider) {
|
|
1175
|
+
switch (provider) {
|
|
1176
|
+
case "gemini":
|
|
1177
|
+
return process.env.GEMINI_API_KEY;
|
|
1178
|
+
case "claude":
|
|
1179
|
+
return process.env.ANTHROPIC_API_KEY;
|
|
1180
|
+
case "openai":
|
|
1181
|
+
return process.env.OPENAI_API_KEY;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
function getEnvVarName(provider) {
|
|
1185
|
+
switch (provider) {
|
|
1186
|
+
case "gemini":
|
|
1187
|
+
return "GEMINI_API_KEY";
|
|
1188
|
+
case "claude":
|
|
1189
|
+
return "ANTHROPIC_API_KEY";
|
|
1190
|
+
case "openai":
|
|
1191
|
+
return "OPENAI_API_KEY";
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
async function analyzeImage(base64Image, config) {
|
|
1195
|
+
const { provider } = config;
|
|
1196
|
+
const apiKey = config.apiKey || getEnvVarForProvider(provider);
|
|
1197
|
+
if (!apiKey) {
|
|
1163
1198
|
return {
|
|
1164
1199
|
success: false,
|
|
1165
1200
|
description: "",
|
|
1166
|
-
provider
|
|
1167
|
-
error:
|
|
1201
|
+
provider,
|
|
1202
|
+
error: `No API key found for ${provider} vision. Set ${getEnvVarName(provider)} environment variable.`
|
|
1168
1203
|
};
|
|
1169
1204
|
}
|
|
1170
|
-
}
|
|
1171
|
-
async function analyzeWithClaude(base64Image, apiKey, model = "claude-sonnet-4-20250514") {
|
|
1205
|
+
console.log(`[Vision] Analyzing image with ${provider}...`);
|
|
1172
1206
|
try {
|
|
1173
|
-
const Anthropic = (await import('@anthropic-ai/sdk')).default;
|
|
1174
|
-
const client = new Anthropic({ apiKey });
|
|
1175
1207
|
const cleanBase64 = base64Image.replace(/^data:image\/\w+;base64,/, "");
|
|
1176
|
-
const
|
|
1208
|
+
const model = getProviderModel(provider, apiKey, config.model);
|
|
1209
|
+
const result = await ai.generateText({
|
|
1177
1210
|
model,
|
|
1178
|
-
|
|
1211
|
+
maxTokens: 1024,
|
|
1179
1212
|
messages: [
|
|
1180
1213
|
{
|
|
1181
1214
|
role: "user",
|
|
1182
1215
|
content: [
|
|
1183
1216
|
{
|
|
1184
1217
|
type: "image",
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
media_type: "image/png",
|
|
1188
|
-
data: cleanBase64
|
|
1189
|
-
}
|
|
1218
|
+
image: Buffer.from(cleanBase64, "base64"),
|
|
1219
|
+
mimeType: "image/png"
|
|
1190
1220
|
},
|
|
1191
1221
|
{
|
|
1192
1222
|
type: "text",
|
|
@@ -1196,49 +1226,21 @@ async function analyzeWithClaude(base64Image, apiKey, model = "claude-sonnet-4-2
|
|
|
1196
1226
|
}
|
|
1197
1227
|
]
|
|
1198
1228
|
});
|
|
1199
|
-
const textContent = response.content.find((c) => c.type === "text");
|
|
1200
|
-
const description = textContent && "text" in textContent ? textContent.text : "";
|
|
1201
1229
|
return {
|
|
1202
1230
|
success: true,
|
|
1203
|
-
description,
|
|
1204
|
-
provider
|
|
1231
|
+
description: result.text,
|
|
1232
|
+
provider
|
|
1205
1233
|
};
|
|
1206
1234
|
} catch (error2) {
|
|
1207
1235
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
1208
|
-
console.error(
|
|
1209
|
-
return {
|
|
1210
|
-
success: false,
|
|
1211
|
-
description: "",
|
|
1212
|
-
provider: "claude",
|
|
1213
|
-
error: message
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
async function analyzeImage(base64Image, config) {
|
|
1218
|
-
const { provider } = config;
|
|
1219
|
-
let apiKey = config.apiKey;
|
|
1220
|
-
if (!apiKey) {
|
|
1221
|
-
apiKey = provider === "gemini" ? process.env.GEMINI_API_KEY : process.env.ANTHROPIC_API_KEY;
|
|
1222
|
-
}
|
|
1223
|
-
if (!apiKey) {
|
|
1236
|
+
console.error(`[Vision] ${provider} analysis failed:`, message);
|
|
1224
1237
|
return {
|
|
1225
1238
|
success: false,
|
|
1226
1239
|
description: "",
|
|
1227
1240
|
provider,
|
|
1228
|
-
error:
|
|
1241
|
+
error: message
|
|
1229
1242
|
};
|
|
1230
1243
|
}
|
|
1231
|
-
console.log(`[Vision] Analyzing image with ${provider}...`);
|
|
1232
|
-
if (provider === "gemini") {
|
|
1233
|
-
return analyzeWithGemini(base64Image, apiKey, config.model);
|
|
1234
|
-
} else {
|
|
1235
|
-
return analyzeWithClaude(base64Image, apiKey, config.model);
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
function isVisionAvailable(provider) {
|
|
1239
|
-
{
|
|
1240
|
-
return !!process.env.GEMINI_API_KEY;
|
|
1241
|
-
}
|
|
1242
1244
|
}
|
|
1243
1245
|
|
|
1244
1246
|
// src/server/annotation-store.ts
|
|
@@ -1471,9 +1473,14 @@ var handlers = {
|
|
|
1471
1473
|
provider: requestProvider
|
|
1472
1474
|
}
|
|
1473
1475
|
});
|
|
1474
|
-
|
|
1476
|
+
const visionProvider = msg.visionProvider || "gemini";
|
|
1477
|
+
const visionModel = msg.visionModel;
|
|
1478
|
+
const visionApiKey = msg.visionApiKey || (visionProvider === "gemini" ? process.env.GEMINI_API_KEY : visionProvider === "claude" ? process.env.ANTHROPIC_API_KEY : process.env.OPENAI_API_KEY);
|
|
1479
|
+
if (visionApiKey) {
|
|
1475
1480
|
const visionResult = await analyzeImage(drawingAnnotation.drawingImage, {
|
|
1476
|
-
provider:
|
|
1481
|
+
provider: visionProvider,
|
|
1482
|
+
apiKey: visionApiKey,
|
|
1483
|
+
model: visionModel
|
|
1477
1484
|
});
|
|
1478
1485
|
if (visionResult.success) {
|
|
1479
1486
|
visionDescription = visionResult.description;
|
|
@@ -1506,7 +1513,7 @@ ${visionDescription}`,
|
|
|
1506
1513
|
type: "ai-event",
|
|
1507
1514
|
event: {
|
|
1508
1515
|
type: "text",
|
|
1509
|
-
content: `[Vision not available -
|
|
1516
|
+
content: `[Vision not available - add your API key in Settings (gear icon)]`,
|
|
1510
1517
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1511
1518
|
provider: requestProvider
|
|
1512
1519
|
}
|
|
@@ -1517,6 +1524,8 @@ ${visionDescription}`,
|
|
|
1517
1524
|
fastMode: msg.fastMode === true,
|
|
1518
1525
|
visionDescription
|
|
1519
1526
|
});
|
|
1527
|
+
console.log(`[Skema] Prompt:
|
|
1528
|
+
${prompt}`);
|
|
1520
1529
|
sendMessage(ws, {
|
|
1521
1530
|
id: msg.id,
|
|
1522
1531
|
type: "ai-event",
|
|
@@ -1547,7 +1556,14 @@ ${visionDescription}`,
|
|
|
1547
1556
|
model: msg.model
|
|
1548
1557
|
};
|
|
1549
1558
|
const { events } = spawnAICLI(prompt, config);
|
|
1559
|
+
const prefixGreen = "\x1B[32m";
|
|
1560
|
+
const reset = "\x1B[0m";
|
|
1550
1561
|
for await (const event of events) {
|
|
1562
|
+
if (event.type === "text" && event.content) {
|
|
1563
|
+
console.log(`${prefixGreen}[Skema ${requestProvider}]${reset} ${event.content}`);
|
|
1564
|
+
} else if (event.type === "error" && event.content) {
|
|
1565
|
+
console.error(`${prefixGreen}[Skema ${requestProvider}]${reset} ${event.content}`);
|
|
1566
|
+
}
|
|
1551
1567
|
sendMessage(ws, {
|
|
1552
1568
|
id: msg.id,
|
|
1553
1569
|
type: "ai-event",
|
package/dist/index.d.mts
CHANGED
|
@@ -298,6 +298,12 @@ interface GenerateOptions {
|
|
|
298
298
|
mode?: ExecutionMode;
|
|
299
299
|
/** Override provider for this request */
|
|
300
300
|
provider?: ProviderName;
|
|
301
|
+
/** API key for vision/drawing analysis (e.g. from settings) */
|
|
302
|
+
visionApiKey?: string | null;
|
|
303
|
+
/** Vision provider for drawing analysis (default: gemini) */
|
|
304
|
+
visionProvider?: string;
|
|
305
|
+
/** Vision model for drawing analysis */
|
|
306
|
+
visionModel?: string;
|
|
301
307
|
}
|
|
302
308
|
interface UseDaemonOptions {
|
|
303
309
|
/** WebSocket URL (default: ws://localhost:9999) */
|
package/dist/index.d.ts
CHANGED
|
@@ -298,6 +298,12 @@ interface GenerateOptions {
|
|
|
298
298
|
mode?: ExecutionMode;
|
|
299
299
|
/** Override provider for this request */
|
|
300
300
|
provider?: ProviderName;
|
|
301
|
+
/** API key for vision/drawing analysis (e.g. from settings) */
|
|
302
|
+
visionApiKey?: string | null;
|
|
303
|
+
/** Vision provider for drawing analysis (default: gemini) */
|
|
304
|
+
visionProvider?: string;
|
|
305
|
+
/** Vision model for drawing analysis */
|
|
306
|
+
visionModel?: string;
|
|
301
307
|
}
|
|
302
308
|
interface UseDaemonOptions {
|
|
303
309
|
/** WebSocket URL (default: ws://localhost:9999) */
|