deepflow 0.1.78 → 0.1.80

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.
@@ -0,0 +1,258 @@
1
+ ---
2
+ name: browse-fetch
3
+ description: Fetches live web content using headless Chromium via Playwright. Use when you need to read documentation, articles, or any public URL that requires JavaScript rendering. Falls back to WebFetch for simple HTML pages.
4
+ ---
5
+
6
+ # Browse-Fetch
7
+
8
+ Retrieve live web content with a headless browser. Handles JavaScript-rendered pages, SPAs, and dynamic content that WebFetch cannot reach.
9
+
10
+ ## When to Use
11
+
12
+ - Reading documentation sites that require JavaScript to render (e.g., React-based docs, Vite, Next.js portals)
13
+ - Fetching the current content of a specific URL provided by the user
14
+ - Extracting article or reference content from a known page before implementing code against it
15
+
16
+ ## Skip When
17
+
18
+ - The URL is a plain HTML page or GitHub raw file — use WebFetch instead (faster, no overhead)
19
+ - The target requires authentication (login wall) or CAPTCHA — browser cannot bypass; note the block and continue
20
+
21
+ ---
22
+
23
+ ## Browser Core Protocol
24
+
25
+ This protocol is the reusable foundation for all browser-based skills (browse-fetch, browse-verify, etc.).
26
+
27
+ ### 1. Install Check
28
+
29
+ Before launching, verify Playwright is available:
30
+
31
+ ```bash
32
+ # Prefer bun if available, fall back to node
33
+ if which bun > /dev/null 2>&1; then RUNTIME=bun; else RUNTIME=node; fi
34
+
35
+ $RUNTIME -e "require('playwright')" 2>/dev/null \
36
+ || npx --yes playwright install chromium --with-deps 2>&1 | tail -5
37
+ ```
38
+
39
+ If installation fails, fall back to WebFetch (see Fallback section below).
40
+
41
+ ### 2. Launch Command
42
+
43
+ ```bash
44
+ # Detect runtime
45
+ if which bun > /dev/null 2>&1; then RUNTIME=bun; else RUNTIME=node; fi
46
+
47
+ $RUNTIME -e "
48
+ const { chromium } = require('playwright');
49
+ (async () => {
50
+ const browser = await chromium.launch({ headless: true });
51
+ const context = await browser.newContext({
52
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
53
+ });
54
+ const page = await context.newPage();
55
+
56
+ // --- navigation + extraction (see sections 3–4) ---
57
+
58
+ await browser.close();
59
+ })().catch(e => { console.error(e.message); process.exit(1); });
60
+ "
61
+ ```
62
+
63
+ ### 3. Navigation
64
+
65
+ ```js
66
+ // Inside the async IIFE above
67
+ await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
68
+ // Allow JS to settle
69
+ await page.waitForTimeout(1500);
70
+ ```
71
+
72
+ - Use `waitUntil: 'domcontentloaded'` for speed; upgrade to `'networkidle'` only if content is missing.
73
+ - Set `timeout: 30000` (30 s). On timeout, treat as graceful failure (see section 5).
74
+
75
+ ### 4. Content Extraction
76
+
77
+ Extract the main readable text, not raw HTML:
78
+
79
+ ```js
80
+ // Primary: semantic content containers
81
+ let text = await page.innerText('main, article, [role="main"]').catch(() => '');
82
+
83
+ // Fallback: full body text
84
+ if (!text || text.trim().length < 100) {
85
+ text = await page.innerText('body').catch(() => '');
86
+ }
87
+
88
+ // Truncate to ~4000 tokens (~16000 chars) to stay within context budget
89
+ const MAX_CHARS = 16000;
90
+ if (text.length > MAX_CHARS) {
91
+ text = text.slice(0, MAX_CHARS) + '\n\n[content truncated — use a more specific selector or paginate]';
92
+ }
93
+
94
+ console.log(text);
95
+ ```
96
+
97
+ For interactive element inspection (e.g., browse-verify), use `locator.ariaSnapshot()` instead of `innerText`.
98
+
99
+ ### 5. Graceful Failure
100
+
101
+ Detect and handle blocks without crashing:
102
+
103
+ ```js
104
+ const title = await page.title();
105
+ const url = page.url();
106
+
107
+ // Login wall
108
+ if (/sign.?in|log.?in|auth/i.test(title) || url.includes('/login')) {
109
+ console.log(`[browse-fetch] Blocked by login wall at ${url}. Skipping.`);
110
+ await browser.close();
111
+ process.exit(0);
112
+ }
113
+
114
+ // CAPTCHA
115
+ const bodyText = await page.innerText('body').catch(() => '');
116
+ if (/captcha|robot|human verification/i.test(bodyText)) {
117
+ console.log(`[browse-fetch] CAPTCHA detected at ${url}. Skipping.`);
118
+ await browser.close();
119
+ process.exit(0);
120
+ }
121
+ ```
122
+
123
+ On graceful failure: return the URL and a short explanation, then continue with the task using available context.
124
+
125
+ ### 6. Cleanup
126
+
127
+ Always close the browser in a `finally` block or after use:
128
+
129
+ ```js
130
+ await browser.close();
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Fetch Workflow
136
+
137
+ **Goal:** retrieve and return the text content of a single URL.
138
+
139
+ ```bash
140
+ # Full inline script — adapt URL and selector per query
141
+ if which bun > /dev/null 2>&1; then RUNTIME=bun; else RUNTIME=node; fi
142
+
143
+ $RUNTIME -e "
144
+ const { chromium } = require('playwright');
145
+ (async () => {
146
+ const browser = await chromium.launch({ headless: true });
147
+ const context = await browser.newContext({
148
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
149
+ });
150
+ const page = await context.newPage();
151
+
152
+ try {
153
+ await page.goto('https://example.com/docs/page', {
154
+ waitUntil: 'domcontentloaded',
155
+ timeout: 30000
156
+ });
157
+ await page.waitForTimeout(1500);
158
+
159
+ const title = await page.title();
160
+ const url = page.url();
161
+
162
+ if (/sign.?in|log.?in|auth/i.test(title) || url.includes('/login')) {
163
+ console.log('[browse-fetch] Blocked by login wall at ' + url);
164
+ return;
165
+ }
166
+
167
+ let text = await page.innerText('main, article, [role=\"main\"]').catch(() => '');
168
+ if (!text || text.trim().length < 100) {
169
+ text = await page.innerText('body').catch(() => '');
170
+ }
171
+
172
+ const MAX_CHARS = 16000;
173
+ if (text.length > MAX_CHARS) {
174
+ text = text.slice(0, MAX_CHARS) + '\n\n[content truncated]';
175
+ }
176
+
177
+ console.log('=== ' + title + ' ===\n' + text);
178
+ } finally {
179
+ await browser.close();
180
+ }
181
+ })().catch(e => { console.error(e.message); process.exit(1); });
182
+ "
183
+ ```
184
+
185
+ Adapt the URL and selector per query. The agent inlines the full script via `node -e` or `bun -e` so no temp files are needed for extractions under ~4000 tokens.
186
+
187
+ ---
188
+
189
+ ## Search + Navigation Protocol
190
+
191
+ **Time-box:** 60 seconds total. **Page cap:** 5 pages per query.
192
+
193
+ > Search engines (Google, DuckDuckGo) block headless browsers with CAPTCHAs. Do NOT use Playwright to search them.
194
+
195
+ Instead, use one of these strategies:
196
+
197
+ | Strategy | When to use |
198
+ |----------|-------------|
199
+ | Direct URL construction | You know the domain (e.g., `docs.stripe.com/api/charges`) |
200
+ | WebSearch tool | General keyword search before fetching pages |
201
+ | Site-specific search | Navigate to `site.com/search?q=term` if the site exposes it |
202
+
203
+ **Navigation loop** (up to 5 pages):
204
+
205
+ 1. Construct or obtain the target URL.
206
+ 2. Run the fetch workflow above.
207
+ 3. If the page lacks the needed information, look for a next-page link or a more specific sub-URL.
208
+ 4. Repeat up to 4 more times (5 total).
209
+ 5. Stop and summarize what was found within the 60 s window.
210
+
211
+ ---
212
+
213
+ ## Session Cache
214
+
215
+ The context window is the cache. Extracted content lives in the conversation until it is no longer needed.
216
+
217
+ For extractions larger than ~4000 tokens, write to a temp file and reference it:
218
+
219
+ ```bash
220
+ # Write large extraction to temp file
221
+ TMPFILE=$(mktemp /tmp/browse-fetch-XXXXXX.txt)
222
+ $RUNTIME -e "...script..." > "$TMPFILE"
223
+ echo "Content saved to $TMPFILE"
224
+ # Read relevant sections with grep or head rather than loading all at once
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Fallback Without Playwright
230
+
231
+ When Playwright is unavailable or fails to install, fall back to the WebFetch tool for:
232
+
233
+ - Static HTML sites (GitHub README, raw docs, Wikipedia)
234
+ - Any URL the user provides where JavaScript rendering is not required
235
+
236
+ | Condition | Action |
237
+ |-----------|--------|
238
+ | `playwright` not installed, install fails | Use WebFetch |
239
+ | Page is a known static domain (github.com/raw, pastebin, etc.) | Use WebFetch directly — skip Playwright |
240
+ | Playwright times out twice | Use WebFetch as fallback attempt |
241
+
242
+ ```
243
+ WebFetch: { url: "https://example.com/page", prompt: "Extract the main content" }
244
+ ```
245
+
246
+ If WebFetch also fails, return the URL with an explanation and continue the task.
247
+
248
+ ---
249
+
250
+ ## Rules
251
+
252
+ - Always run the install check before the first browser launch in a session.
253
+ - Detect runtime with `which bun` first; use `node` if bun is absent.
254
+ - Never navigate to Google or DuckDuckGo with Playwright — use WebSearch tool or direct URLs.
255
+ - Truncate output at ~4000 tokens (~16 000 chars) to protect context budget.
256
+ - On login wall or CAPTCHA, log the block, skip, and continue — never retry infinitely.
257
+ - Close the browser in every code path (use `finally`).
258
+ - Do not persist browser sessions across unrelated tasks.
@@ -0,0 +1,264 @@
1
+ ---
2
+ name: browse-verify
3
+ description: Verifies UI acceptance criteria by launching a headless browser, extracting the accessibility tree, and evaluating structured assertions deterministically. Use when a spec has browser-based ACs that need automated verification after implementation.
4
+ ---
5
+
6
+ # Browse-Verify
7
+
8
+ Headless browser verification using Playwright's accessibility tree. Evaluates structured assertions from PLAN.md without LLM calls — purely deterministic matching.
9
+
10
+ ## When to Use
11
+
12
+ After implementing a spec that contains browser-based acceptance criteria:
13
+ - Visual/layout checks (element presence, text content, roles)
14
+ - Interactive state checks (aria-checked, aria-expanded, aria-disabled)
15
+ - Structural checks (element within a container)
16
+
17
+ **Skip when:** The spec has no browser-facing ACs, or the implementation is backend-only.
18
+
19
+ ## Prerequisites
20
+
21
+ - Node.js (preferred) or Bun
22
+ - Playwright 1.x (`npm install playwright` or `npx playwright install`)
23
+ - Chromium browser (auto-installed if missing)
24
+
25
+ ## Runtime Detection
26
+
27
+ ```bash
28
+ # Prefer Node.js; fall back to Bun
29
+ if which node > /dev/null 2>&1; then
30
+ RUNTIME=node
31
+ elif which bun > /dev/null 2>&1; then
32
+ RUNTIME=bun
33
+ else
34
+ echo "Error: neither node nor bun found" && exit 1
35
+ fi
36
+ ```
37
+
38
+ ## Browser Auto-Install
39
+
40
+ Before running, ensure Chromium is available:
41
+
42
+ ```bash
43
+ npx playwright install chromium
44
+ ```
45
+
46
+ Run this once per environment. If it fails due to permissions, instruct the user to run it manually.
47
+
48
+ ## Protocol
49
+
50
+ ### 1. Read Assertions from PLAN.md
51
+
52
+ Assertions are written into PLAN.md by the `plan` skill during planning. Format:
53
+
54
+ ```yaml
55
+ assertions:
56
+ - role: button
57
+ name: "Submit"
58
+ check: visible
59
+ - role: checkbox
60
+ name: "Accept terms"
61
+ check: state
62
+ value: checked
63
+ - role: heading
64
+ name: "Dashboard"
65
+ check: visible
66
+ within: main
67
+ - role: textbox
68
+ name: "Email"
69
+ check: value
70
+ value: "user@example.com"
71
+ ```
72
+
73
+ Assertion schema:
74
+
75
+ | Field | Required | Description |
76
+ |----------|----------|-------------|
77
+ | `role` | yes | ARIA role (button, checkbox, heading, textbox, link, etc.) |
78
+ | `name` | yes | Accessible name (exact or partial match) |
79
+ | `check` | yes | One of: `visible`, `absent`, `state`, `value`, `count` |
80
+ | `value` | no | Expected value for `state` or `value` checks |
81
+ | `within` | no | Ancestor role or selector to scope the search |
82
+
83
+ ### 2. Launch Browser and Navigate
84
+
85
+ ```javascript
86
+ const { chromium } = require('playwright');
87
+
88
+ const browser = await chromium.launch({ headless: true });
89
+ const page = await browser.newPage();
90
+
91
+ await page.goto(TARGET_URL, { waitUntil: 'networkidle' });
92
+ ```
93
+
94
+ `TARGET_URL` is read from the spec's metadata or passed as an argument.
95
+
96
+ ### 3. Extract Accessibility Tree
97
+
98
+ Use `locator.ariaSnapshot()` — **NOT** `page.accessibility.snapshot()` (removed in Playwright 1.x):
99
+
100
+ ```javascript
101
+ // Full-page aria snapshot (YAML-like role tree)
102
+ const snapshot = await page.locator('body').ariaSnapshot();
103
+
104
+ // Scoped snapshot within a container
105
+ const containerSnapshot = await page.locator('main').ariaSnapshot();
106
+ ```
107
+
108
+ `ariaSnapshot()` returns a YAML-like string such as:
109
+
110
+ ```yaml
111
+ - heading "Dashboard" [level=1]
112
+ - button "Submit" [disabled]
113
+ - checkbox "Accept terms" [checked]
114
+ - textbox "Email": user@example.com
115
+ ```
116
+
117
+ ### 4. Capture Bounding Boxes (optional)
118
+
119
+ For spatial/layout assertions or debugging:
120
+
121
+ ```javascript
122
+ const element = page.getByRole(role, { name: assertionName });
123
+ const box = await element.boundingBox();
124
+ // box: { x, y, width, height } or null if not visible
125
+ ```
126
+
127
+ ### 5. Evaluate Assertions Deterministically
128
+
129
+ Parse the aria snapshot and evaluate each assertion. No LLM calls during this phase.
130
+
131
+ ```javascript
132
+ function evaluateAssertion(snapshot, assertion) {
133
+ const { role, name, check, value, within } = assertion;
134
+
135
+ // Optionally scope to a sub-tree
136
+ const tree = within
137
+ ? extractSubtree(snapshot, within)
138
+ : snapshot;
139
+
140
+ switch (check) {
141
+ case 'visible':
142
+ return treeContains(tree, role, name);
143
+
144
+ case 'absent':
145
+ return !treeContains(tree, role, name);
146
+
147
+ case 'state':
148
+ // e.g., value: "checked", "disabled", "expanded"
149
+ return treeContainsWithState(tree, role, name, value);
150
+
151
+ case 'value':
152
+ // Matches textbox/combobox displayed value
153
+ return treeContainsWithValue(tree, role, name, value);
154
+
155
+ case 'count':
156
+ return countMatches(tree, role, name) === parseInt(value, 10);
157
+ }
158
+ }
159
+ ```
160
+
161
+ Matching rules:
162
+ - Role matching is case-insensitive
163
+ - Name matching is case-insensitive substring match (unless wrapped in quotes for exact match)
164
+ - State tokens (`[checked]`, `[disabled]`, `[expanded]`) are parsed from the snapshot line
165
+
166
+ ### 6. Capture Screenshot
167
+
168
+ After evaluation, capture a screenshot for the audit trail:
169
+
170
+ ```javascript
171
+ const screenshotDir = `.deepflow/screenshots/${specName}`;
172
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
173
+ const screenshotPath = `${screenshotDir}/${timestamp}.png`;
174
+
175
+ await fs.mkdir(screenshotDir, { recursive: true });
176
+ await page.screenshot({ path: screenshotPath, fullPage: true });
177
+ ```
178
+
179
+ Screenshot path convention: `.deepflow/screenshots/{spec-name}/{timestamp}.png`
180
+
181
+ ### 7. Report Results
182
+
183
+ Emit a structured result for each assertion:
184
+
185
+ ```
186
+ [PASS] button "Submit" — visible ✓
187
+ [PASS] checkbox "Accept terms" — state: checked ✓
188
+ [FAIL] heading "Dashboard" — expected visible, not found in snapshot
189
+ [PASS] textbox "Email" — value: user@example.com ✓
190
+
191
+ Results: 3 passed, 1 failed
192
+ Screenshot: .deepflow/screenshots/login-form/2026-03-14T12-00-00-000Z.png
193
+ ```
194
+
195
+ Exit with code 0 if all assertions pass, 1 if any fail.
196
+
197
+ ### 8. Tear Down
198
+
199
+ ```javascript
200
+ await browser.close();
201
+ ```
202
+
203
+ Always close the browser, even on error (use try/finally).
204
+
205
+ ## Full Script Template
206
+
207
+ ```javascript
208
+ #!/usr/bin/env node
209
+ const { chromium } = require('playwright');
210
+ const fs = require('fs/promises');
211
+ const path = require('path');
212
+
213
+ async function main({ targetUrl, specName, assertions }) {
214
+ // Auto-install chromium if needed
215
+ // (handled by: npx playwright install chromium)
216
+
217
+ const browser = await chromium.launch({ headless: true });
218
+ const page = await browser.newPage();
219
+
220
+ try {
221
+ await page.goto(targetUrl, { waitUntil: 'networkidle' });
222
+
223
+ const snapshot = await page.locator('body').ariaSnapshot();
224
+
225
+ const results = assertions.map(assertion => ({
226
+ assertion,
227
+ passed: evaluateAssertion(snapshot, assertion),
228
+ }));
229
+
230
+ // Screenshot
231
+ const screenshotDir = path.join('.deepflow', 'screenshots', specName);
232
+ await fs.mkdir(screenshotDir, { recursive: true });
233
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
234
+ const screenshotPath = path.join(screenshotDir, `${timestamp}.png`);
235
+ await page.screenshot({ path: screenshotPath, fullPage: true });
236
+
237
+ // Report
238
+ const passed = results.filter(r => r.passed).length;
239
+ const failed = results.filter(r => !r.passed).length;
240
+
241
+ for (const { assertion, passed: ok } of results) {
242
+ const status = ok ? '[PASS]' : '[FAIL]';
243
+ console.log(`${status} ${assertion.role} "${assertion.name}" — ${assertion.check}${assertion.value ? ': ' + assertion.value : ''}`);
244
+ }
245
+
246
+ console.log(`\nResults: ${passed} passed, ${failed} failed`);
247
+ console.log(`Screenshot: ${screenshotPath}`);
248
+
249
+ process.exit(failed > 0 ? 1 : 0);
250
+ } finally {
251
+ await browser.close();
252
+ }
253
+ }
254
+ ```
255
+
256
+ ## Rules
257
+
258
+ - Never call an LLM during the verify phase — all assertion evaluation is deterministic
259
+ - Always use `locator.ariaSnapshot()`, never `page.accessibility.snapshot()` (removed)
260
+ - Always close the browser in a `finally` block
261
+ - Screenshot every run regardless of pass/fail outcome
262
+ - If Playwright is not installed, emit a clear error and instructions — don't silently skip
263
+ - Partial name matching is the default; use exact matching only when the assertion specifies it
264
+ - Report results to stdout in the structured format above for downstream parsing
@@ -75,3 +75,17 @@ quality:
75
75
 
