skema-core 0.1.2 → 2.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/dist/cli.js CHANGED
@@ -3,11 +3,16 @@
3
3
 
4
4
  var fs = require('fs');
5
5
  var path = require('path');
6
+ var index_js = require('@modelcontextprotocol/sdk/server/index.js');
7
+ var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
8
+ var types_js = require('@modelcontextprotocol/sdk/types.js');
9
+ var WebSocket2 = require('ws');
6
10
  require('dotenv/config');
7
- var ws = require('ws');
8
11
  var child_process = require('child_process');
9
12
  var generativeAi = require('@google/generative-ai');
10
13
 
14
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
+
11
16
  function _interopNamespace(e) {
12
17
  if (e && e.__esModule) return e;
13
18
  var n = Object.create(null);
@@ -28,6 +33,7 @@ function _interopNamespace(e) {
28
33
 
29
34
  var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
30
35
  var path__namespace = /*#__PURE__*/_interopNamespace(path);
36
+ var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
31
37
 
32
38
  var __defProp = Object.defineProperty;
33
39
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -221,6 +227,411 @@ var init_init = __esm({
221
227
  }
222
228
  }
223
229
  });
230
+
231
+ // src/mcp/server.ts
232
+ var server_exports = {};
233
+ __export(server_exports, {
234
+ startMcpServer: () => startMcpServer
235
+ });
236
+ function scheduleReconnect() {
237
+ if (reconnectTimer) return;
238
+ reconnectTimer = setTimeout(async () => {
239
+ reconnectTimer = null;
240
+ console.error("[Skema MCP] Attempting to reconnect to daemon...");
241
+ try {
242
+ await connectToDaemon();
243
+ } catch {
244
+ }
245
+ }, RECONNECT_INTERVAL);
246
+ }
247
+ function connectToDaemon() {
248
+ return new Promise((resolve, reject) => {
249
+ if (daemonWs && daemonConnected) {
250
+ resolve();
251
+ return;
252
+ }
253
+ if (daemonWs) {
254
+ daemonWs.removeAllListeners();
255
+ daemonWs = null;
256
+ }
257
+ daemonWs = new WebSocket2__default.default(DAEMON_URL);
258
+ daemonWs.on("open", () => {
259
+ daemonConnected = true;
260
+ console.error("[Skema MCP] Connected to daemon at", DAEMON_URL);
261
+ daemonWs.send(JSON.stringify({
262
+ id: `mcp-identify-${Date.now()}`,
263
+ type: "identify",
264
+ client: "mcp-server"
265
+ }));
266
+ resolve();
267
+ });
268
+ daemonWs.on("message", (data) => {
269
+ try {
270
+ const msg = JSON.parse(data.toString());
271
+ if (msg.id && pendingRequests.has(msg.id)) {
272
+ const pending = pendingRequests.get(msg.id);
273
+ pendingRequests.delete(msg.id);
274
+ clearTimeout(pending.timeout);
275
+ if (msg.type === "error") {
276
+ pending.reject(new Error(msg.error));
277
+ } else {
278
+ pending.resolve(msg);
279
+ }
280
+ }
281
+ } catch (e) {
282
+ console.error("[Skema MCP] Failed to parse daemon message:", e);
283
+ }
284
+ });
285
+ daemonWs.on("close", () => {
286
+ daemonConnected = false;
287
+ daemonWs = null;
288
+ console.error("[Skema MCP] Disconnected from daemon");
289
+ scheduleReconnect();
290
+ });
291
+ daemonWs.on("error", (err) => {
292
+ daemonConnected = false;
293
+ console.error("[Skema MCP] Daemon connection error:", err.message);
294
+ reject(err);
295
+ });
296
+ });
297
+ }
298
+ function sendToDaemon(type, payload = {}) {
299
+ return new Promise(async (resolve, reject) => {
300
+ try {
301
+ if (!daemonConnected) {
302
+ await connectToDaemon();
303
+ }
304
+ if (!daemonWs || !daemonConnected) {
305
+ reject(new Error("Not connected to Skema daemon. Is it running?"));
306
+ return;
307
+ }
308
+ const id = `mcp-${++messageIdCounter}`;
309
+ const timeout = setTimeout(() => {
310
+ pendingRequests.delete(id);
311
+ reject(new Error(`Daemon request timed out: ${type}`));
312
+ }, 3e4);
313
+ pendingRequests.set(id, { resolve, reject, timeout });
314
+ daemonWs.send(JSON.stringify({ id, type, ...payload }));
315
+ } catch (err) {
316
+ reject(err);
317
+ }
318
+ });
319
+ }
320
+ function success(data) {
321
+ return {
322
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
323
+ };
324
+ }
325
+ function error(message) {
326
+ return {
327
+ content: [{ type: "text", text: message }],
328
+ isError: true
329
+ };
330
+ }
331
+ async function handleTool(name, args2) {
332
+ switch (name) {
333
+ case "skema_get_pending": {
334
+ try {
335
+ const response = await sendToDaemon("get-pending-annotations");
336
+ return success({
337
+ count: response.count,
338
+ annotations: response.annotations
339
+ });
340
+ } catch (err) {
341
+ return error(`Failed to get pending annotations: ${err.message}`);
342
+ }
343
+ }
344
+ case "skema_get_all_annotations": {
345
+ try {
346
+ const response = await sendToDaemon("get-all-annotations");
347
+ return success({
348
+ count: response.count,
349
+ annotations: response.annotations
350
+ });
351
+ } catch (err) {
352
+ return error(`Failed to get annotations: ${err.message}`);
353
+ }
354
+ }
355
+ case "skema_get_annotation": {
356
+ try {
357
+ const response = await sendToDaemon("get-annotation", {
358
+ annotationId: args2.annotationId
359
+ });
360
+ return success(response.annotation);
361
+ } catch (err) {
362
+ return error(`Failed to get annotation: ${err.message}`);
363
+ }
364
+ }
365
+ case "skema_acknowledge": {
366
+ try {
367
+ await sendToDaemon("acknowledge-annotation", {
368
+ annotationId: args2.annotationId
369
+ });
370
+ return success({ acknowledged: true, annotationId: args2.annotationId });
371
+ } catch (err) {
372
+ return error(`Failed to acknowledge: ${err.message}`);
373
+ }
374
+ }
375
+ case "skema_resolve": {
376
+ try {
377
+ await sendToDaemon("resolve-annotation", {
378
+ annotationId: args2.annotationId,
379
+ summary: args2.summary
380
+ });
381
+ return success({
382
+ resolved: true,
383
+ annotationId: args2.annotationId,
384
+ summary: args2.summary
385
+ });
386
+ } catch (err) {
387
+ return error(`Failed to resolve: ${err.message}`);
388
+ }
389
+ }
390
+ case "skema_dismiss": {
391
+ try {
392
+ await sendToDaemon("dismiss-annotation", {
393
+ annotationId: args2.annotationId,
394
+ reason: args2.reason
395
+ });
396
+ return success({
397
+ dismissed: true,
398
+ annotationId: args2.annotationId,
399
+ reason: args2.reason
400
+ });
401
+ } catch (err) {
402
+ return error(`Failed to dismiss: ${err.message}`);
403
+ }
404
+ }
405
+ case "skema_watch": {
406
+ const timeoutSeconds = Math.min(300, Math.max(1, args2?.timeoutSeconds ?? 120));
407
+ const pollInterval = 2e3;
408
+ const maxPolls = Math.ceil(timeoutSeconds * 1e3 / pollInterval);
409
+ for (let i = 0; i < maxPolls; i++) {
410
+ try {
411
+ const response = await sendToDaemon("get-pending-annotations");
412
+ if (response.count > 0) {
413
+ return success({
414
+ timeout: false,
415
+ count: response.count,
416
+ annotations: response.annotations
417
+ });
418
+ }
419
+ } catch (err) {
420
+ return error(`Lost connection to daemon: ${err.message}`);
421
+ }
422
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
423
+ }
424
+ return success({
425
+ timeout: true,
426
+ message: `No new annotations within ${timeoutSeconds} seconds`
427
+ });
428
+ }
429
+ default:
430
+ return error(`Unknown tool: ${name}`);
431
+ }
432
+ }
433
+ async function startMcpServer() {
434
+ try {
435
+ await connectToDaemon();
436
+ } catch {
437
+ console.error("[Skema MCP] Warning: Could not connect to daemon. Will auto-retry...");
438
+ scheduleReconnect();
439
+ }
440
+ const server = new index_js.Server(
441
+ {
442
+ name: "skema",
443
+ version: "0.3.0"
444
+ },
445
+ {
446
+ capabilities: {
447
+ tools: {},
448
+ resources: {}
449
+ }
450
+ }
451
+ );
452
+ server.setRequestHandler(types_js.ListToolsRequestSchema, async () => {
453
+ return { tools: TOOLS };
454
+ });
455
+ server.setRequestHandler(types_js.CallToolRequestSchema, async (request) => {
456
+ const { name, arguments: args2 } = request.params;
457
+ try {
458
+ return await handleTool(name, args2);
459
+ } catch (err) {
460
+ const message = err instanceof Error ? err.message : String(err);
461
+ return error(message);
462
+ }
463
+ });
464
+ server.setRequestHandler(types_js.ListResourcesRequestSchema, async () => {
465
+ return {
466
+ resources: [
467
+ {
468
+ uri: "skema://status",
469
+ name: "Skema Status",
470
+ description: "Current Skema daemon connection status and pending annotation count",
471
+ mimeType: "application/json"
472
+ }
473
+ ]
474
+ };
475
+ });
476
+ server.setRequestHandler(types_js.ReadResourceRequestSchema, async (request) => {
477
+ const { uri } = request.params;
478
+ switch (uri) {
479
+ case "skema://status": {
480
+ let pendingCount = 0;
481
+ let connected = false;
482
+ try {
483
+ const response = await sendToDaemon("get-pending-annotations");
484
+ pendingCount = response.count;
485
+ connected = true;
486
+ } catch {
487
+ connected = false;
488
+ }
489
+ return {
490
+ contents: [{
491
+ uri,
492
+ mimeType: "application/json",
493
+ text: JSON.stringify({
494
+ daemonConnected: connected,
495
+ daemonUrl: DAEMON_URL,
496
+ pendingAnnotations: pendingCount
497
+ }, null, 2)
498
+ }]
499
+ };
500
+ }
501
+ default:
502
+ throw new Error(`Unknown resource: ${uri}`);
503
+ }
504
+ });
505
+ server.oninitialized = () => {
506
+ const clientInfo = server.getClientVersion();
507
+ const clientName = clientInfo?.name || "unknown";
508
+ console.error("[Skema MCP] Connected client:", clientName, clientInfo?.version || "");
509
+ if (daemonWs && daemonConnected) {
510
+ daemonWs.send(JSON.stringify({
511
+ id: `mcp-client-info-${Date.now()}`,
512
+ type: "mcp-client-info",
513
+ clientName,
514
+ clientVersion: clientInfo?.version
515
+ }));
516
+ }
517
+ };
518
+ const transport = new stdio_js.StdioServerTransport();
519
+ await server.connect(transport);
520
+ console.error("[Skema MCP] Server started");
521
+ console.error("[Skema MCP] Daemon URL:", DAEMON_URL);
522
+ console.error("[Skema MCP] Tools: skema_get_pending, skema_acknowledge, skema_resolve, skema_dismiss, skema_watch");
523
+ }
524
+ var DAEMON_PORT, DAEMON_URL, daemonWs, daemonConnected, reconnectTimer, RECONNECT_INTERVAL, messageIdCounter, pendingRequests, TOOLS;
525
+ var init_server = __esm({
526
+ "src/mcp/server.ts"() {
527
+ DAEMON_PORT = parseInt(process.env.SKEMA_PORT || "9999", 10);
528
+ DAEMON_URL = `ws://localhost:${DAEMON_PORT}`;
529
+ daemonWs = null;
530
+ daemonConnected = false;
531
+ reconnectTimer = null;
532
+ RECONNECT_INTERVAL = 3e3;
533
+ messageIdCounter = 0;
534
+ pendingRequests = /* @__PURE__ */ new Map();
535
+ TOOLS = [
536
+ {
537
+ name: "skema_get_pending",
538
+ description: "Get all pending annotations from the Skema browser overlay. These are visual annotations (DOM selections, drawings, multi-selects) that the user has made on their web page and wants you to implement as code changes. Each annotation includes the user's comment describing what they want, plus element selectors, CSS paths, bounding boxes, and other context to help you find the right code to modify.",
539
+ inputSchema: {
540
+ type: "object",
541
+ properties: {},
542
+ required: []
543
+ }
544
+ },
545
+ {
546
+ name: "skema_get_all_annotations",
547
+ description: "Get all annotations (pending, acknowledged, resolved, dismissed). Useful for reviewing the full history of annotation requests and their current status.",
548
+ inputSchema: {
549
+ type: "object",
550
+ properties: {},
551
+ required: []
552
+ }
553
+ },
554
+ {
555
+ name: "skema_get_annotation",
556
+ description: "Get details of a specific annotation by ID.",
557
+ inputSchema: {
558
+ type: "object",
559
+ properties: {
560
+ annotationId: {
561
+ type: "string",
562
+ description: "The annotation ID to look up"
563
+ }
564
+ },
565
+ required: ["annotationId"]
566
+ }
567
+ },
568
+ {
569
+ name: "skema_acknowledge",
570
+ description: "Mark an annotation as acknowledged. Use this to tell the user you've seen their annotation and are working on it. The browser overlay will update to show the status change.",
571
+ inputSchema: {
572
+ type: "object",
573
+ properties: {
574
+ annotationId: {
575
+ type: "string",
576
+ description: "The annotation ID to acknowledge"
577
+ }
578
+ },
579
+ required: ["annotationId"]
580
+ }
581
+ },
582
+ {
583
+ name: "skema_resolve",
584
+ description: "Mark an annotation as resolved after you've implemented the requested change. Include a summary of what you did so the user can see it in the browser overlay.",
585
+ inputSchema: {
586
+ type: "object",
587
+ properties: {
588
+ annotationId: {
589
+ type: "string",
590
+ description: "The annotation ID to resolve"
591
+ },
592
+ summary: {
593
+ type: "string",
594
+ description: "Summary of what was done to resolve this annotation"
595
+ }
596
+ },
597
+ required: ["annotationId"]
598
+ }
599
+ },
600
+ {
601
+ name: "skema_dismiss",
602
+ description: "Dismiss an annotation if you decide not to implement it. Include a reason so the user understands why.",
603
+ inputSchema: {
604
+ type: "object",
605
+ properties: {
606
+ annotationId: {
607
+ type: "string",
608
+ description: "The annotation ID to dismiss"
609
+ },
610
+ reason: {
611
+ type: "string",
612
+ description: "Reason for dismissing this annotation"
613
+ }
614
+ },
615
+ required: ["annotationId", "reason"]
616
+ }
617
+ },
618
+ {
619
+ name: "skema_watch",
620
+ description: "Wait for new annotations to appear. This blocks until the user creates new annotations in the browser overlay, then returns them as a batch. Use this in a loop for hands-free processing: call skema_watch, process the returned annotations, resolve them, then call skema_watch again.",
621
+ inputSchema: {
622
+ type: "object",
623
+ properties: {
624
+ timeoutSeconds: {
625
+ type: "number",
626
+ description: "Max seconds to wait for annotations (default: 120, max: 300)"
627
+ }
628
+ },
629
+ required: []
630
+ }
631
+ }
632
+ ];
633
+ }
634
+ });
224
635
  var PROVIDERS = {
225
636
  gemini: {
226
637
  command: "gemini",
@@ -430,6 +841,50 @@ function isProviderAvailable(provider) {
430
841
  function getAvailableProviders() {
431
842
  return ["gemini", "claude"].filter(isProviderAvailable);
432
843
  }
844
+ function checkProviderAuthorized(provider) {
845
+ const { execSync: execSync3 } = __require("child_process");
846
+ try {
847
+ if (provider === "gemini") {
848
+ execSync3("gemini --version", { stdio: "ignore", timeout: 5e3 });
849
+ return true;
850
+ } else if (provider === "claude") {
851
+ execSync3("claude --version", { stdio: "ignore", timeout: 5e3 });
852
+ return true;
853
+ }
854
+ return false;
855
+ } catch {
856
+ return false;
857
+ }
858
+ }
859
+ function getProviderStatus(provider) {
860
+ const installed = isProviderAvailable(provider);
861
+ if (!installed) {
862
+ return {
863
+ installed: false,
864
+ authorized: false,
865
+ message: `${provider} CLI not installed`
866
+ };
867
+ }
868
+ const authorized = checkProviderAuthorized(provider);
869
+ if (!authorized) {
870
+ return {
871
+ installed: true,
872
+ authorized: false,
873
+ message: `${provider} CLI installed but not authorized`
874
+ };
875
+ }
876
+ return {
877
+ installed: true,
878
+ authorized: true,
879
+ message: "Ready"
880
+ };
881
+ }
882
+ function getAllProviderStatuses() {
883
+ return {
884
+ gemini: getProviderStatus("gemini"),
885
+ claude: getProviderStatus("claude")
886
+ };
887
+ }
433
888
 
434
889
  // src/lib/utils.ts
435
890
  function getGridCellReference(x, y, gridSize = 100) {
@@ -442,7 +897,7 @@ function getGridCellReference(x, y, gridSize = 100) {
442
897
  // src/server/prompts.ts
443
898
  var CRITICAL_RULES = `CRITICAL RULES:
444
899
  - Do NOT create new files. Only edit existing files.
445
- - Do NOT modify the import { SkemaWrapper } from "@/components/skema-wrapper" line.
900
+ - Do NOT modify the Skema overlay component import or the SkemaOverlay component itself.
446
901
  - Ensure all JSX tags are properly closed - every <tag> needs a matching </tag>.
447
902
  - Do NOT run any shell commands. No npm, no git, no lint, no build commands. Just edit files.
448
903
  - STOP immediately after making the file changes. Do not verify, do not run tests, do not check status.`;
@@ -581,7 +1036,7 @@ var DRAWING_IMPLEMENTATION_GUIDELINES = `- **CRITICAL: DO NOT CREATE ANY NEW FIL
581
1036
  var DRAWING_ERROR_PREVENTION_RULES = `1. **NEVER CREATE NEW FILES** - Do NOT create new component files, utility files, or any other files. Write everything inline in the existing file
582
1037
  2. You do not need to update package.json or anything, just add / edit the react component.
583
1038
  3. Do NOT add import statements in the middle of the file or inside JSX - imports go ONLY at the top
584
- 4. Do NOT modify the import { SkemaWrapper } from "@/components/skema-wrapper" line or the SkemaWrapper component itself
1039
+ 4. Do NOT modify the Skema overlay component import or the SkemaOverlay component itself
585
1040
  5. If you need something that requires an import and it's not already imported, either use an alternative that doesn't need an import, or add the import at the very TOP of the file with the other imports
586
1041
  6. DONT MAKE ANY CHANGES THAT WOULD RESULT IN A Build Error
587
1042
  7. **JSX SYNTAX VALIDATION** - ALWAYS ensure every JSX tag is properly closed. Every opening tag like <div>, <a>, <span>, <button> MUST have a matching closing tag </div>, </a>, </span>, </button>. Self-closing tags like <img />, <input />, <br /> must end with />. Before finishing, mentally verify all tag pairs are balanced.`;
@@ -702,8 +1157,8 @@ async function analyzeWithGemini(base64Image, apiKey, model = "gemini-2.5-flash"
702
1157
  description: text,
703
1158
  provider: "gemini"
704
1159
  };
705
- } catch (error) {
706
- const message = error instanceof Error ? error.message : String(error);
1160
+ } catch (error2) {
1161
+ const message = error2 instanceof Error ? error2.message : String(error2);
707
1162
  console.error("[Vision] Gemini analysis failed:", message);
708
1163
  return {
709
1164
  success: false,
@@ -748,8 +1203,8 @@ async function analyzeWithClaude(base64Image, apiKey, model = "claude-sonnet-4-2
748
1203
  description,
749
1204
  provider: "claude"
750
1205
  };
751
- } catch (error) {
752
- const message = error instanceof Error ? error.message : String(error);
1206
+ } catch (error2) {
1207
+ const message = error2 instanceof Error ? error2.message : String(error2);
753
1208
  console.error("[Vision] Claude analysis failed:", message);
754
1209
  return {
755
1210
  success: false,
@@ -786,10 +1241,97 @@ function isVisionAvailable(provider) {
786
1241
  }
787
1242
  }
788
1243
 
1244
+ // src/server/annotation-store.ts
1245
+ var storedAnnotations = /* @__PURE__ */ new Map();
1246
+ var listeners = /* @__PURE__ */ new Set();
1247
+ function notify(event, annotation) {
1248
+ for (const listener of listeners) {
1249
+ try {
1250
+ listener(event, annotation);
1251
+ } catch (e) {
1252
+ console.error("[AnnotationStore] Listener error:", e);
1253
+ }
1254
+ }
1255
+ }
1256
+ function queueAnnotation(annotation, comment) {
1257
+ const id = annotation.id || `ann-${Date.now()}`;
1258
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1259
+ const stored = {
1260
+ annotation: { ...annotation, id },
1261
+ comment,
1262
+ status: "pending",
1263
+ createdAt: now,
1264
+ updatedAt: now
1265
+ };
1266
+ storedAnnotations.set(id, stored);
1267
+ console.log(`[AnnotationStore] Queued annotation ${id}: "${comment.slice(0, 50)}..."`);
1268
+ notify("annotation.created", stored);
1269
+ return stored;
1270
+ }
1271
+ function getPendingAnnotations() {
1272
+ return Array.from(storedAnnotations.values()).filter((a) => a.status === "pending");
1273
+ }
1274
+ function getAllAnnotations() {
1275
+ return Array.from(storedAnnotations.values());
1276
+ }
1277
+ function getAnnotation(id) {
1278
+ return storedAnnotations.get(id);
1279
+ }
1280
+ function acknowledgeAnnotation(id) {
1281
+ const stored = storedAnnotations.get(id);
1282
+ if (!stored) return void 0;
1283
+ stored.status = "acknowledged";
1284
+ stored.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1285
+ notify("annotation.updated", stored);
1286
+ return stored;
1287
+ }
1288
+ function resolveAnnotation(id, summary) {
1289
+ const stored = storedAnnotations.get(id);
1290
+ if (!stored) return void 0;
1291
+ stored.status = "resolved";
1292
+ stored.resolvedBy = "agent";
1293
+ stored.resolutionSummary = summary;
1294
+ stored.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1295
+ notify("annotation.updated", stored);
1296
+ return stored;
1297
+ }
1298
+ function dismissAnnotation(id, reason) {
1299
+ const stored = storedAnnotations.get(id);
1300
+ if (!stored) return void 0;
1301
+ stored.status = "dismissed";
1302
+ stored.resolvedBy = "agent";
1303
+ stored.dismissalReason = reason;
1304
+ stored.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1305
+ notify("annotation.updated", stored);
1306
+ return stored;
1307
+ }
1308
+ function clearAnnotations() {
1309
+ storedAnnotations.clear();
1310
+ }
1311
+ function getPendingCount() {
1312
+ let count = 0;
1313
+ for (const a of storedAnnotations.values()) {
1314
+ if (a.status === "pending") count++;
1315
+ }
1316
+ return count;
1317
+ }
1318
+
789
1319
  // src/server/daemon.ts
790
1320
  var currentProvider = "gemini";
791
1321
  var workingDirectory = process.cwd();
1322
+ var currentMode = "direct-cli";
792
1323
  var annotationSnapshots2 = /* @__PURE__ */ new Map();
1324
+ var mcpServerClient = null;
1325
+ var mcpClientName = null;
1326
+ function getAnnotationCounts() {
1327
+ const all = getAllAnnotations();
1328
+ return {
1329
+ pending: all.filter((a) => a.status === "pending").length,
1330
+ acknowledged: all.filter((a) => a.status === "acknowledged").length,
1331
+ resolved: all.filter((a) => a.status === "resolved").length,
1332
+ dismissed: all.filter((a) => a.status === "dismissed").length
1333
+ };
1334
+ }
793
1335
  function createSnapshot2(annotationId) {
794
1336
  try {
795
1337
  const stashRef = child_process.execSync("git stash create", { cwd: workingDirectory, encoding: "utf-8" }).trim();
@@ -801,8 +1343,8 @@ function createSnapshot2(annotationId) {
801
1343
  const headRef = child_process.execSync("git rev-parse HEAD", { cwd: workingDirectory, encoding: "utf-8" }).trim();
802
1344
  annotationSnapshots2.set(annotationId, headRef);
803
1345
  return headRef;
804
- } catch (error) {
805
- console.error("[Daemon] Failed to create snapshot:", error);
1346
+ } catch (error2) {
1347
+ console.error("[Daemon] Failed to create snapshot:", error2);
806
1348
  return null;
807
1349
  }
808
1350
  }
@@ -828,8 +1370,8 @@ function revertSnapshot(annotationId) {
828
1370
  }
829
1371
  annotationSnapshots2.delete(annotationId);
830
1372
  return { success: true, message: `Reverted ${changedFiles.length} file(s)` };
831
- } catch (error) {
832
- return { success: false, message: String(error) };
1373
+ } catch (error2) {
1374
+ return { success: false, message: String(error2) };
833
1375
  }
834
1376
  }
835
1377
  var handlers = {
@@ -849,17 +1391,45 @@ var handlers = {
849
1391
  if (!["gemini", "claude"].includes(newProvider)) {
850
1392
  return { id: msg.id, type: "error", error: `Invalid provider: ${newProvider}` };
851
1393
  }
852
- if (!isProviderAvailable(newProvider)) {
1394
+ if (currentMode === "direct-cli" && !isProviderAvailable(newProvider)) {
853
1395
  return {
854
1396
  id: msg.id,
855
1397
  type: "error",
856
- error: `Provider "${newProvider}" is not installed. Run: ${newProvider === "gemini" ? "npm install -g @anthropic-ai/gemini-cli" : "npm install -g @anthropic-ai/claude-code"}`
1398
+ error: `Provider "${newProvider}" CLI is not installed.`
857
1399
  };
858
1400
  }
859
1401
  currentProvider = newProvider;
860
1402
  console.log(`[Daemon] Switched to provider: ${currentProvider}`);
861
1403
  return { id: msg.id, type: "provider-changed", provider: currentProvider };
862
1404
  },
1405
+ "check-providers": async (msg) => {
1406
+ const statuses = getAllProviderStatuses();
1407
+ return {
1408
+ id: msg.id,
1409
+ type: "provider-statuses",
1410
+ providerStatus: statuses
1411
+ };
1412
+ },
1413
+ // -------------------------------------------------------------------------
1414
+ // Mode Management
1415
+ // -------------------------------------------------------------------------
1416
+ "get-mode": async (msg) => {
1417
+ return {
1418
+ id: msg.id,
1419
+ type: "mode",
1420
+ mode: currentMode,
1421
+ availableModes: ["direct-cli", "mcp"]
1422
+ };
1423
+ },
1424
+ "set-mode": async (msg) => {
1425
+ const newMode = msg.mode;
1426
+ if (!["direct-cli", "mcp"].includes(newMode)) {
1427
+ return { id: msg.id, type: "error", error: `Invalid mode: ${newMode}` };
1428
+ }
1429
+ currentMode = newMode;
1430
+ console.log(`[Daemon] Switched to mode: ${currentMode}`);
1431
+ return { id: msg.id, type: "mode-changed", mode: currentMode };
1432
+ },
863
1433
  // -------------------------------------------------------------------------
864
1434
  // AI Generation (streaming)
865
1435
  // -------------------------------------------------------------------------
@@ -867,21 +1437,41 @@ var handlers = {
867
1437
  const annotation = msg.annotation;
868
1438
  const projectContext = msg.projectContext;
869
1439
  const annotationId = annotation.id || `temp-${Date.now()}`;
1440
+ const requestMode = msg.mode || currentMode;
1441
+ const requestProvider = msg.provider || currentProvider;
1442
+ if (requestMode === "mcp") {
1443
+ const comment = annotation.comment || "";
1444
+ const stored = queueAnnotation(annotation, comment);
1445
+ const counts = getAnnotationCounts();
1446
+ sendMessage(ws, {
1447
+ id: msg.id,
1448
+ type: "annotation-queued",
1449
+ success: true,
1450
+ annotationId: stored.annotation.id,
1451
+ pendingCount: getPendingCount(),
1452
+ annotationCounts: counts
1453
+ });
1454
+ broadcastToClients({
1455
+ type: "mcp-annotation-counts",
1456
+ counts
1457
+ });
1458
+ return;
1459
+ }
870
1460
  createSnapshot2(annotationId);
871
1461
  let visionDescription = "";
872
1462
  const drawingAnnotation = annotation;
873
1463
  if (annotation.type === "drawing" && drawingAnnotation.drawingImage) {
1464
+ sendMessage(ws, {
1465
+ id: msg.id,
1466
+ type: "ai-event",
1467
+ event: {
1468
+ type: "text",
1469
+ content: `[Analyzing drawing with vision...]`,
1470
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1471
+ provider: requestProvider
1472
+ }
1473
+ });
874
1474
  if (isVisionAvailable()) {
875
- sendMessage(ws, {
876
- id: msg.id,
877
- type: "ai-event",
878
- event: {
879
- type: "text",
880
- content: `[Analyzing drawing with Gemini vision...]`,
881
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
882
- provider: currentProvider
883
- }
884
- });
885
1475
  const visionResult = await analyzeImage(drawingAnnotation.drawingImage, {
886
1476
  provider: "gemini"
887
1477
  });
@@ -895,7 +1485,7 @@ var handlers = {
895
1485
  content: `[Vision analysis complete]
896
1486
  ${visionDescription}`,
897
1487
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
898
- provider: currentProvider
1488
+ provider: requestProvider
899
1489
  }
900
1490
  });
901
1491
  } else {
@@ -906,7 +1496,7 @@ ${visionDescription}`,
906
1496
  type: "error",
907
1497
  content: `Vision analysis failed: ${visionResult.error}`,
908
1498
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
909
- provider: currentProvider
1499
+ provider: requestProvider
910
1500
  }
911
1501
  });
912
1502
  }
@@ -918,14 +1508,13 @@ ${visionDescription}`,
918
1508
  type: "text",
919
1509
  content: `[Vision not available - set GEMINI_API_KEY for image analysis]`,
920
1510
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
921
- provider: currentProvider
1511
+ provider: requestProvider
922
1512
  }
923
1513
  });
924
1514
  }
925
1515
  }
926
1516
  const prompt = buildPromptFromAnnotation(annotation, projectContext, {
927
1517
  fastMode: msg.fastMode === true,
928
- // Default to detailed mode (false) unless explicitly set to true
929
1518
  visionDescription
930
1519
  });
931
1520
  sendMessage(ws, {
@@ -935,11 +1524,25 @@ ${visionDescription}`,
935
1524
  type: "debug",
936
1525
  content: prompt,
937
1526
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
938
- provider: currentProvider
1527
+ provider: requestProvider
939
1528
  }
