vite-plugin-ai-annotator 1.14.9 → 1.14.11

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.
@@ -1080,114 +1080,142 @@ var MagicString = class _MagicString {
1080
1080
  }
1081
1081
  };
1082
1082
 
1083
- // src/auto-setup-mcp.ts
1083
+ // src/auto-setup-skills.ts
1084
1084
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
1085
1085
  import { join, dirname } from "node:path";
1086
- var MCP_SERVER_NAME = "ai-annotator";
1087
- function detectPackageManager(projectRoot) {
1088
- if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bun.lock"))) {
1089
- return "bunx";
1090
- }
1091
- if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) {
1092
- return "pnpm dlx";
1093
- }
1094
- return "npx";
1095
- }
1096
- function buildMcpServerConfig(packageManager, serverUrl) {
1097
- const baseArgs = ["vite-plugin-ai-annotator", "mcp", "-s", serverUrl];
1098
- if (packageManager === "pnpm dlx") {
1099
- return {
1100
- command: "pnpm",
1101
- args: ["dlx", ...baseArgs]
1102
- };
1103
- }
1104
- return {
1105
- command: packageManager,
1106
- args: baseArgs
1107
- };
1108
- }
1109
- function readJsonFile(filePath) {
1110
- try {
1111
- const content = readFileSync(filePath, "utf-8");
1112
- return JSON.parse(content);
1113
- } catch {
1114
- return null;
1115
- }
1086
+ var MARKER_START = "<!-- ai-annotator:start -->";
1087
+ var MARKER_END = "<!-- ai-annotator:end -->";
1088
+ function generateSkillBody(serverUrl) {
1089
+ return `AI Annotator provides access to the user's live browser session. Users select UI elements and add feedback comments. Use the REST API to read feedback, capture screenshots, inject CSS/JS, and read console logs.
1090
+
1091
+ Server: \`${serverUrl}\`
1092
+
1093
+ ## REST API
1094
+
1095
+ All endpoints return JSON. Obtain session ID from \`GET /api/sessions\` first.
1096
+
1097
+ | Method | Endpoint | Body/Query | Description |
1098
+ |--------|----------|------------|-------------|
1099
+ | \`GET\` | \`${serverUrl}/api/sessions\` | \u2014 | List connected browser sessions |
1100
+ | \`GET\` | \`${serverUrl}/api/sessions/:id/page-context\` | \u2014 | Page URL, title, selection count |
1101
+ | \`POST\` | \`${serverUrl}/api/sessions/:id/select\` | \`{mode?, selector?, selectorType?}\` | Trigger feedback selection |
1102
+ | \`GET\` | \`${serverUrl}/api/sessions/:id/feedback\` | \`?fields=xpath,attributes,styles,children\` | Get selected feedback items |
1103
+ | \`DELETE\` | \`${serverUrl}/api/sessions/:id/feedback\` | \u2014 | Clear all selections |
1104
+ | \`POST\` | \`${serverUrl}/api/sessions/:id/screenshot\` | \`{type?, selector?, quality?}\` | Capture screenshot |
1105
+ | \`POST\` | \`${serverUrl}/api/sessions/:id/inject-css\` | \`{css}\` | Inject CSS into page |
1106
+ | \`POST\` | \`${serverUrl}/api/sessions/:id/inject-js\` | \`{code}\` | Execute JS in page context |
1107
+ | \`GET\` | \`${serverUrl}/api/sessions/:id/console\` | \`?clear=true\` | Get captured console logs |
1108
+
1109
+ ## Workflow
1110
+
1111
+ 1. \`GET ${serverUrl}/api/sessions\` \u2192 get session ID
1112
+ 2. \`GET ${serverUrl}/api/sessions/{id}/feedback\` \u2192 read user feedback
1113
+ 3. Make code changes based on feedback
1114
+ 4. \`DELETE ${serverUrl}/api/sessions/{id}/feedback\` \u2192 clear feedback after addressing it`;
1116
1115
  }
1117
- function writeJsonFile(filePath, data) {
1116
+ function writeIfChanged(filePath, content, verbose) {
1118
1117
  const dir = dirname(filePath);
1119
1118
  if (!existsSync(dir)) {
1120
1119
  mkdirSync(dir, { recursive: true });
1121
1120
  }
1122
- writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
1123
- }
1124
- function setupConfigFile(filePath, serverConfig, verbose) {
1125
- const existingConfig = readJsonFile(filePath);
1126
- if (existingConfig) {
1127
- const existing = existingConfig.mcpServers?.[MCP_SERVER_NAME];
1128
- if (existing && existing.command === serverConfig.command && JSON.stringify(existing.args) === JSON.stringify(serverConfig.args)) {
1129
- if (verbose) {
1130
- console.log(`[ai-annotator] ${filePath} already configured`);
1131
- }
1121
+ if (existsSync(filePath)) {
1122
+ const existing = readFileSync(filePath, "utf-8");
1123
+ if (existing === content) {
1124
+ if (verbose) console.log(`[ai-annotator] ${filePath} already up-to-date`);
1132
1125
  return false;
1133
1126
  }
1134
- existingConfig.mcpServers = existingConfig.mcpServers || {};
1135
- existingConfig.mcpServers[MCP_SERVER_NAME] = serverConfig;
1136
- writeJsonFile(filePath, existingConfig);
1137
- if (verbose) {
1138
- console.log(`[ai-annotator] Updated ${filePath}`);
1127
+ }
1128
+ writeFileSync(filePath, content);
1129
+ if (verbose) console.log(`[ai-annotator] Updated ${filePath}`);
1130
+ return true;
1131
+ }
1132
+ function updateMarkerSection(filePath, content, verbose) {
1133
+ const section = `${MARKER_START}
1134
+ ${content}
1135
+ ${MARKER_END}`;
1136
+ if (existsSync(filePath)) {
1137
+ const existing = readFileSync(filePath, "utf-8");
1138
+ const startIdx = existing.indexOf(MARKER_START);
1139
+ const endIdx = existing.indexOf(MARKER_END);
1140
+ if (startIdx !== -1 && endIdx !== -1) {
1141
+ const updated = existing.slice(0, startIdx) + section + existing.slice(endIdx + MARKER_END.length);
1142
+ if (updated === existing) {
1143
+ if (verbose) console.log(`[ai-annotator] ${filePath} already up-to-date`);
1144
+ return false;
1145
+ }
1146
+ writeFileSync(filePath, updated);
1147
+ if (verbose) console.log(`[ai-annotator] Updated ${filePath}`);
1148
+ return true;
1139
1149
  }
1150
+ const separator = existing.endsWith("\n") ? "\n" : "\n\n";
1151
+ writeFileSync(filePath, existing + separator + section + "\n");
1152
+ if (verbose) console.log(`[ai-annotator] Appended to ${filePath}`);
1140
1153
  return true;
1141
1154
  }
1142
- const newConfig = {
1143
- mcpServers: {
1144
- [MCP_SERVER_NAME]: serverConfig
1145
- }
1146
- };
1147
- writeJsonFile(filePath, newConfig);
1148
- if (verbose) {
1149
- console.log(`[ai-annotator] Created ${filePath}`);
1155
+ const dir = dirname(filePath);
1156
+ if (!existsSync(dir)) {
1157
+ mkdirSync(dir, { recursive: true });
1150
1158
  }
1159
+ writeFileSync(filePath, section + "\n");
1160
+ if (verbose) console.log(`[ai-annotator] Created ${filePath}`);
1151
1161
  return true;
1152
1162
  }
1153
- function detectExistingConfigs(projectRoot) {
1154
- const potentialConfigs = [
1155
- join(projectRoot, ".mcp.json"),
1156
- join(projectRoot, ".cursor", "mcp.json"),
1157
- join(projectRoot, ".vscode", "mcp.json")
1158
- ];
1159
- return potentialConfigs.filter(existsSync);
1163
+ function track(result, filePath, updated) {
1164
+ if (updated) {
1165
+ result.updated.push(filePath);
1166
+ } else {
1167
+ result.alreadyConfigured.push(filePath);
1168
+ }
1160
1169
  }
1161
- function autoSetupMcp(options) {
1170
+ function autoSetupSkills(options) {
1162
1171
  const { projectRoot, serverUrl, verbose = false } = options;
1163
- const result = {
1164
- updated: [],
1165
- alreadyConfigured: []
1166
- };
1167
- const packageManager = detectPackageManager(projectRoot);
1168
- const serverConfig = buildMcpServerConfig(packageManager, serverUrl);
1169
- if (verbose) {
1170
- console.log(`[ai-annotator] Detected package manager: ${packageManager}`);
1171
- }
1172
- const existingConfigs = detectExistingConfigs(projectRoot);
1173
- if (existingConfigs.length === 0) {
1174
- const defaultConfigPath = join(projectRoot, ".mcp.json");
1175
- const wasUpdated = setupConfigFile(defaultConfigPath, serverConfig, verbose);
1176
- if (wasUpdated) {
1177
- result.updated.push(defaultConfigPath);
1178
- }
1179
- return result;
1180
- }
1181
- if (verbose) {
1182
- console.log(`[ai-annotator] Found ${existingConfigs.length} MCP config file(s)`);
1172
+ const result = { updated: [], alreadyConfigured: [] };
1173
+ const body = generateSkillBody(serverUrl);
1174
+ const claudeSkill = join(projectRoot, ".claude", "skills", "ai-annotator", "SKILL.md");
1175
+ track(result, claudeSkill, writeIfChanged(claudeSkill, `---
1176
+ name: ai-annotator
1177
+ description: This skill should be used when the user asks to "check browser feedback", "get user feedback", "capture screenshot", "inspect element", "inject CSS", "inject JS", "read console logs", or mentions AI Annotator, browser session, or UI feedback.
1178
+ ---
1179
+
1180
+ ${body}
1181
+ `, verbose));
1182
+ if (existsSync(join(projectRoot, ".cursor"))) {
1183
+ const cursorRule = join(projectRoot, ".cursor", "rules", "ai-annotator.mdc");
1184
+ track(result, cursorRule, writeIfChanged(cursorRule, `---
1185
+ description: AI Annotator - interact with user's live browser session for UI feedback
1186
+ globs:
1187
+ alwaysApply: true
1188
+ ---
1189
+
1190
+ ${body}
1191
+ `, verbose));
1192
+ }
1193
+ if (existsSync(join(projectRoot, ".windsurf"))) {
1194
+ const windsurfRule = join(projectRoot, ".windsurf", "rules", "ai-annotator.md");
1195
+ track(result, windsurfRule, writeIfChanged(windsurfRule, `---
1196
+ trigger: always_on
1197
+ ---
1198
+
1199
+ ${body}
1200
+ `, verbose));
1201
+ }
1202
+ const agentsMd = join(projectRoot, "AGENTS.md");
1203
+ if (existsSync(agentsMd)) {
1204
+ track(result, agentsMd, updateMarkerSection(agentsMd, body, verbose));
1205
+ }
1206
+ if (existsSync(join(projectRoot, ".github"))) {
1207
+ const copilotInstructions = join(projectRoot, ".github", "instructions", "ai-annotator.instructions.md");
1208
+ track(result, copilotInstructions, writeIfChanged(copilotInstructions, `---
1209
+ applyTo: "**"
1210
+ ---
1211
+
1212
+ ${body}
1213
+ `, verbose));
1183
1214
  }
1184
- for (const configFile of existingConfigs) {
1185
- const wasUpdated = setupConfigFile(configFile, serverConfig, verbose);
1186
- if (wasUpdated) {
1187
- result.updated.push(configFile);
1188
- } else {
1189
- result.alreadyConfigured.push(configFile);
1190
- }
1215
+ if (existsSync(join(projectRoot, ".clinerules"))) {
1216
+ const clineRule = join(projectRoot, ".clinerules", "ai-annotator.md");
1217
+ track(result, clineRule, writeIfChanged(clineRule, `${body}
1218
+ `, verbose));
1191
1219
  }
1192
1220
  return result;
1193
1221
  }
@@ -1276,7 +1304,7 @@ var AiAnnotatorServer = class {
1276
1304
  publicAddress: options.publicAddress ?? `http://${listenAddress}:${port}`,
1277
1305
  verbose: options.verbose ?? false,
1278
1306
  injectSourceLoc: options.injectSourceLoc ?? true,
1279
- autoSetupMcp: options.autoSetupMcp ?? false
1307
+ autoSetupSkills: options.autoSetupSkills ?? true
1280
1308
  };
1281
1309
  const currentFileDir = dirname2(fileURLToPath(import.meta.url));
1282
1310
  this.isRunningFromSource = currentFileDir.endsWith("/src") || currentFileDir.endsWith("\\src");
@@ -1323,9 +1351,6 @@ var AiAnnotatorServer = class {
1323
1351
  if (this.options.verbose) {
1324
1352
  args.push("--verbose");
1325
1353
  }
1326
- if (this.options.autoSetupMcp) {
1327
- args.push("--skip-mcp-instructions");
1328
- }
1329
1354
  this.log(`Starting annotator server: ${cmd} ${args.join(" ")}`);
1330
1355
  this.log(`Working directory: ${this.packageDir}`);
1331
1356
  this.serverProcess = spawn(cmd, args, {
@@ -1418,17 +1443,15 @@ function aiAnnotator(options = {}) {
1418
1443
  configResolved(config) {
1419
1444
  serverManager = new AiAnnotatorServer(options);
1420
1445
  root = config.root;
1421
- if (options.autoSetupMcp) {
1422
- const serverUrl = `http://${options.listenAddress ?? "127.0.0.1"}:${options.port ?? 7318}`;
1423
- const result = autoSetupMcp({
1446
+ if (options.autoSetupSkills !== false) {
1447
+ const serverUrl = options.publicAddress ?? `http://${options.listenAddress ?? "127.0.0.1"}:${options.port ?? 7318}`;
1448
+ const skillsResult = autoSetupSkills({
1424
1449
  projectRoot: root,
1425
1450
  serverUrl,
1426
1451
  verbose: options.verbose
1427
1452
  });
1428
- if (result.updated.length > 0) {
1429
- console.log(`[ai-annotator] \u2705 MCP config updated: ${result.updated.map((f) => f.replace(root + "/", "")).join(", ")}`);
1430
- } else if (result.alreadyConfigured.length > 0) {
1431
- console.log(`[ai-annotator] \u2705 MCP config already up-to-date`);
1453
+ if (skillsResult.updated.length > 0) {
1454
+ console.log(`[ai-annotator] \u2705 AI skills updated: ${skillsResult.updated.map((f) => f.replace(root + "/", "")).join(", ")}`);
1432
1455
  }
1433
1456
  }
1434
1457
  },
@@ -1513,8 +1536,7 @@ var nuxt_module_default = defineNuxtModule({
1513
1536
  port: 7318,
1514
1537
  listenAddress: "127.0.0.1",
1515
1538
  verbose: false,
1516
- injectSourceLoc: true,
1517
- autoSetupMcp: false
1539
+ injectSourceLoc: true
1518
1540
  },
1519
1541
  setup(options, nuxt) {
1520
1542
  if (!nuxt.options.dev) {