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