940
1529
  });
1530
+ const cliProvider = requestProvider;
1531
+ if (!isProviderAvailable(cliProvider)) {
1532
+ sendMessage(ws, {
1533
+ id: msg.id,
1534
+ type: "ai-event",
1535
+ event: {
1536
+ type: "error",
1537
+ content: `${requestProvider} CLI is not installed. Run: npm install -g @google/gemini-cli`,
1538
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1539
+ provider: requestProvider
1540
+ }
1541
+ });
1542
+ return;
1543
+ }
941
1544
  const config = {
942
- provider: currentProvider,
1545
+ provider: cliProvider,
943
1546
  cwd: workingDirectory,
944
1547
  model: msg.model
945
1548
  };
@@ -957,7 +1560,8 @@ ${visionDescription}`,
957
1560
  type: "generate-complete",
958
1561
  success: true,
959
1562
  annotationId,
960
- provider: currentProvider
1563
+ provider: requestProvider,
1564
+ mode: requestMode
961
1565
  });
962
1566
  break;
963
1567
  }
@@ -975,6 +1579,109 @@ ${visionDescription}`,
975
1579
  return { id: msg.id, type: "revert-result", ...result };
976
1580
  },
977
1581
  // -------------------------------------------------------------------------
1582
+ // MCP Annotation Queue Management
1583
+ // -------------------------------------------------------------------------
1584
+ "get-pending-annotations": async (msg) => {
1585
+ const pending = getPendingAnnotations();
1586
+ return {
1587
+ id: msg.id,
1588
+ type: "pending-annotations",
1589
+ count: pending.length,
1590
+ annotations: pending.map(serializeStoredAnnotation)
1591
+ };
1592
+ },
1593
+ "get-all-annotations": async (msg) => {
1594
+ const all = getAllAnnotations();
1595
+ return {
1596
+ id: msg.id,
1597
+ type: "all-annotations",
1598
+ count: all.length,
1599
+ annotations: all.map(serializeStoredAnnotation)
1600
+ };
1601
+ },
1602
+ "get-annotation": async (msg) => {
1603
+ const id = msg.annotationId;
1604
+ const stored = getAnnotation(id);
1605
+ if (!stored) {
1606
+ return { id: msg.id, type: "error", error: `Annotation not found: ${id}` };
1607
+ }
1608
+ return {
1609
+ id: msg.id,
1610
+ type: "annotation",
1611
+ annotation: serializeStoredAnnotation(stored)
1612
+ };
1613
+ },
1614
+ "acknowledge-annotation": async (msg) => {
1615
+ const id = msg.annotationId;
1616
+ const stored = acknowledgeAnnotation(id);
1617
+ if (!stored) {
1618
+ return { id: msg.id, type: "error", error: `Annotation not found: ${id}` };
1619
+ }
1620
+ const counts = getAnnotationCounts();
1621
+ broadcastToClients({
1622
+ type: "annotation-status-changed",
1623
+ annotationId: id,
1624
+ status: "acknowledged"
1625
+ });
1626
+ broadcastToClients({ type: "mcp-annotation-counts", counts });
1627
+ return {
1628
+ id: msg.id,
1629
+ type: "annotation-acknowledged",
1630
+ annotationId: id
1631
+ };
1632
+ },
1633
+ "resolve-annotation": async (msg) => {
1634
+ const id = msg.annotationId;
1635
+ const summary = msg.summary;
1636
+ const stored = resolveAnnotation(id, summary);
1637
+ if (!stored) {
1638
+ return { id: msg.id, type: "error", error: `Annotation not found: ${id}` };
1639
+ }
1640
+ const counts = getAnnotationCounts();
1641
+ broadcastToClients({
1642
+ type: "annotation-status-changed",
1643
+ annotationId: id,
1644
+ status: "resolved",
1645
+ summary
1646
+ });
1647
+ broadcastToClients({ type: "mcp-annotation-counts", counts });
1648
+ return {
1649
+ id: msg.id,
1650
+ type: "annotation-resolved",
1651
+ annotationId: id,
1652
+ summary
1653
+ };
1654
+ },
1655
+ "dismiss-annotation": async (msg) => {
1656
+ const id = msg.annotationId;
1657
+ const reason = msg.reason;
1658
+ const stored = dismissAnnotation(id, reason);
1659
+ if (!stored) {
1660
+ return { id: msg.id, type: "error", error: `Annotation not found: ${id}` };
1661
+ }
1662
+ const counts = getAnnotationCounts();
1663
+ broadcastToClients({
1664
+ type: "annotation-status-changed",
1665
+ annotationId: id,
1666
+ status: "dismissed",
1667
+ reason
1668
+ });
1669
+ broadcastToClients({ type: "mcp-annotation-counts", counts });
1670
+ return {
1671
+ id: msg.id,
1672
+ type: "annotation-dismissed",
1673
+ annotationId: id,
1674
+ reason
1675
+ };
1676
+ },
1677
+ "clear-queued-annotations": async (msg) => {
1678
+ clearAnnotations();
1679
+ return {
1680
+ id: msg.id,
1681
+ type: "annotations-cleared"
1682
+ };
1683
+ },
1684
+ // -------------------------------------------------------------------------
978
1685
  // File Operations