76
76
  # Retry flaky tests once before failing (default: true)
77
77
  test_retry_on_fail: true
78
+
79
+ # Enable L5 browser verification after tests pass (default: false)
80
+ # When true, deepflow will start the dev server and run visual checks
81
+ browser_verify: false
82
+
83
+ # Override the dev server start command for browser verification
84
+ # If empty, deepflow will attempt to auto-detect (e.g., "npm run dev", "yarn dev")
85
+ dev_command: ""
86
+
87
+ # Port that the dev server listens on (default: 3000)
88
+ dev_port: 3000
89
+
90
+ # Timeout in seconds to wait for the dev server to become ready (default: 30)
91
+ browser_timeout: 30
@@ -1,87 +0,0 @@
1
- ---
2
- name: context-hub
3
- description: Fetches curated API docs for external libraries before coding. Use when implementing code that uses external APIs/SDKs (Stripe, OpenAI, MongoDB, etc.) to avoid hallucinating APIs and reduce token usage.
4
- ---
5
-
6
- # Context Hub
7
-
8
- Fetch curated, versioned docs for external libraries instead of guessing APIs.
9
-
10
- ## When to Use
11
-
12
- Before writing code that calls an external API or SDK:
13
- - New library integration (e.g., Stripe payments, AWS S3)
14
- - Unfamiliar API version or method
15
- - Complex API with many options (e.g., MongoDB aggregation)
16
-
17
- **Skip when:** Working with internal code (use LSP instead) or well-known stdlib APIs.
18
-
19
- ## Prerequisites
20
-
21
- Requires `chub` CLI: `npm install -g @aisuite/chub`
22
-
23
- If `chub` is not installed, tell the user and skip — don't block implementation.
24
-
25
- ## Workflow
26
-
27
- ### 1. Search for docs
28
-
29
- ```bash
30
- chub search "<library or API>" --json
31
- ```
32
-
33
- Example:
34
- ```bash
35
- chub search "stripe payments" --json
36
- chub search "mongodb aggregation" --json
37
- ```
38
-
39
- ### 2. Fetch relevant docs
40
-
41
- ```bash
42
- chub get <id> --lang <py|js|ts>
43
- ```
44
-
45
- Use `--lang` matching the project language. Use `--full` only if the summary lacks what you need.
46
-
47
- ### 3. Write code using fetched docs
48
-
49
- Use the retrieved documentation as ground truth for API signatures, parameter names, and patterns.
50
-
51
- ### 4. Annotate discoveries
52
-
53
- When you find something the docs missed or got wrong:
54
-
55
- ```bash
56
- chub annotate <id> "Note: method X requires param Y since v2.0"
57
- ```
58
-
59
- This persists locally and appears on future `chub get` calls — the agent learns across sessions.
60
-
61
- ### 5. Rate docs (optional)
62
-
63
- ```bash
64
- chub feedback <id> up --label accurate
65
- chub feedback <id> down --label outdated
66
- ```
67
-
68
- Labels: `accurate`, `outdated`, `incomplete`, `wrong-version`, `helpful`
69
-
70
- ## Integration with LSP
71
-
72
- | Need | Tool |
73
- |------|------|
74
- | Internal code navigation | LSP (`goToDefinition`, `findReferences`) |
75
- | External API signatures | Context Hub (`chub get`) |
76
- | Symbol search in project | LSP (`workspaceSymbol`) |
77
- | Library usage patterns | Context Hub (`chub search`) |
78
-
79
- **Combined approach:** Use LSP to understand how the project currently uses a library, then use Context Hub to verify correct API usage and discover better patterns.
80
-
81
- ## Rules
82
-
83
- - Always search before implementing external API calls
84
- - Trust chub docs over training data for API specifics
85
- - Annotate gaps so future sessions benefit
86
- - Don't block on chub failures — fall back to best knowledge
87
- - Prefer `--json` flag for programmatic parsing in automated workflows