@eyeglass/cli 0.1.1 → 0.1.3

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.
Files changed (2) hide show
  1. package/dist/index.js +119 -57
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,20 +5,6 @@ import { execFileSync } from 'child_process';
5
5
  // ============================================================================
6
6
  // Templates
7
7
  // ============================================================================
8
- const VITE_PLUGIN = `// Eyeglass Vite Plugin - Auto-generated by eyeglass init
9
- export function eyeglassPlugin() {
10
- return {
11
- name: 'eyeglass',
12
- transformIndexHtml(html) {
13
- if (process.env.NODE_ENV === 'production') return html;
14
- return html.replace(
15
- '</body>',
16
- '<script type="module">import "@eyeglass/inspector";</script></body>'
17
- );
18
- },
19
- };
20
- }
21
- `;
22
8
  const NEXT_CONFIG_ADDITION = `
23
9
  // Eyeglass configuration - Auto-generated by eyeglass init
24
10
  const withEyeglass = (nextConfig) => {
@@ -48,6 +34,71 @@ const CLAUDE_CONFIG = {
48
34
  },
49
35
  },
50
36
  };
37
+ const EYEGLASS_SKILL = `---
38
+ name: eyeglass
39
+ description: Listen for Eyeglass requests from the browser and make UI changes
40
+ user_invocable: true
41
+ ---
42
+
43
+ # Eyeglass Skill
44
+
45
+ Eyeglass lets users select UI elements in their browser and request changes. When invoked, enter a listening loop to handle requests.
46
+
47
+ ## How It Works
48
+
49
+ 1. User selects an element in their browser using the Eyeglass inspector
50
+ 2. User types a request (e.g., "make this blue", "add a button here")
51
+ 3. You receive the element context and make the requested changes
52
+ 4. User can send follow-up requests on the same element
53
+
54
+ ## Listening Loop
55
+
56
+ When this skill is invoked, enter a continuous listening mode:
57
+
58
+ \`\`\`
59
+ while listening:
60
+ 1. Call wait_for_request() - blocks until user selects an element
61
+ 2. Call update_status("fixing", "Working on it...") to show progress
62
+ 3. Use report_action() to show what files you're reading/writing
63
+ 4. Make the requested code changes
64
+ 5. Call update_status("success", "Done!") when complete
65
+ 6. IMMEDIATELY call wait_for_request() again to continue listening
66
+ \`\`\`
67
+
68
+ ## Available MCP Tools
69
+
70
+ - **wait_for_request()** - Block until user selects an element. Returns element context.
71
+ - **update_status(status, message)** - Update the browser UI. Status: "fixing" | "success" | "failed"
72
+ - **report_action(action, target)** - Show progress. Action: "reading" | "writing" | "searching"
73
+ - **send_thought(content)** - Share your reasoning with the user
74
+ - **ask_question(question, options)** - Ask the user a multiple choice question
75
+
76
+ ## Example Flow
77
+
78
+ \`\`\`
79
+ User invokes: /eyeglass
80
+
81
+ You: [Call wait_for_request()]
82
+ → Returns: User selected <Button /> and said "make this red"
83
+
84
+ You: [Call update_status("fixing", "Adding red variant to Button...")]
85
+ [Call report_action("reading", "src/components/Button.tsx")]
86
+ [Read the file, make changes]
87
+ [Call report_action("writing", "src/components/Button.tsx")]
88
+ [Write the changes]
89
+ [Call update_status("success", "Button is now red!")]
90
+
91
+ You: [Call wait_for_request()] ← IMPORTANT: Go back to listening!
92
+ → Waiting for next request...
93
+ \`\`\`
94
+
95
+ ## Key Points
96
+
97
+ - **Always loop back** - After completing a request, immediately call wait_for_request() again
98
+ - **Show progress** - Use update_status and report_action so the user sees what you're doing
99
+ - **Stay in the browser** - The user doesn't need to come back to the terminal
100
+ - **Handle follow-ups** - Users can send multiple requests on the same element
101
+ `;
51
102
  // ============================================================================
52
103
  // Utilities
53
104
  // ============================================================================