979
1686
  // -------------------------------------------------------------------------
980
1687
  "read-file": async (msg) => {
@@ -983,8 +1690,8 @@ ${visionDescription}`,
983
1690
  try {
984
1691
  const content = fs__namespace.readFileSync(absolutePath, "utf-8");
985
1692
  return { id: msg.id, type: "file-content", path: filePath, content };
986
- } catch (error) {
987
- return { id: msg.id, type: "error", error: `Failed to read file: ${error}` };
1693
+ } catch (error2) {
1694
+ return { id: msg.id, type: "error", error: `Failed to read file: ${error2}` };
988
1695
  }
989
1696
  },
990
1697
  "write-file": async (msg) => {
@@ -996,8 +1703,8 @@ ${visionDescription}`,
996
1703
  fs__namespace.writeFileSync(absolutePath, content, "utf-8");
997
1704
  console.log(`[Daemon] Wrote file: ${filePath}`);
998
1705
  return { id: msg.id, type: "write-success", path: filePath };
999
- } catch (error) {
1000
- return { id: msg.id, type: "error", error: `Failed to write file: ${error}` };
1706
+ } catch (error2) {
1707
+ return { id: msg.id, type: "error", error: `Failed to write file: ${error2}` };
1001
1708
  }
1002
1709
  },
