@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.
- package/dist/index.js +119 -57
- 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
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
215
|
-
|
|
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
|
|
306
|
+
log(`Would add inspector import to ${path.relative(cwd, entryFile)}`);
|
|
247
307
|
}
|
|
248
308
|
else {
|
|
249
|
-
writeFile(
|
|
250
|
-
log(`
|
|
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
|
|
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':
|