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 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 your drawings. Add your API key to a `.env` file in your project root:
50
+ Skema uses vision AI to analyze drawings. You can set your Gemini API key in either place:
51
51
 
52
- ```env
53
- # For Gemini (recommended)
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
- # Or for Claude
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
- var generativeAi = require('@google/generative-ai');
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
- async function analyzeWithGemini(base64Image, apiKey, model = "gemini-2.5-flash") {
1140
- try {
1141
- const genAI = new generativeAi.GoogleGenerativeAI(apiKey);
1142
- const visionModel = genAI.getGenerativeModel({ model });
1143
- const cleanBase64 = base64Image.replace(/^data:image\/\w+;base64,/, "");
1144
- const result = await visionModel.generateContent([
1145
- IMAGE_ANALYSIS_PROMPT,
1146
- {
1147
- inlineData: {
1148
- data: cleanBase64,
1149
- mimeType: "image/png"
1150
- }
1151
- }
1152
- ]);
1153
- const response = await result.response;
1154
- const text = response.text();
1155
- return {
1156
- success: true,
1157
- description: text,
1158
- provider: "gemini"
1159
- };
1160
- } catch (error2) {
1161
- const message = error2 instanceof Error ? error2.message : String(error2);
1162
- console.error("[Vision] Gemini analysis failed:", message);
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: "gemini",
1167
- error: message
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 response = await client.messages.create({
1208
+ const model = getProviderModel(provider, apiKey, config.model);
1209
+ const result = await ai.generateText({
1177
1210
  model,
1178
- max_tokens: 1024,
1211
+ maxTokens: 1024,
1179
1212
  messages: [
1180
1213
  {
1181
1214
  role: "user",
1182
1215
  content: [
1183
1216
  {
1184
1217
  type: "image",
1185
- source: {
1186
- type: "base64",
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: "claude"
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("[Vision] Claude analysis failed:", message);
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: `No API key found for ${provider} vision. Set ${provider === "gemini" ? "GEMINI_API_KEY" : "ANTHROPIC_API_KEY"} environment variable.`
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
- if (isVisionAvailable()) {
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: "gemini"
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 - set GEMINI_API_KEY for image analysis]`,
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) */