@@ -151,6 +202,26 @@ function detectProject() {
151
202
  // ============================================================================
152
203
  // Config Modifiers
153
204
  // ============================================================================
205
+ function setupEyeglassSkill(dryRun) {
206
+ const cwd = process.cwd();
207
+ const skillsDir = path.join(cwd, '.claude', 'skills');
208
+ const skillPath = path.join(skillsDir, 'eyeglass.md');
209
+ if (fileExists(skillPath)) {
210
+ log('Eyeglass skill already exists', 'success');
211
+ return true;
212
+ }
213
+ if (dryRun) {
214
+ log(`Would create ${path.relative(cwd, skillPath)}`);
215
+ }
216
+ else {
217
+ if (!fileExists(skillsDir)) {
218
+ fs.mkdirSync(skillsDir, { recursive: true });
219
+ }
220
+ writeFile(skillPath, EYEGLASS_SKILL);
221
+ log('Created .claude/skills/eyeglass.md', 'success');
222
+ }
223
+ return true;
224
+ }
154
225
  function setupClaudeConfig(dryRun) {
155
226
  const cwd = process.cwd();
156
227
  const claudeDir = path.join(cwd, '.claude');
@@ -199,55 +270,44 @@ function setupClaudeConfig(dryRun) {
199
270
  }
200
271
  function setupVite(configFile, dryRun) {
201
272
  const cwd = process.cwd();
202
- const isTs = configFile.endsWith('.ts');
203
- const pluginFile = path.join(cwd, isTs ? 'eyeglass.plugin.ts' : 'eyeglass.plugin.js');
204
- // Create plugin file
205
- if (!fileExists(pluginFile)) {
206
- if (dryRun) {
207
- log(`Would create ${path.basename(pluginFile)}`);
208
- }
209
- else {
210
- writeFile(pluginFile, VITE_PLUGIN);
211
- log(`Created ${path.basename(pluginFile)}`, 'success');
273
+ // Find entry file (main.tsx, main.ts, main.jsx, main.js, src/main.*, etc.)
274
+ const entryFiles = [
275
+ 'src/main.tsx',
276
+ 'src/main.ts',
277
+ 'src/main.jsx',
278
+ 'src/main.js',
279
+ 'main.tsx',
280
+ 'main.ts',
281
+ 'main.jsx',
282
+ 'main.js',
283
+ ];
284
+ let entryFile = null;
285
+ for (const entry of entryFiles) {
286
+ const fullPath = path.join(cwd, entry);
287
+ if (fileExists(fullPath)) {
288
+ entryFile = fullPath;
289
+ break;
212
290
  }
213
291
  }
214
- // Modify vite.config
215
- const config = readFile(configFile);
216
- // Check if already configured
217
- if (config.includes('eyeglassPlugin')) {
218
- log('Vite config already has eyeglass plugin', 'success');
219
- return true;
220
- }
221
- // Add import
222
- const importStatement = `import { eyeglassPlugin } from './eyeglass.plugin';\n`;
223
- let newConfig = config;
224
- // Find a good place to add the import (after other imports or at the top)
225
- const lastImportMatch = config.match(/^import .+$/gm);
226
- if (lastImportMatch) {
227
- const lastImport = lastImportMatch[lastImportMatch.length - 1];
228
- const lastImportIndex = config.lastIndexOf(lastImport) + lastImport.length;
229
- newConfig = config.slice(0, lastImportIndex) + '\n' + importStatement + config.slice(lastImportIndex);
230
- }
231
- else {
232
- newConfig = importStatement + config;
233
- }
234
- // Add to plugins array
235
- // This handles: plugins: [...] or plugins: [react(), ...]
236
- const pluginsMatch = newConfig.match(/plugins\s*:\s*\[/);
237
- if (pluginsMatch) {
238
- const insertPos = newConfig.indexOf(pluginsMatch[0]) + pluginsMatch[0].length;
239
- newConfig = newConfig.slice(0, insertPos) + 'eyeglassPlugin(), ' + newConfig.slice(insertPos);
240
- }
241
- else {
242
- log('Could not find plugins array in vite config - please add eyeglassPlugin() manually', 'warn');
292
+ if (!entryFile) {
293
+ log('Could not find entry file - please import @eyeglass/inspector manually in your main file', 'warn');
243
294
  return false;
244
295
  }
296
+ const content = readFile(entryFile);
297
+ // Check if already imported
298
+ if (content.includes('@eyeglass/inspector')) {
299
+ log('Inspector already imported', 'success');
300
+ return true;
301
+ }
302
+ // Add import at the top
303
+ const importStatement = `import '@eyeglass/inspector';\n`;
304
+ const newContent = importStatement + content;
245
305
  if (dryRun) {
246
- log(`Would modify ${path.basename(configFile)} to add eyeglassPlugin()`);
306
+ log(`Would add inspector import to ${path.relative(cwd, entryFile)}`);
247
307
  }
248
308
  else {
249
- writeFile(configFile, newConfig);
250
- log(`Updated ${path.basename(configFile)} with eyeglass plugin`, 'success');
309
+ writeFile(entryFile, newContent);
310
+ log(`Added inspector import to ${path.relative(cwd, entryFile)}`, 'success');
251
311
  }
252
312
  return true;
253
313
  }
@@ -368,7 +428,9 @@ function init(options) {
368
428
  }
369
429
  // Step 2: Setup Claude Code MCP config
370
430
  setupClaudeConfig(dryRun);
371
- // Step 3: Setup project-specific integration
431
+ // Step 3: Setup Eyeglass skill for Claude
432
+ setupEyeglassSkill(dryRun);
433
+ // Step 4: Setup project-specific integration
372
434
  console.log('');
373
435
  switch (project.type) {
374
436
  case 'vite':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eyeglass/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Visual debugging for AI coding agents - CLI",
5
5
  "type": "module",
6
6
  "bin": {