1003
1710
  "list-files": async (msg) => {
@@ -1010,8 +1717,8 @@ ${visionDescription}`,
1010
1717
  isDirectory: entry.isDirectory()
1011
1718
  }));
1012
1719
  return { id: msg.id, type: "file-list", path: dirPath, files };
1013
- } catch (error) {
1014
- return { id: msg.id, type: "error", error: `Failed to list files: ${error}` };
1720
+ } catch (error2) {
1721
+ return { id: msg.id, type: "error", error: `Failed to list files: ${error2}` };
1015
1722
  }
1016
1723
  },
1017
1724
  // -------------------------------------------------------------------------
@@ -1024,18 +1731,45 @@ ${visionDescription}`,
1024
1731
  }
1025
1732
  console.log(`[Daemon] Running command: ${command2}`);
1026
1733
  return new Promise((resolve) => {
1027
- child_process.exec(command2, { cwd: workingDirectory }, (error, stdout, stderr) => {
1734
+ child_process.exec(command2, { cwd: workingDirectory }, (error2, stdout, stderr) => {
1028
1735
  resolve({
1029
1736
  id: msg.id,
1030
1737
  type: "command-result",
1031
1738
  stdout,
1032
1739
  stderr,
1033
- exitCode: error ? error.code : 0
1740
+ exitCode: error2 ? error2.code : 0
1034
1741
  });
1035
1742
  });
1036
1743
  });
1037
1744
  },
1038
1745
  // -------------------------------------------------------------------------
1746
+ // MCP Server Identification
1747
+ // -------------------------------------------------------------------------
1748
+ identify: async (msg, ws) => {
1749
+ if (msg.client === "mcp-server") {
1750
+ mcpServerClient = ws;
1751
+ mcpClientName = null;
1752
+ console.log("[Daemon] MCP server identified and connected");
1753
+ broadcastToClients({
1754
+ type: "mcp-server-status",
1755
+ connected: true,
1756
+ clientName: null
1757
+ });
1758
+ return { id: msg.id, type: "identified", client: "mcp-server" };
1759
+ }
1760
+ return { id: msg.id, type: "identified", client: "unknown" };
1761
+ },
1762
+ "mcp-client-info": async (msg) => {
1763
+ mcpClientName = msg.clientName || null;
1764
+ console.log("[Daemon] MCP client identified:", mcpClientName, msg.clientVersion || "");
1765
+ broadcastToClients({
1766
+ type: "mcp-server-status",
1767
+ connected: true,
1768
+ clientName: mcpClientName
1769
+ });
1770
+ return { id: msg.id, type: "ok" };
1771
+ },
1772
+ // -------------------------------------------------------------------------
1039
1773
  // Status
1040
1774
  // -------------------------------------------------------------------------
1041
1775
  ping: async (msg) => {
@@ -1043,23 +1777,70 @@ ${visionDescription}`,
1043
1777
  id: msg.id,
1044
1778
  type: "pong",
1045
1779
  provider: currentProvider,
1780
+ mode: currentMode,
1046
1781
  cwd: workingDirectory,
1047
- availableProviders: getAvailableProviders()
1782
+ availableProviders: getAvailableProviders(),
1783
+ availableModes: ["direct-cli", "mcp"],
1784
+ mcpServerConnected: mcpServerClient?.readyState === WebSocket2.WebSocket.OPEN,
1785
+ mcpClientName
1048
1786
  };
1049
1787
  }
1050
1788
  };
1051
- function sendMessage(ws$1, message) {
1052
- if (ws$1.readyState === ws.WebSocket.OPEN) {
1053
- ws$1.send(JSON.stringify(message));
1789
+ var connectedClients = /* @__PURE__ */ new Set();
1790
+ function broadcastToClients(message) {
1791
+ for (const client of connectedClients) {
1792
+ sendMessage(client, message);
1793
+ }
1794
+ }
1795
+ function serializeStoredAnnotation(stored) {
1796
+ return {
1797
+ id: stored.annotation.id,
1798
+ type: stored.annotation.type,
1799
+ comment: stored.comment,
1800
+ status: stored.status,
1801
+ createdAt: stored.createdAt,
1802
+ updatedAt: stored.updatedAt,
1803
+ resolvedBy: stored.resolvedBy,
1804
+ resolutionSummary: stored.resolutionSummary,
1805
+ dismissalReason: stored.dismissalReason,
1806
+ // Include key annotation data the agent needs
1807
+ annotation: {
1808
+ type: stored.annotation.type,
1809
+ selector: stored.annotation.selector,
1810
+ tagName: stored.annotation.tagName,
1811
+ text: stored.annotation.text,
1812
+ elementPath: stored.annotation.elementPath,
1813
+ boundingBox: stored.annotation.boundingBox,
1814
+ drawingSvg: stored.annotation.drawingSvg,
1815
+ drawingImage: stored.annotation.drawingImage,
1816
+ extractedText: stored.annotation.extractedText,
1817
+ nearbyElements: stored.annotation.nearbyElements,
1818
+ viewport: stored.annotation.viewport,
1819
+ projectStyles: stored.annotation.projectStyles,
1820
+ isMultiSelect: stored.annotation.isMultiSelect,
1821
+ elements: stored.annotation.elements
1822
+ }
1823
+ };
1824
+ }
1825
+ function sendMessage(ws, message) {
1826
+ if (ws.readyState === WebSocket2.WebSocket.OPEN) {
1827
+ ws.send(JSON.stringify(message));
1054
1828
  }
1055
1829
  }
1056
1830
  function handleConnection(ws) {
1057
1831
  console.log("[Daemon] Client connected");
1832
+ connectedClients.add(ws);
1058
1833
  sendMessage(ws, {
1059
1834
  type: "connected",
1060
1835
  provider: currentProvider,
1836
+ mode: currentMode,
1061
1837
  cwd: workingDirectory,
1062
- availableProviders: getAvailableProviders()
1838
+ availableProviders: getAvailableProviders(),
1839
+ availableModes: ["direct-cli", "mcp"],
1840
+ pendingAnnotations: currentMode === "mcp" ? getPendingCount() : 0,
1841
+ providerStatus: getAllProviderStatuses(),
1842
+ mcpServerConnected: mcpServerClient?.readyState === WebSocket2.WebSocket.OPEN,
1843
+ mcpClientName
1063
1844
  });
1064
1845
  ws.on("message", async (data) => {
1065
1846
  let msg;
@@ -1079,55 +1860,72 @@ function handleConnection(ws) {
1079
1860
  if (response) {
1080
1861
  sendMessage(ws, response);
1081
1862
  }
1082
- } catch (error) {
1863
+ } catch (error2) {
1083
1864
  sendMessage(ws, {
1084
1865
  id: msg.id,
1085
1866
  type: "error",
1086
- error: `Handler error: ${error}`
1867
+ error: `Handler error: ${error2}`
1087
1868
  });
1088
1869
  }
1089
1870
  });
1090
1871
  ws.on("close", () => {
1091
1872
  console.log("[Daemon] Client disconnected");
1873
+ connectedClients.delete(ws);
1874
+ if (ws === mcpServerClient) {
1875
+ mcpServerClient = null;
1876
+ mcpClientName = null;
1877
+ console.log("[Daemon] MCP server disconnected");
1878
+ broadcastToClients({
1879
+ type: "mcp-server-status",
1880
+ connected: false,
1881
+ clientName: null
1882
+ });
1883
+ }
1092
1884
  });
1093
- ws.on("error", (error) => {
1094
- console.error("[Daemon] WebSocket error:", error);
1885
+ ws.on("error", (error2) => {
1886
+ console.error("[Daemon] WebSocket error:", error2);
1095
1887
  });
1096
1888
  }
1097
1889
  function startDaemon(config = {}) {
1098
1890
  const port = config.port ?? 9999;
1099
1891
  workingDirectory = config.cwd ?? process.cwd();
1100
1892
  currentProvider = config.defaultProvider ?? "gemini";
1101
- if (!isProviderAvailable(currentProvider)) {
1102
- const available = getAvailableProviders();
1103
- if (available.length > 0) {
1104
- console.log(`[Daemon] ${currentProvider} not found, falling back to ${available[0]}`);
1105
- currentProvider = available[0];
1106
- } else {
1107
- console.warn("[Daemon] Warning: No AI providers found. Install gemini or claude CLI.");
1893
+ currentMode = config.defaultMode ?? "direct-cli";
1894
+ if (currentMode === "direct-cli") {
1895
+ if (!isProviderAvailable(currentProvider)) {
1896
+ const available = getAvailableProviders();
1897
+ if (available.length > 0) {
1898
+ console.log(`[Daemon] ${currentProvider} CLI not found, falling back to ${available[0]}`);
1899
+ currentProvider = available[0];
1900
+ } else {
1901
+ console.warn("[Daemon] Warning: No CLI providers found. Install gemini or claude CLI.");
1902
+ }
1108
1903
  }
1109
1904
  }
1110
- const wss = new ws.WebSocketServer({ port });
1905
+ const wss = new WebSocket2.WebSocketServer({ port });
1111
1906
  wss.on("connection", handleConnection);
1112
1907
  wss.on("listening", () => {
1908
+ const cliProviders = getAvailableProviders();
1113
1909
  console.log("");
1114
1910
  console.log(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
1115
1911
  console.log(" \u2502 \u2502");
1116
- console.log(` \u2502 \u{1F3A8} Skema Daemon running on ws://localhost:${port} \u2502`);
1912
+ console.log(` \u2502 Skema Daemon running on ws://localhost:${port} \u2502`);
1117
1913
  console.log(" \u2502 \u2502");
1914
+ console.log(` \u2502 Mode: ${currentMode.padEnd(39)}\u2502`);
1118
1915
  console.log(` \u2502 Provider: ${currentProvider.padEnd(35)}\u2502`);
1119
1916
  console.log(` \u2502 Directory: ${workingDirectory.slice(-33).padEnd(34)}\u2502`);
1917
+ console.log(` \u2502 CLI Providers: ${(cliProviders.join(", ") || "none").padEnd(29)}\u2502`);
1120
1918
  console.log(" \u2502 \u2502");
1121
1919
  console.log(" \u2502 Waiting for browser connections... \u2502");
1122
1920
  console.log(" \u2502 \u2502");
1123
1921
  console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
1124
1922
  console.log("");
1125
1923
  });
