myaidev-method 0.3.3 → 0.3.5
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/.claude-plugin/plugin.json +0 -1
- package/.env.example +5 -4
- package/CHANGELOG.md +2 -2
- package/CONTENT_CREATION_GUIDE.md +489 -3211
- package/DEVELOPER_USE_CASES.md +1 -1
- package/MODULAR_INSTALLATION.md +2 -2
- package/README.md +39 -33
- package/TECHNICAL_ARCHITECTURE.md +1 -1
- package/USER_GUIDE.md +242 -190
- package/agents/content-editor-agent.md +90 -0
- package/agents/content-planner-agent.md +97 -0
- package/agents/content-research-agent.md +62 -0
- package/agents/content-seo-agent.md +101 -0
- package/agents/content-writer-agent.md +69 -0
- package/agents/infographic-analyzer-agent.md +63 -0
- package/agents/infographic-designer-agent.md +72 -0
- package/bin/cli.js +777 -535
- package/{content-rules.example.md → content-rules-example.md} +2 -2
- package/dist/mcp/health-check.js +82 -68
- package/dist/mcp/mcp-config.json +8 -0
- package/dist/mcp/openstack-server.js +1746 -1262
- package/dist/server/.tsbuildinfo +1 -1
- package/extension.json +21 -4
- package/package.json +181 -184
- package/skills/company-config/SKILL.md +133 -0
- package/skills/configure/SKILL.md +1 -1
- package/skills/myai-configurator/SKILL.md +77 -0
- package/skills/myai-configurator/content-creation-configurator/SKILL.md +516 -0
- package/skills/myai-configurator/content-maintenance-configurator/SKILL.md +397 -0
- package/skills/myai-content-enrichment/SKILL.md +114 -0
- package/skills/myai-content-ideation/SKILL.md +288 -0
- package/skills/myai-content-ideation/evals/evals.json +182 -0
- package/skills/myai-content-production-coordinator/SKILL.md +946 -0
- package/skills/{content-rules-setup → myai-content-rules-setup}/SKILL.md +1 -1
- package/skills/{content-verifier → myai-content-verifier}/SKILL.md +1 -1
- package/skills/myai-content-writer/SKILL.md +333 -0
- package/skills/myai-content-writer/agents/editor-agent.md +138 -0
- package/skills/myai-content-writer/agents/planner-agent.md +121 -0
- package/skills/myai-content-writer/agents/research-agent.md +83 -0
- package/skills/myai-content-writer/agents/seo-agent.md +139 -0
- package/skills/myai-content-writer/agents/visual-planner-agent.md +110 -0
- package/skills/myai-content-writer/agents/writer-agent.md +85 -0
- package/skills/{infographic → myai-infographic}/SKILL.md +1 -1
- package/skills/myai-proprietary-content-verifier/SKILL.md +175 -0
- package/skills/myai-proprietary-content-verifier/evals/evals.json +36 -0
- package/skills/myai-skill-builder/SKILL.md +699 -0
- package/skills/myai-skill-builder/agents/analyzer-agent.md +137 -0
- package/skills/myai-skill-builder/agents/comparator-agent.md +77 -0
- package/skills/myai-skill-builder/agents/grader-agent.md +103 -0
- package/skills/myai-skill-builder/assets/eval_review.html +131 -0
- package/skills/myai-skill-builder/references/schemas.md +211 -0
- package/skills/myai-skill-builder/scripts/aggregate_benchmark.py +190 -0
- package/skills/myai-skill-builder/scripts/generate_review.py +381 -0
- package/skills/myai-skill-builder/scripts/package_skill.py +91 -0
- package/skills/myai-skill-builder/scripts/run_eval.py +105 -0
- package/skills/myai-skill-builder/scripts/run_loop.py +211 -0
- package/skills/myai-skill-builder/scripts/utils.py +123 -0
- package/skills/myai-visual-generator/SKILL.md +125 -0
- package/skills/myai-visual-generator/evals/evals.json +155 -0
- package/skills/myai-visual-generator/references/infographic-pipeline.md +73 -0
- package/skills/myai-visual-generator/references/research-visuals.md +57 -0
- package/skills/myai-visual-generator/references/services.md +89 -0
- package/skills/myai-visual-generator/scripts/visual-generation-utils.js +1272 -0
- package/skills/myaidev-analyze/agents/dependency-mapper-agent.md +236 -0
- package/skills/myaidev-analyze/agents/pattern-detector-agent.md +240 -0
- package/skills/myaidev-analyze/agents/structure-scanner-agent.md +171 -0
- package/skills/myaidev-analyze/agents/tech-profiler-agent.md +291 -0
- package/skills/myaidev-architect/agents/compliance-checker-agent.md +287 -0
- package/skills/myaidev-architect/agents/requirements-analyst-agent.md +194 -0
- package/skills/myaidev-architect/agents/system-designer-agent.md +315 -0
- package/skills/myaidev-coder/agents/implementer-agent.md +185 -0
- package/skills/myaidev-coder/agents/integration-agent.md +168 -0
- package/skills/myaidev-coder/agents/pattern-scanner-agent.md +161 -0
- package/skills/myaidev-coder/agents/self-reviewer-agent.md +168 -0
- package/skills/myaidev-debug/agents/fix-agent-debug.md +317 -0
- package/skills/myaidev-debug/agents/hypothesis-agent.md +226 -0
- package/skills/myaidev-debug/agents/investigator-agent.md +250 -0
- package/skills/myaidev-debug/agents/symptom-collector-agent.md +231 -0
- package/skills/myaidev-documenter/agents/code-reader-agent.md +172 -0
- package/skills/myaidev-documenter/agents/doc-validator-agent.md +174 -0
- package/skills/myaidev-documenter/agents/doc-writer-agent.md +379 -0
- package/skills/myaidev-figma/SKILL.md +212 -0
- package/skills/myaidev-figma/capture.js +133 -0
- package/skills/myaidev-figma/crawl.js +130 -0
- package/skills/myaidev-figma-configure/SKILL.md +130 -0
- package/skills/myaidev-migrate/agents/migration-planner-agent.md +237 -0
- package/skills/myaidev-migrate/agents/migration-writer-agent.md +248 -0
- package/skills/myaidev-migrate/agents/schema-analyzer-agent.md +190 -0
- package/skills/myaidev-performance/agents/benchmark-agent.md +281 -0
- package/skills/myaidev-performance/agents/optimizer-agent.md +277 -0
- package/skills/myaidev-performance/agents/profiler-agent.md +252 -0
- package/skills/myaidev-refactor/agents/refactor-executor-agent.md +221 -0
- package/skills/myaidev-refactor/agents/refactor-planner-agent.md +213 -0
- package/skills/myaidev-refactor/agents/regression-guard-agent.md +242 -0
- package/skills/myaidev-refactor/agents/smell-detector-agent.md +233 -0
- package/skills/myaidev-reviewer/agents/auto-fixer-agent.md +238 -0
- package/skills/myaidev-reviewer/agents/code-analyst-agent.md +220 -0
- package/skills/myaidev-reviewer/agents/security-scanner-agent.md +262 -0
- package/skills/myaidev-tester/agents/coverage-analyst-agent.md +163 -0
- package/skills/myaidev-tester/agents/tdd-driver-agent.md +242 -0
- package/skills/myaidev-tester/agents/test-runner-agent.md +176 -0
- package/skills/myaidev-tester/agents/test-strategist-agent.md +154 -0
- package/skills/myaidev-tester/agents/test-writer-agent.md +242 -0
- package/skills/myaidev-workflow/agents/analyzer-agent.md +317 -0
- package/skills/myaidev-workflow/agents/coordinator-agent.md +253 -0
- package/skills/openstack-manager/SKILL.md +1 -1
- package/skills/payloadcms-publisher/SKILL.md +141 -77
- package/skills/payloadcms-publisher/references/field-mapping.md +142 -0
- package/skills/payloadcms-publisher/references/lexical-format.md +97 -0
- package/skills/security-auditor/SKILL.md +1 -1
- package/src/cli/commands/addon.js +184 -123
- package/src/config/workflows.js +172 -228
- package/src/lib/ascii-banner.js +197 -182
- package/src/lib/{content-coordinator.js → content-production-coordinator.js} +649 -459
- package/src/lib/installation-detector.js +93 -59
- package/src/lib/payloadcms-utils.js +285 -510
- package/src/lib/update-manager.js +120 -61
- package/src/lib/workflow-installer.js +55 -0
- package/src/mcp/health-check.js +82 -68
- package/src/mcp/openstack-server.js +1746 -1262
- package/src/scripts/configure-visual-apis.js +224 -173
- package/src/scripts/configure-wordpress-mcp.js +96 -66
- package/src/scripts/init/install.js +109 -85
- package/src/scripts/init-project.js +138 -67
- package/src/scripts/utils/write-content.js +67 -52
- package/src/scripts/wordpress/publish-to-wordpress.js +128 -128
- package/src/templates/claude/CLAUDE.md +131 -0
- package/hooks/hooks.json +0 -26
- package/skills/content-coordinator/SKILL.md +0 -130
- package/skills/content-enrichment/SKILL.md +0 -80
- package/skills/content-writer/SKILL.md +0 -285
- package/skills/visual-generator/SKILL.md +0 -140
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: myaidev-figma
|
|
3
|
+
description: Capture any website (all pages or selected sub-pages) and push the designs directly into a Figma file. Invoke this when the user wants to import, capture, screenshot, or convert a website into Figma designs.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, mcp__figma-remote-mcp__generate_figma_design
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# /myaidev-figma — Website → Figma Design Capture
|
|
9
|
+
|
|
10
|
+
You are now running the **myaidev-figma** skill. Your job is to capture a live website (homepage + sub-pages) and push every selected page into a Figma file as a pixel-faithful design. Follow every step precisely.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## STEP 0 — Greet & Collect Inputs
|
|
15
|
+
|
|
16
|
+
Tell the user:
|
|
17
|
+
> "**myaidev-figma** — Website to Figma Capture
|
|
18
|
+
> I'll crawl your site, let you pick which pages to capture, and push them all into Figma."
|
|
19
|
+
|
|
20
|
+
Then ask these three questions **together in one message**:
|
|
21
|
+
|
|
22
|
+
1. **Website URL** — what is the root URL to capture? (e.g. `https://example.com`)
|
|
23
|
+
2. **Figma destination** — should I:
|
|
24
|
+
- (a) Create a **new** Figma file — if so, what should it be named?
|
|
25
|
+
- (b) Add to an **existing** Figma file — if so, paste the Figma file URL or key.
|
|
26
|
+
3. **Crawl depth** — how deep should I go finding sub-pages? (`1` = direct links from homepage, `2` = two levels deep). Default is `1`.
|
|
27
|
+
|
|
28
|
+
Wait for the user's answers before continuing.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## STEP 1 — Ensure Playwright is Available
|
|
33
|
+
|
|
34
|
+
Run this shell check:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx playwright --version 2>/dev/null || echo "NOT_INSTALLED"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**If playwright is missing** (output contains `NOT_INSTALLED`):
|
|
41
|
+
- Tell the user: "Installing Playwright MCP server and browser dependencies — this may take a minute..."
|
|
42
|
+
- Run: `npm install -g @playwright/mcp playwright 2>&1 | tail -5`
|
|
43
|
+
- Then install the Chromium browser: `npx playwright install chromium 2>&1 | tail -10`
|
|
44
|
+
- After install, verify: `npx playwright --version`
|
|
45
|
+
|
|
46
|
+
**Also ensure the Playwright MCP server is registered with Claude:**
|
|
47
|
+
```bash
|
|
48
|
+
claude mcp get playwright 2>/dev/null || claude mcp add playwright -- npx @playwright/mcp 2>&1
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If the `mcp add` succeeded, tell the user:
|
|
52
|
+
> "Playwright MCP server registered. **Please restart Claude Code** once to activate MCP, then run `/myaidev-figma` again."
|
|
53
|
+
> Then **stop** — do not continue until the user restarts.
|
|
54
|
+
|
|
55
|
+
If playwright was already installed and the MCP was already registered, continue to STEP 2.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## STEP 2 — Find the Playwright + Chromium Paths
|
|
60
|
+
|
|
61
|
+
Run these to discover the environment:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Find playwright package
|
|
65
|
+
PLAYWRIGHT_PKG=$(find ~/.npm/_npx -name "playwright" -type d -maxdepth 8 2>/dev/null | sort -r | head -1)
|
|
66
|
+
echo "PLAYWRIGHT_PKG=$PLAYWRIGHT_PKG"
|
|
67
|
+
|
|
68
|
+
# Find Chromium executable (macOS)
|
|
69
|
+
CHROMIUM=$(find ~/Library/Caches/ms-playwright -name "chrome-headless-shell" -o -name "Chromium" -type f 2>/dev/null | grep -v ".pak" | sort -r | head -1)
|
|
70
|
+
# Try Linux if macOS yields nothing
|
|
71
|
+
if [ -z "$CHROMIUM" ]; then
|
|
72
|
+
CHROMIUM=$(find ~/.cache/ms-playwright -name "chrome" -type f 2>/dev/null | sort -r | head -1)
|
|
73
|
+
fi
|
|
74
|
+
echo "CHROMIUM=$CHROMIUM"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Save these values — you will use them in the Node.js scripts below. If `PLAYWRIGHT_PKG` is empty, the package may be installed globally; try:
|
|
78
|
+
```bash
|
|
79
|
+
PLAYWRIGHT_PKG=$(node -e "console.log(require.resolve('playwright').replace('/index.js',''))" 2>/dev/null || echo "")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## STEP 3 — Crawl the Website to Discover Pages
|
|
85
|
+
|
|
86
|
+
Use the crawl.js script located at `~/.claude/skills/myaidev-figma/crawl.js`.
|
|
87
|
+
|
|
88
|
+
Run it like this (replace paths with what you found in STEP 2):
|
|
89
|
+
```bash
|
|
90
|
+
NODE_PATH="<PLAYWRIGHT_PKG>/.." node ~/.claude/skills/myaidev-figma/crawl.js "<USER_URL>" <CRAWL_DEPTH> 2>/tmp/dev-f-crawl-err.log
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
If crawl.js fails (bad NODE_PATH, etc.), fall back to using `mcp__playwright__browser_navigate` to navigate to the URL and extract links from the page snapshot. Extract all `<a href>` links that share the same origin as the root URL.
|
|
94
|
+
|
|
95
|
+
Parse the JSON output. You now have a list of pages like:
|
|
96
|
+
```json
|
|
97
|
+
[
|
|
98
|
+
{ "url": "https://example.com/", "title": "Home", "depth": 0 },
|
|
99
|
+
{ "url": "https://example.com/pricing", "title": "Pricing", "depth": 1 },
|
|
100
|
+
...
|
|
101
|
+
]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Present the discovered pages to the user** in a numbered list:
|
|
105
|
+
```
|
|
106
|
+
Found N pages:
|
|
107
|
+
1. / — Home
|
|
108
|
+
2. /pricing — Pricing
|
|
109
|
+
3. /about — About Us
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
Which pages would you like to capture?
|
|
113
|
+
Enter numbers (e.g. "1 3 5"), "all", or "1-5" for a range.
|
|
114
|
+
(Tip: capturing many pages takes longer — start with a few to test)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Wait for the user's selection. Parse it into the final list of URLs to capture.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## STEP 4 — Set Up Figma Destination
|
|
122
|
+
|
|
123
|
+
Call `mcp__figma-remote-mcp__generate_figma_design` **without any parameters** first to get the current options/plans list.
|
|
124
|
+
|
|
125
|
+
Based on the user's answer from STEP 0:
|
|
126
|
+
|
|
127
|
+
**If new file:**
|
|
128
|
+
- Use `outputMode: "newFile"` with the user's chosen `fileName` and the appropriate `planKey` from the options list.
|
|
129
|
+
|
|
130
|
+
**If existing file:**
|
|
131
|
+
- Extract `fileKey` from the URL they pasted (format: `figma.com/design/<fileKey>/...`).
|
|
132
|
+
- Use `outputMode: "existingFile"` with that `fileKey`.
|
|
133
|
+
|
|
134
|
+
Generate **one capture ID per page** by calling `mcp__figma-remote-mcp__generate_figma_design` with the chosen outputMode once per page. Store each `captureId` and `endpoint` from the responses.
|
|
135
|
+
|
|
136
|
+
> Important: call generate_figma_design once per page to get separate captureIds. For `existingFile`, use the same `fileKey` for all calls.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## STEP 5 — Capture Each Page
|
|
141
|
+
|
|
142
|
+
For each page in the user's selection, run `capture.js`:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
NODE_PATH="<PLAYWRIGHT_PKG>/.." node ~/.claude/skills/myaidev-figma/capture.js \
|
|
146
|
+
"<PAGE_URL>" \
|
|
147
|
+
"<CAPTURE_ID>" \
|
|
148
|
+
"<ENDPOINT_URL>" \
|
|
149
|
+
"body" \
|
|
150
|
+
2>/tmp/dev-f-capture-<slug>.log
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Run captures sequentially** (one at a time) to avoid overloading the browser.
|
|
154
|
+
|
|
155
|
+
After each capture starts, immediately begin polling:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Poll loop (up to 10 times, every 5 seconds):
|
|
159
|
+
Call mcp__figma-remote-mcp__generate_figma_design with captureId: "<CAPTURE_ID>"
|
|
160
|
+
If status == "completed" → move to next page
|
|
161
|
+
If status == "pending" or "processing" → wait 5s and retry
|
|
162
|
+
If still pending after 10 polls → log error and move on
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Show the user a live status line per page:
|
|
166
|
+
```
|
|
167
|
+
[1/5] Capturing https://example.com/ ... ✓ Done
|
|
168
|
+
[2/5] Capturing https://example.com/pricing ... ⏳ Processing...
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## STEP 6 — Report Results
|
|
174
|
+
|
|
175
|
+
Once all captures are done, tell the user:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
✓ myaidev-figma complete! X of Y pages captured.
|
|
179
|
+
|
|
180
|
+
Figma file: <FIGMA_FILE_URL>
|
|
181
|
+
|
|
182
|
+
Pages captured:
|
|
183
|
+
✓ / — Home
|
|
184
|
+
✓ /pricing — Pricing
|
|
185
|
+
✗ /blog — Failed (timeout)
|
|
186
|
+
|
|
187
|
+
To capture more pages or re-capture a page, just run /myaidev-figma again.
|
|
188
|
+
The Figma capture toolbar (visible in the browser) also lets you manually re-capture pages at any time.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Open the Figma file:
|
|
192
|
+
```bash
|
|
193
|
+
open "<FIGMA_FILE_URL>"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Error Handling
|
|
199
|
+
|
|
200
|
+
- **Capture returns `success: false`**: The captureId may have expired. Generate a new one and retry that page once.
|
|
201
|
+
- **Node.js script not found**: Use the absolute path `~/.claude/skills/myaidev-figma/capture.js`.
|
|
202
|
+
- **`window.figma` not defined after injection**: The CSP stripping may have failed. Try navigating to the page with hash params approach or report the issue to the user.
|
|
203
|
+
- **Browser not found**: Run `npx playwright install chromium` and retry.
|
|
204
|
+
- **Rate limits or network errors**: Wait 10 seconds and retry up to 3 times.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Notes
|
|
209
|
+
|
|
210
|
+
- The `$ARGUMENTS` variable contains anything the user typed after `/myaidev-figma`. If they passed a URL directly (e.g. `/myaidev-figma https://example.com`), skip asking for the URL in STEP 0 and use `$ARGUMENTS` as the URL.
|
|
211
|
+
- Always be transparent about what you're doing — show the Bash commands you run and their outputs.
|
|
212
|
+
- If the user already has Playwright MCP tools available (`mcp__playwright__browser_navigate` etc.), prefer using those for navigation in STEP 3 instead of the Node.js crawl script.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* dev-f capture script
|
|
4
|
+
* Usage: node capture.js <url> <captureId> <endpoint> [selector]
|
|
5
|
+
* Captures a webpage and submits it to the Figma MCP capture endpoint.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { chromium } = require('playwright');
|
|
9
|
+
|
|
10
|
+
const [,, targetUrl, captureId, endpoint, selector = 'body'] = process.argv;
|
|
11
|
+
|
|
12
|
+
if (!targetUrl || !captureId || !endpoint) {
|
|
13
|
+
console.error('Usage: node capture.js <url> <captureId> <endpoint> [selector]');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Find an available Chromium executable
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
|
|
22
|
+
function findChromium() {
|
|
23
|
+
const base = path.join(os.homedir(), 'Library', 'Caches', 'ms-playwright');
|
|
24
|
+
if (!fs.existsSync(base)) return null;
|
|
25
|
+
|
|
26
|
+
const dirs = fs.readdirSync(base)
|
|
27
|
+
.filter(d => d.startsWith('chromium'))
|
|
28
|
+
.sort()
|
|
29
|
+
.reverse(); // prefer newer
|
|
30
|
+
|
|
31
|
+
for (const dir of dirs) {
|
|
32
|
+
// arm64
|
|
33
|
+
const arm = path.join(base, dir, 'chrome-mac-arm64', 'Chromium.app', 'Contents', 'MacOS', 'Chromium');
|
|
34
|
+
if (fs.existsSync(arm)) return arm;
|
|
35
|
+
// headless shell arm64
|
|
36
|
+
const shell_arm = path.join(base, dir, 'chrome-headless-shell-mac-arm64', 'chrome-headless-shell');
|
|
37
|
+
if (fs.existsSync(shell_arm)) return shell_arm;
|
|
38
|
+
// x64
|
|
39
|
+
const x64 = path.join(base, dir, 'chrome-mac-x64', 'Chromium.app', 'Contents', 'MacOS', 'Chromium');
|
|
40
|
+
if (fs.existsSync(x64)) return x64;
|
|
41
|
+
const shell_x64 = path.join(base, dir, 'chrome-headless-shell-mac-x64', 'chrome-headless-shell');
|
|
42
|
+
if (fs.existsSync(shell_x64)) return shell_x64;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Linux paths
|
|
46
|
+
const linuxBase = path.join(os.homedir(), '.cache', 'ms-playwright');
|
|
47
|
+
if (fs.existsSync(linuxBase)) {
|
|
48
|
+
const ldirs = fs.readdirSync(linuxBase).filter(d => d.startsWith('chromium')).sort().reverse();
|
|
49
|
+
for (const dir of ldirs) {
|
|
50
|
+
const lp = path.join(linuxBase, dir, 'chrome-linux', 'chrome');
|
|
51
|
+
if (fs.existsSync(lp)) return lp;
|
|
52
|
+
const ls = path.join(linuxBase, dir, 'chrome-linux', 'chrome-headless-shell');
|
|
53
|
+
if (fs.existsSync(ls)) return ls;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
(async () => {
|
|
61
|
+
const executablePath = findChromium();
|
|
62
|
+
|
|
63
|
+
const launchOptions = { headless: true };
|
|
64
|
+
if (executablePath) {
|
|
65
|
+
launchOptions.executablePath = executablePath;
|
|
66
|
+
process.stderr.write(`Using Chromium: ${executablePath}\n`);
|
|
67
|
+
} else {
|
|
68
|
+
process.stderr.write('No cached Chromium found, using Playwright default (may need install)\n');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const browser = await chromium.launch(launchOptions);
|
|
72
|
+
const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
|
|
73
|
+
const page = await context.newPage();
|
|
74
|
+
|
|
75
|
+
// Strip CSP headers so the injected capture script can run
|
|
76
|
+
await page.route('**/*', async (route) => {
|
|
77
|
+
try {
|
|
78
|
+
const response = await route.fetch();
|
|
79
|
+
const headers = { ...response.headers() };
|
|
80
|
+
delete headers['content-security-policy'];
|
|
81
|
+
delete headers['content-security-policy-report-only'];
|
|
82
|
+
await route.fulfill({ response, headers });
|
|
83
|
+
} catch (e) {
|
|
84
|
+
await route.continue();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
process.stderr.write(`Navigating to: ${targetUrl}\n`);
|
|
89
|
+
await page.goto(targetUrl, { waitUntil: 'load', timeout: 60000 });
|
|
90
|
+
await page.waitForTimeout(2000);
|
|
91
|
+
process.stderr.write(`Title: ${await page.title()}\n`);
|
|
92
|
+
|
|
93
|
+
// Fetch and inject the Figma capture script
|
|
94
|
+
const r = await context.request.get('https://mcp.figma.com/mcp/html-to-design/capture.js');
|
|
95
|
+
const scriptText = await r.text();
|
|
96
|
+
process.stderr.write(`Capture script fetched (${scriptText.length} bytes)\n`);
|
|
97
|
+
|
|
98
|
+
await page.evaluate((s) => {
|
|
99
|
+
const el = document.createElement('script');
|
|
100
|
+
el.textContent = s;
|
|
101
|
+
document.head.appendChild(el);
|
|
102
|
+
}, scriptText);
|
|
103
|
+
|
|
104
|
+
await page.waitForTimeout(1000);
|
|
105
|
+
|
|
106
|
+
const hasFigma = await page.evaluate(() => typeof window.figma !== 'undefined');
|
|
107
|
+
if (!hasFigma) {
|
|
108
|
+
process.stderr.write('ERROR: window.figma not available after script injection\n');
|
|
109
|
+
await browser.close();
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
process.stderr.write('Submitting capture...\n');
|
|
114
|
+
const result = await page.evaluate(
|
|
115
|
+
async ({ captureId, endpoint, selector }) => {
|
|
116
|
+
return await window.figma.captureForDesign({ captureId, endpoint, selector });
|
|
117
|
+
},
|
|
118
|
+
{ captureId, endpoint, selector }
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
process.stderr.write(`Result: ${JSON.stringify(result)}\n`);
|
|
122
|
+
await browser.close();
|
|
123
|
+
|
|
124
|
+
if (result && result.success === false) {
|
|
125
|
+
process.stderr.write(`Capture failed: ${result.error}\n`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log(JSON.stringify(result));
|
|
130
|
+
})().catch(e => {
|
|
131
|
+
process.stderr.write(`Fatal: ${e.message}\n`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* dev-f crawl script
|
|
4
|
+
* Usage: node crawl.js <url> [maxDepth]
|
|
5
|
+
* Crawls a website and outputs a JSON array of discovered internal page URLs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { chromium } = require('playwright');
|
|
9
|
+
|
|
10
|
+
const [,, rootUrl, maxDepthArg = '1'] = process.argv;
|
|
11
|
+
const maxDepth = parseInt(maxDepthArg, 10);
|
|
12
|
+
|
|
13
|
+
if (!rootUrl) {
|
|
14
|
+
console.error('Usage: node crawl.js <url> [maxDepth]');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
|
|
22
|
+
function findChromium() {
|
|
23
|
+
const base = path.join(os.homedir(), 'Library', 'Caches', 'ms-playwright');
|
|
24
|
+
if (!fs.existsSync(base)) return null;
|
|
25
|
+
const dirs = fs.readdirSync(base).filter(d => d.startsWith('chromium')).sort().reverse();
|
|
26
|
+
for (const dir of dirs) {
|
|
27
|
+
const shell_arm = path.join(base, dir, 'chrome-headless-shell-mac-arm64', 'chrome-headless-shell');
|
|
28
|
+
if (fs.existsSync(shell_arm)) return shell_arm;
|
|
29
|
+
const arm = path.join(base, dir, 'chrome-mac-arm64', 'Chromium.app', 'Contents', 'MacOS', 'Chromium');
|
|
30
|
+
if (fs.existsSync(arm)) return arm;
|
|
31
|
+
const x64 = path.join(base, dir, 'chrome-mac-x64', 'Chromium.app', 'Contents', 'MacOS', 'Chromium');
|
|
32
|
+
if (fs.existsSync(x64)) return x64;
|
|
33
|
+
const shell_x64 = path.join(base, dir, 'chrome-headless-shell-mac-x64', 'chrome-headless-shell');
|
|
34
|
+
if (fs.existsSync(shell_x64)) return shell_x64;
|
|
35
|
+
}
|
|
36
|
+
const linuxBase = path.join(os.homedir(), '.cache', 'ms-playwright');
|
|
37
|
+
if (fs.existsSync(linuxBase)) {
|
|
38
|
+
const ldirs = fs.readdirSync(linuxBase).filter(d => d.startsWith('chromium')).sort().reverse();
|
|
39
|
+
for (const dir of ldirs) {
|
|
40
|
+
const lp = path.join(linuxBase, dir, 'chrome-linux', 'chrome');
|
|
41
|
+
if (fs.existsSync(lp)) return lp;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeUrl(href, base) {
|
|
48
|
+
try {
|
|
49
|
+
const u = new URL(href, base);
|
|
50
|
+
u.hash = '';
|
|
51
|
+
if (u.pathname !== '/' && u.pathname.endsWith('/')) {
|
|
52
|
+
u.pathname = u.pathname.slice(0, -1);
|
|
53
|
+
}
|
|
54
|
+
return u.toString();
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
(async () => {
|
|
61
|
+
const rootParsed = new URL(rootUrl);
|
|
62
|
+
const origin = rootParsed.origin;
|
|
63
|
+
|
|
64
|
+
const executablePath = findChromium();
|
|
65
|
+
const launchOptions = { headless: true };
|
|
66
|
+
if (executablePath) launchOptions.executablePath = executablePath;
|
|
67
|
+
|
|
68
|
+
const browser = await chromium.launch(launchOptions);
|
|
69
|
+
const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
|
|
70
|
+
|
|
71
|
+
const visited = new Set();
|
|
72
|
+
const queue = [{ url: normalizeUrl(rootUrl, rootUrl), depth: 0 }];
|
|
73
|
+
const pages = [];
|
|
74
|
+
|
|
75
|
+
// Always include the root
|
|
76
|
+
const rootNorm = normalizeUrl(rootUrl, rootUrl);
|
|
77
|
+
visited.add(rootNorm);
|
|
78
|
+
pages.push({ url: rootNorm, title: '', depth: 0 });
|
|
79
|
+
|
|
80
|
+
while (queue.length > 0) {
|
|
81
|
+
const { url, depth } = queue.shift();
|
|
82
|
+
if (depth > maxDepth) continue;
|
|
83
|
+
|
|
84
|
+
process.stderr.write(`Crawling (depth ${depth}): ${url}\n`);
|
|
85
|
+
const page = await context.newPage();
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
await page.goto(url, { waitUntil: 'load', timeout: 30000 });
|
|
89
|
+
await page.waitForTimeout(500);
|
|
90
|
+
|
|
91
|
+
// Update title for this page
|
|
92
|
+
const title = await page.title();
|
|
93
|
+
const existing = pages.find(p => p.url === url);
|
|
94
|
+
if (existing) existing.title = title;
|
|
95
|
+
|
|
96
|
+
if (depth < maxDepth) {
|
|
97
|
+
// Extract all internal links
|
|
98
|
+
const links = await page.evaluate((origin) => {
|
|
99
|
+
return Array.from(document.querySelectorAll('a[href]'))
|
|
100
|
+
.map(a => a.href)
|
|
101
|
+
.filter(h => h.startsWith(origin));
|
|
102
|
+
}, origin);
|
|
103
|
+
|
|
104
|
+
for (const link of links) {
|
|
105
|
+
const norm = normalizeUrl(link, url);
|
|
106
|
+
if (!norm) continue;
|
|
107
|
+
if (!norm.startsWith(origin)) continue;
|
|
108
|
+
// Skip non-page URLs
|
|
109
|
+
if (/\.(pdf|zip|png|jpg|jpeg|gif|svg|ico|css|js|xml|json|woff|woff2|ttf|eot)(\?|$)/i.test(norm)) continue;
|
|
110
|
+
if (!visited.has(norm)) {
|
|
111
|
+
visited.add(norm);
|
|
112
|
+
const pg = { url: norm, title: '', depth: depth + 1 };
|
|
113
|
+
pages.push(pg);
|
|
114
|
+
queue.push({ url: norm, depth: depth + 1 });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {
|
|
119
|
+
process.stderr.write(`Failed to crawl ${url}: ${e.message}\n`);
|
|
120
|
+
} finally {
|
|
121
|
+
await page.close();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await browser.close();
|
|
126
|
+
console.log(JSON.stringify(pages, null, 2));
|
|
127
|
+
})().catch(e => {
|
|
128
|
+
process.stderr.write(`Fatal: ${e.message}\n`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: myaidev-figma-configure
|
|
3
|
+
description: One-time setup for myaidev-figma. Registers Playwright MCP and Figma Remote MCP, installs the Figma plugin, and walks the user through Figma authentication. Run this before using /myaidev-figma for the first time.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, mcp__figma-remote-mcp__whoami
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# /myaidev-figma-configure — One-time Setup
|
|
9
|
+
|
|
10
|
+
You are running **myaidev-figma-configure**. Your job is to get the user's environment ready for `/myaidev-figma` in as few steps as possible.
|
|
11
|
+
|
|
12
|
+
Greet the user:
|
|
13
|
+
|
|
14
|
+
> "**myaidev-figma-configure** — One-time Setup
|
|
15
|
+
> I'll register Playwright MCP, Figma Remote MCP, install the Figma plugin, and verify your Figma authentication. This only needs to be done once."
|
|
16
|
+
|
|
17
|
+
Track two flags as you proceed:
|
|
18
|
+
- `NEEDS_RESTART` — set to true if any MCP was newly registered
|
|
19
|
+
- `AUTH_NEEDED` — set to true if Figma authentication still needs to happen
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## STEP 1 — Register Playwright MCP
|
|
24
|
+
|
|
25
|
+
Check if already registered:
|
|
26
|
+
```bash
|
|
27
|
+
claude mcp get playwright 2>/dev/null && echo "ALREADY_REGISTERED" || echo "NOT_REGISTERED"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**If NOT_REGISTERED:**
|
|
31
|
+
```bash
|
|
32
|
+
claude mcp add playwright -- npx @playwright/mcp 2>&1
|
|
33
|
+
```
|
|
34
|
+
- On success: tell user "✓ Playwright MCP registered." Set `NEEDS_RESTART=true`.
|
|
35
|
+
- On failure: tell user the exact error and stop — they may need to update Claude Code (`npm install -g @anthropic-ai/claude-code`).
|
|
36
|
+
|
|
37
|
+
**If ALREADY_REGISTERED:**
|
|
38
|
+
- Tell user: "✓ Playwright MCP already registered."
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## STEP 2 — Register Figma Remote MCP
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
claude mcp get figma-remote-mcp 2>/dev/null && echo "ALREADY_REGISTERED" || echo "NOT_REGISTERED"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**If NOT_REGISTERED:**
|
|
49
|
+
```bash
|
|
50
|
+
claude mcp add --transport http figma-remote-mcp https://mcp.figma.com/mcp 2>&1
|
|
51
|
+
```
|
|
52
|
+
- On success: tell user "✓ Figma Remote MCP registered." Set `NEEDS_RESTART=true`.
|
|
53
|
+
- On failure: tell user the exact error and stop.
|
|
54
|
+
|
|
55
|
+
**If ALREADY_REGISTERED:**
|
|
56
|
+
- Tell user: "✓ Figma Remote MCP already registered."
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## STEP 3 — Install Figma Plugin
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
claude plugin install figma@claude-plugin-directory 2>&1
|
|
64
|
+
echo "EXIT:$?"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- If exit code is 0: tell user "✓ Figma plugin installed."
|
|
68
|
+
- If exit code is non-zero or command not found: tell the user:
|
|
69
|
+
> "After restarting, run this slash command to install the Figma plugin: `/plugin install figma@claude-plugin-directory`"
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## STEP 4 — Authenticate with Figma
|
|
74
|
+
|
|
75
|
+
**If `NEEDS_RESTART=true`:** Skip this step. Jump to STEP 5.
|
|
76
|
+
|
|
77
|
+
**If everything was already registered (no restart needed):**
|
|
78
|
+
|
|
79
|
+
Tell user: "Verifying your Figma authentication..."
|
|
80
|
+
|
|
81
|
+
Call `mcp__figma-remote-mcp__whoami`.
|
|
82
|
+
|
|
83
|
+
**Case A — returns user details (name/email):**
|
|
84
|
+
- Tell user: "✓ Authenticated with Figma as **[name]**."
|
|
85
|
+
- No further action needed.
|
|
86
|
+
|
|
87
|
+
**Case B — returns an auth URL or login prompt:**
|
|
88
|
+
- Open it automatically:
|
|
89
|
+
```bash
|
|
90
|
+
open "<AUTH_URL>"
|
|
91
|
+
```
|
|
92
|
+
- Tell user:
|
|
93
|
+
> "A Figma login page has opened in your browser. Sign in and authorize access, then let me know when done."
|
|
94
|
+
- Wait for user confirmation.
|
|
95
|
+
- Call `mcp__figma-remote-mcp__whoami` again.
|
|
96
|
+
- If user details returned: "✓ Authenticated as **[name]**. All done!"
|
|
97
|
+
- If still failing: tell user to try opening the URL manually or re-run `/myaidev-figma-configure`.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## STEP 5 — Summary
|
|
102
|
+
|
|
103
|
+
**If `NEEDS_RESTART=true`:**
|
|
104
|
+
|
|
105
|
+
Print:
|
|
106
|
+
```
|
|
107
|
+
✓ Setup complete!
|
|
108
|
+
|
|
109
|
+
✓ Playwright MCP — registered
|
|
110
|
+
✓ Figma Remote MCP — registered
|
|
111
|
+
✓ Figma plugin — installed
|
|
112
|
+
|
|
113
|
+
Next steps:
|
|
114
|
+
1. Restart Claude Code ← MCPs only activate after a restart
|
|
115
|
+
2. Run /myaidev-figma-configure again to complete Figma authentication
|
|
116
|
+
3. Then run /myaidev-figma to start capturing
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**If everything was already set up and authentication passed:**
|
|
120
|
+
|
|
121
|
+
Print:
|
|
122
|
+
```
|
|
123
|
+
✓ All set!
|
|
124
|
+
|
|
125
|
+
✓ Playwright MCP — ready
|
|
126
|
+
✓ Figma Remote MCP — ready
|
|
127
|
+
✓ Figma — authenticated as [name]
|
|
128
|
+
|
|
129
|
+
Run /myaidev-figma to start capturing websites into Figma.
|
|
130
|
+
```
|