crawlio-browser 1.6.2 → 1.6.4
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/mcp-server/{chunk-T4GKS2PG.js → chunk-LOOYHD6I.js} +1 -1
- package/dist/mcp-server/index.js +2 -2
- package/dist/mcp-server/{init-PFND5ZFY.js → init-UJD7YE3X.js} +1 -1
- package/package.json +1 -1
- package/skills/clone/SKILL.md +101 -91
- package/skills/compare/SKILL.md +94 -90
- package/skills/dossier/SKILL.md +81 -135
- package/skills/extract/SKILL.md +86 -46
- package/skills/investigate/SKILL.md +107 -83
- package/skills/monitor/SKILL.md +94 -55
- package/skills/test/SKILL.md +108 -92
- package/skills/web-research/SKILL.md +30 -5
package/skills/monitor/SKILL.md
CHANGED
|
@@ -1,64 +1,103 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: monitor
|
|
3
|
-
description: "Monitor a
|
|
4
|
-
allowed-tools:
|
|
5
|
-
argument-hint: <url>
|
|
3
|
+
description: "Monitor a page for changes — capture baseline, recapture, diff, report what changed"
|
|
4
|
+
allowed-tools: mcp__crawlio-browser__search, mcp__crawlio-browser__execute, mcp__crawlio-browser__connect_tab
|
|
6
5
|
---
|
|
7
6
|
|
|
8
|
-
# Monitor
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
7
|
+
# Monitor — Change Detection
|
|
8
|
+
|
|
9
|
+
Capture a baseline snapshot, recapture later, diff the results. Report structural, content, technology, and performance changes as structured findings.
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- Tracking what changed on a page between two points in time
|
|
14
|
+
- Detecting content additions/removals, tech stack changes, performance regressions
|
|
15
|
+
- Verifying a deploy changed what was expected (and nothing else)
|
|
16
|
+
|
|
17
|
+
## Protocol
|
|
18
|
+
|
|
19
|
+
1. **search** for diff and snapshot commands: `search("diff snapshot")`
|
|
20
|
+
2. **connect_tab** to the target URL
|
|
21
|
+
3. **execute** Code Mode: baseline with `smart.snapshot()` + `smart.extractPage()`
|
|
22
|
+
4. On recapture, diff and emit one `smart.finding()` per change dimension
|
|
23
|
+
5. Return `smart.findings()` as the final output
|
|
24
|
+
|
|
25
|
+
## Code Example
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
// 1. Baseline
|
|
29
|
+
const baselineSnap = await smart.snapshot();
|
|
30
|
+
const baseline = await smart.extractPage();
|
|
31
|
+
const baselineTech = await smart.detectTechnologies();
|
|
32
|
+
|
|
33
|
+
// ... user triggers recapture ...
|
|
34
|
+
|
|
35
|
+
// 2. Recapture + diff
|
|
36
|
+
const current = await smart.extractPage();
|
|
37
|
+
const currentTech = await smart.detectTechnologies();
|
|
38
|
+
const diff = await smart.diffSnapshots(baselineSnap.snapshot);
|
|
39
|
+
|
|
40
|
+
smart.finding({
|
|
41
|
+
claim: `${diff.additions} elements added, ${diff.removals} removed since baseline`,
|
|
42
|
+
evidence: [`additions: ${diff.additions}`, `removals: ${diff.removals}`, `unchanged: ${diff.unchanged}`],
|
|
43
|
+
sourceUrl: current.capture.url,
|
|
44
|
+
confidence: "high",
|
|
45
|
+
method: "diffSnapshots",
|
|
46
|
+
dimension: "structure"
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (diff.additions > 0 || diff.removals > 0) {
|
|
50
|
+
smart.finding({
|
|
51
|
+
claim: `Content changed: ${diff.additions + diff.removals} total mutations`,
|
|
52
|
+
evidence: diff.sample || [`${diff.additions} added`, `${diff.removals} removed`],
|
|
53
|
+
sourceUrl: current.capture.url, confidence: "high",
|
|
54
|
+
method: "diffSnapshots", dimension: "content"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Technology changes
|
|
59
|
+
const oldTech = new Set(baselineTech.technologies?.map(t => t.name) || []);
|
|
60
|
+
const newTech = new Set(currentTech.technologies?.map(t => t.name) || []);
|
|
61
|
+
const added = [...newTech].filter(t => !oldTech.has(t));
|
|
62
|
+
const removed = [...oldTech].filter(t => !newTech.has(t));
|
|
63
|
+
if (added.length || removed.length) {
|
|
64
|
+
smart.finding({
|
|
65
|
+
claim: `Tech stack changed: +${added.length} -${removed.length}`,
|
|
66
|
+
evidence: [...added.map(t => `added: ${t}`), ...removed.map(t => `removed: ${t}`)],
|
|
67
|
+
sourceUrl: current.capture.url, confidence: "high",
|
|
68
|
+
method: "detectTechnologies", dimension: "technology"
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Performance regression check
|
|
73
|
+
const oldLcp = baseline.performance?.webVitals?.lcp;
|
|
74
|
+
const newLcp = current.performance?.webVitals?.lcp;
|
|
75
|
+
if (oldLcp && newLcp) {
|
|
76
|
+
const delta = newLcp - oldLcp;
|
|
77
|
+
smart.finding({
|
|
78
|
+
claim: delta > 100 ? `LCP regressed by ${delta}ms` : `LCP stable (delta: ${delta}ms)`,
|
|
79
|
+
evidence: [`baseline: ${oldLcp}ms`, `current: ${newLcp}ms`],
|
|
80
|
+
sourceUrl: current.capture.url, confidence: "high",
|
|
81
|
+
method: "extractPage", dimension: "performance"
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await smart.scrollCapture();
|
|
86
|
+
return { findings: smart.findings(), diff: { added: diff.additions, removed: diff.removals } };
|
|
46
87
|
```
|
|
47
|
-
## Monitor: <url>
|
|
48
88
|
|
|
49
|
-
|
|
50
|
-
- [List of DiffChange entries grouped by dimension]
|
|
89
|
+
## Anti-Patterns
|
|
51
90
|
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
91
|
+
- Do NOT use `sleep()` loops to poll for changes — capture baseline, wait for user signal, recapture
|
|
92
|
+
- Do NOT compare screenshots visually — use `smart.diffSnapshots()` for structural diff
|
|
93
|
+
- Do NOT use `location.href` — use `current.capture.url` from extractPage
|
|
94
|
+
- Always `search()` first to confirm diff commands exist
|
|
56
95
|
|
|
57
|
-
|
|
58
|
-
- Baseline: <baselineId> (quality: ...)
|
|
59
|
-
- Current: <currentId> (quality: ...)
|
|
60
|
-
- Diff: <diffId> (quality: ...)
|
|
96
|
+
## Output
|
|
61
97
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
98
|
+
The skill produces `Finding[]` via `smart.findings()`. Dimension tags:
|
|
99
|
+
|
|
100
|
+
- **structure** — DOM elements added or removed
|
|
101
|
+
- **content** — text and media mutations
|
|
102
|
+
- **technology** — frameworks added or removed
|
|
103
|
+
- **performance** — LCP, CLS, timing regressions or improvements
|
package/skills/test/SKILL.md
CHANGED
|
@@ -1,101 +1,117 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: test
|
|
3
|
-
description: "
|
|
4
|
-
allowed-tools:
|
|
5
|
-
argument-hint: <url>
|
|
3
|
+
description: "Test a page against quality assertions — accessibility, performance, security, SEO, mobile readiness"
|
|
4
|
+
allowed-tools: mcp__crawlio-browser__search, mcp__crawlio-browser__execute, mcp__crawlio-browser__connect_tab
|
|
6
5
|
---
|
|
7
6
|
|
|
8
|
-
# Test
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
7
|
+
# Test — Quality Assertions
|
|
8
|
+
|
|
9
|
+
Run pass/fail assertions across accessibility, performance, security, SEO, and mobile readiness. Every assertion becomes a finding. Confidence auto-caps when data is missing.
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- Auditing accessibility, performance, security, SEO, or mobile readiness
|
|
14
|
+
- Running pass/fail assertions against quality thresholds
|
|
15
|
+
|
|
16
|
+
## Protocol
|
|
17
|
+
|
|
18
|
+
1. **search** for extraction commands: `search("extract page accessibility")`
|
|
19
|
+
2. **connect_tab** to the target URL
|
|
20
|
+
3. **execute** Code Mode: `smart.extractPage()` gathers all dimensions in one call
|
|
21
|
+
4. Emit one `smart.finding()` per assertion — claim states pass or fail
|
|
22
|
+
5. Return `smart.findings()` + `page.gaps`
|
|
23
|
+
|
|
24
|
+
## Code Example
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const page = await smart.extractPage();
|
|
28
|
+
|
|
29
|
+
// Accessibility
|
|
30
|
+
if (page.accessibility) {
|
|
31
|
+
smart.finding({
|
|
32
|
+
claim: page.accessibility.imagesWithoutAlt === 0
|
|
33
|
+
? "All images have alt text"
|
|
34
|
+
: `${page.accessibility.imagesWithoutAlt} images missing alt text`,
|
|
35
|
+
evidence: [`imagesWithoutAlt: ${page.accessibility.imagesWithoutAlt}`, `nodeCount: ${page.accessibility.nodeCount}`],
|
|
36
|
+
sourceUrl: page.capture.url, confidence: "high",
|
|
37
|
+
method: "extractPage", dimension: "accessibility"
|
|
38
|
+
});
|
|
39
|
+
smart.finding({
|
|
40
|
+
claim: page.accessibility.landmarkCount > 0
|
|
41
|
+
? `${page.accessibility.landmarkCount} ARIA landmarks found`
|
|
42
|
+
: "No ARIA landmarks — add banner, main, contentinfo",
|
|
43
|
+
evidence: [`landmarkCount: ${page.accessibility.landmarkCount}`],
|
|
44
|
+
sourceUrl: page.capture.url, confidence: "high",
|
|
45
|
+
method: "extractPage", dimension: "accessibility"
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Performance
|
|
50
|
+
if (page.performance) {
|
|
51
|
+
const lcp = page.performance.webVitals?.lcp;
|
|
52
|
+
const cls = page.performance.webVitals?.cls;
|
|
53
|
+
smart.finding({
|
|
54
|
+
claim: lcp && lcp < 2500 ? `LCP good (${lcp}ms)` : `LCP needs work (${lcp || "unknown"}ms)`,
|
|
55
|
+
evidence: [`LCP: ${lcp}ms`, `CLS: ${cls}`, `thresholds: LCP<2500, CLS<0.1`],
|
|
56
|
+
sourceUrl: page.capture.url, confidence: lcp ? "high" : "low",
|
|
57
|
+
method: "extractPage", dimension: "performance"
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Security
|
|
62
|
+
if (page.security) {
|
|
63
|
+
smart.finding({
|
|
64
|
+
claim: page.security.securityState === "secure"
|
|
65
|
+
? "TLS connection is secure" : `Security state: ${page.security.securityState || "unknown"}`,
|
|
66
|
+
evidence: [`protocol: ${page.security.protocol || "unknown"}`],
|
|
67
|
+
sourceUrl: page.capture.url, confidence: "high",
|
|
68
|
+
method: "extractPage", dimension: "security"
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// SEO
|
|
73
|
+
if (page.capture?.meta) {
|
|
74
|
+
const m = page.capture.meta;
|
|
75
|
+
smart.finding({
|
|
76
|
+
claim: m.title && m.description ? "Title + meta description present" : "SEO meta tags incomplete",
|
|
77
|
+
evidence: [`title: ${m.title || "missing"} (${m.title?.length || 0} chars)`,
|
|
78
|
+
`description: ${m.description || "missing"} (${m.description?.length || 0} chars)`],
|
|
79
|
+
sourceUrl: page.capture.url, confidence: "high",
|
|
80
|
+
method: "extractPage", dimension: "seo"
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Mobile readiness
|
|
85
|
+
if (page.mobileReadiness) {
|
|
86
|
+
smart.finding({
|
|
87
|
+
claim: page.mobileReadiness.hasViewportMeta ? "Viewport meta tag present" : "Missing viewport meta",
|
|
88
|
+
evidence: [`viewport: ${page.mobileReadiness.viewportContent || "none"}`],
|
|
89
|
+
sourceUrl: page.capture.url, confidence: "high",
|
|
90
|
+
method: "extractPage", dimension: "mobile-readiness"
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Tech stack
|
|
95
|
+
const tech = await smart.detectTechnologies();
|
|
96
|
+
if (tech.technologies?.length) {
|
|
97
|
+
smart.finding({
|
|
98
|
+
claim: `${tech.technologies.length} technologies detected`,
|
|
99
|
+
evidence: tech.technologies.map(t => t.name),
|
|
100
|
+
sourceUrl: page.capture.url, confidence: "high",
|
|
101
|
+
method: "detectTechnologies", dimension: "technology"
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { findings: smart.findings(), gaps: page.gaps };
|
|
64
106
|
```
|
|
65
|
-
## Test: <url>
|
|
66
|
-
|
|
67
|
-
### Summary
|
|
68
|
-
- Score: [0-100]/100
|
|
69
|
-
- Tests: [passed] passed, [failed] failed, [warnings] warnings
|
|
70
|
-
- Flows: [count] testable flows discovered
|
|
71
|
-
|
|
72
|
-
### Accessibility
|
|
73
|
-
- [audit results with pass/fail/warning status]
|
|
74
|
-
|
|
75
|
-
### Performance
|
|
76
|
-
- [audit results with scores]
|
|
77
107
|
|
|
78
|
-
|
|
79
|
-
- [audit results with pass/fail status]
|
|
108
|
+
## Anti-Patterns
|
|
80
109
|
|
|
81
|
-
|
|
82
|
-
-
|
|
110
|
+
- Do NOT use `smart.screenshot()` — extractPage captures everything needed
|
|
111
|
+
- Do NOT use `sleep()` to wait for metrics — extractPage handles page load
|
|
112
|
+
- Do NOT use `location.href` — use `page.capture.url`
|
|
113
|
+
- Always `search()` first if unsure which fields extractPage returns
|
|
83
114
|
|
|
84
|
-
|
|
85
|
-
- [audit results]
|
|
115
|
+
## Output
|
|
86
116
|
|
|
87
|
-
|
|
88
|
-
- [list of testable flows with steps]
|
|
89
|
-
|
|
90
|
-
### Evidence Chain
|
|
91
|
-
- Crawler: <crawlId> (quality: ...)
|
|
92
|
-
- Analyzer: <analyzeId> (quality: ...)
|
|
93
|
-
- Auditor: <auditId> (quality: ...)
|
|
94
|
-
- Suite: <suiteId> (quality: ...)
|
|
95
|
-
|
|
96
|
-
### Coverage Gaps
|
|
97
|
-
- [Any gaps from the investigation]
|
|
98
|
-
|
|
99
|
-
### Confidence
|
|
100
|
-
- Overall: high/medium/low
|
|
101
|
-
```
|
|
117
|
+
The skill produces `Finding[]` via `smart.findings()`. Dimensions: **accessibility**, **performance**, **security**, **seo**, **mobile-readiness**, **technology**. When data is missing, confidence auto-caps to "low" and the gap appears in `page.gaps`.
|
|
@@ -29,7 +29,7 @@ await connect_tab({ url: "https://example.com" })
|
|
|
29
29
|
|
|
30
30
|
// Extract everything in one call
|
|
31
31
|
const page = await smart.extractPage()
|
|
32
|
-
// Returns: { capture, performance, security, fonts, meta }
|
|
32
|
+
// Returns: { capture, performance, security, fonts, meta, accessibility, mobileReadiness, gaps }
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
For visual evidence, add `smart.scrollCapture()`:
|
|
@@ -63,12 +63,34 @@ const record = {
|
|
|
63
63
|
security: page.security, // TLS, cert, mixed content
|
|
64
64
|
fonts: page.fonts, // declared + computed
|
|
65
65
|
meta: page.meta, // OG tags, structured data, headings, nav links
|
|
66
|
+
accessibility: page.accessibility, // node count, landmarks, images without alt, heading structure
|
|
67
|
+
mobileReadiness: page.mobileReadiness, // viewport meta, media queries, overflow
|
|
68
|
+
gaps: page.gaps, // what data failed — check before trusting null fields
|
|
66
69
|
}
|
|
67
70
|
```
|
|
68
71
|
|
|
69
72
|
### Phase 3: Analyze
|
|
70
73
|
|
|
71
|
-
Compare against a fixed rubric. Produce
|
|
74
|
+
Compare against a fixed rubric. Produce validated findings using `smart.finding()`:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
smart.finding({
|
|
78
|
+
claim: "Site loads 47 network requests with 2 failures",
|
|
79
|
+
evidence: ["network.total: 47", "network.failed: 2"],
|
|
80
|
+
sourceUrl: page.capture.url,
|
|
81
|
+
confidence: "high",
|
|
82
|
+
method: "extractPage",
|
|
83
|
+
dimension: "performance" // auto-caps confidence if perf data had gaps
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// Retrieve all findings from this session
|
|
87
|
+
const allFindings = smart.findings()
|
|
88
|
+
|
|
89
|
+
// Reset for next research task
|
|
90
|
+
smart.clearFindings()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
If `extractPage()` reported gaps (e.g., performance metrics failed), findings with matching `dimension` get confidence automatically capped one level (high → medium, medium → low). Check `page.gaps` to understand what data is missing.
|
|
72
94
|
|
|
73
95
|
## Use Existing Tools — Not Manual Equivalents
|
|
74
96
|
|
|
@@ -128,9 +150,12 @@ When comparing sites, evaluate these dimensions:
|
|
|
128
150
|
|
|
129
151
|
### Prefer:
|
|
130
152
|
|
|
131
|
-
- **One `extractPage()` per page** — it runs
|
|
132
|
-
- **`comparePages()` for 2-site comparisons** —
|
|
133
|
-
-
|
|
153
|
+
- **One `extractPage()` per page** — it runs 7 ops in parallel (capture + perf + security + fonts + meta + accessibility + mobile-readiness) with typed gaps
|
|
154
|
+
- **`comparePages()` for 2-site comparisons** — returns `{ siteA, siteB, scaffold }` with 11 fixed comparison dimensions
|
|
155
|
+
- **`smart.finding()` for validated findings** — enforces claim + evidence + sourceUrl + confidence + method schema
|
|
156
|
+
- **No `smart.screenshot()`** — it doesn't exist. Use `bridge.send({ type: 'take_screenshot' })` or `smart.scrollCapture()`
|
|
157
|
+
- **No `smart.snapshot({ compact: true })`** — compact option doesn't exist. Use `smart.snapshot()` or `{ interactive: true }`
|
|
158
|
+
- **No `location.href = "..."` for navigation** — use `smart.navigate(url)`. Direct assignment breaks CDP
|
|
134
159
|
|
|
135
160
|
## Example: Competitive Audit
|
|
136
161
|
|