1126
- wss.on("error", (error) => {
1127
- if (error.code === "EADDRINUSE") {
1924
+ wss.on("error", (error2) => {
1925
+ if (error2.code === "EADDRINUSE") {
1128
1926
  console.error(`[Daemon] Port ${port} is already in use. Is another Skema daemon running?`);
1129
1927
  } else {
1130
- console.error("[Daemon] Server error:", error);
1928
+ console.error("[Daemon] Server error:", error2);
1131
1929
  }
1132
1930
  });
1133
1931
  return {
@@ -1148,17 +1946,26 @@ function printHelp() {
1148
1946
  console.log("");
1149
1947
  console.log(" Usage:");
1150
1948
  console.log(" npx skema-core Start the daemon (default)");
1949
+ console.log(" npx skema-core --mcp Start as MCP server (for Cursor/Claude Desktop)");
1151
1950
  console.log(" npx skema-core init Configure your project");
1152
1951
  console.log(" npx skema-core help Show this help");
1153
1952
  console.log("");
1154
1953
  console.log(" Options (for daemon):");
1155
- console.log(" -p, --port <port> Port number (default: 9999)");
1156
- console.log(" -d, --dir <path> Working directory");
1157
- console.log(" --provider <name> Default AI provider (gemini|claude)");
1954
+ console.log(" -p, --port <port> Port number (default: 9999)");
1955
+ console.log(" -d, --dir <path> Working directory");
1956
+ console.log(" --provider <name> Default AI provider (gemini|claude)");
1957
+ console.log(" --mode <mode> Execution mode (direct-cli|mcp)");
1958
+ console.log(" --mcp Start as MCP server (stdio transport)");
1959
+ console.log("");
1960
+ console.log(" Execution Modes:");
1961
+ console.log(" direct-cli Use Gemini/Claude CLI agents (default, no API key needed)");
1962
+ console.log(" mcp Route through AI agent (Cursor, Claude Desktop)");
1158
1963
  console.log("");
1159
1964
  console.log(" Examples:");
1160
1965
  console.log(" npx skema-core");
1161
1966
  console.log(" npx skema-core --port 8080");
1967
+ console.log(" npx skema-core --provider claude");
1968
+ console.log(" npx skema-core --mcp");
1162
1969
  console.log(" npx skema-core init");
1163
1970
  console.log("");
1164
1971
  console.log(' Note: After installing skema-core, you can also use "skema" directly.');
@@ -1178,6 +1985,11 @@ function parseArgs(args2) {
1178
1985
  } else if (arg === "--provider") {
1179
1986
  config.defaultProvider = next;
1180
1987
  i++;
1988
+ } else if (arg === "--mode") {
1989
+ config.defaultMode = next;
1990
+ i++;
1991
+ } else if (arg === "--mcp") {
1992
+ config.mcp = true;
1181
1993
  }
1182
1994
  }
1183
1995
  return config;
@@ -1185,9 +1997,25 @@ function parseArgs(args2) {
1185
1997
  async function runInit() {
1186
1998
  await Promise.resolve().then(() => (init_init(), init_exports));
1187
1999
  }
2000
+ async function runMcpServer() {
2001
+ const { startMcpServer: startMcpServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2002
+ await startMcpServer2();
2003
+ }
1188
2004
  function runDaemon(args2) {
1189
2005
  const config = parseArgs(args2);
1190
- startDaemon(config);
2006
+ if (config.mcp) {
2007
+ runMcpServer().catch((error2) => {
2008
+ console.error("[Skema] MCP server error:", error2);
2009
+ process.exit(1);
2010
+ });
2011
+ return;
2012
+ }
2013
+ startDaemon({
2014
+ port: config.port,
2015
+ cwd: config.cwd,
2016
+ defaultProvider: config.defaultProvider,
2017
+ defaultMode: config.defaultMode
2018
+ });
1191
2019
  }
1192
2020
  if (command === "help" || command === "-h" || command === "--help") {
1193
2021
  printHelp();
@@ -1195,6 +2023,11 @@ if (command === "help" || command === "-h" || command === "--help") {
1195
2023
  runInit();
1196
2024
  } else if (command === "serve") {
1197
2025
  runDaemon(args.slice(1));
2026
+ } else if (command === "mcp" || command === "--mcp") {
2027
+ runMcpServer().catch((error2) => {
2028
+ console.error("[Skema] MCP server error:", error2);
2029
+ process.exit(1);
2030
+ });
1198
2031
  } else {
1199
2032
  runDaemon(args);
1200
2033
  }