octocode-cli 1.3.1 → 1.5.1
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/README.md +193 -34
- package/out/chunks/chunk-7476PETK.js +309 -0
- package/out/chunks/chunk-CVNNNSMQ.js +26 -0
- package/out/chunks/chunk-OQBJTZWK.js +60 -0
- package/out/chunks/chunk-UCZCF3BQ.js +9 -0
- package/out/chunks/command-help-specs-JZXVSLZ5.js +8 -0
- package/out/chunks/commands-XBFPLHSQ.js +8 -0
- package/out/chunks/help-P7TCOYAJ.js +10 -0
- package/out/chunks/main-help-ULF5PAQY.js +10 -0
- package/out/chunks/prompts-5E6VKRX5.js +8 -0
- package/out/chunks/spinner-URV2OX6O.js +8 -0
- package/out/chunks/tool-command-M6VA7P2F.js +8 -0
- package/out/octocode-cli.js +1 -1
- package/package.json +5 -3
- package/skills/README.md +13 -1
- package/skills/agentic-flow-best-practices/SKILL.md +280 -0
- package/skills/agentic-flow-best-practices/references/agent-collaboration-patterns.md +75 -0
- package/skills/agentic-flow-best-practices/references/pr-review-agent-example.md +47 -0
- package/skills/agentic-flow-best-practices/references/resources.md +112 -0
- package/skills/octocode-chrome-devtools/README.md +541 -0
- package/skills/octocode-chrome-devtools/SKILL.md +197 -0
- package/skills/octocode-chrome-devtools/agents/openai.yaml +7 -0
- package/skills/octocode-chrome-devtools/references/CDP_AGENT_REFERENCE.md +401 -0
- package/skills/octocode-chrome-devtools/references/CHROME_FLAGS.md +234 -0
- package/skills/octocode-chrome-devtools/references/INTENTS.md +108 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_AUTH.md +179 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_AUTOMATION.md +214 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_DEBUG.md +329 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_ENVIRONMENT.md +237 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_INSPECT.md +263 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_STORAGE_CONSENT.md +214 -0
- package/skills/octocode-chrome-devtools/references/RECOVERY.md +39 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS.md +43 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_ASYNC_WORKERS.md +345 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_BROWSER.md +403 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_OBSERVE.md +275 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_SPECIAL.md +18 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-runner.mjs +503 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-sandbox.mjs +123 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-template.mjs +81 -0
- package/skills/octocode-chrome-devtools/scripts/octocode-chrome-devtools.vpn.example.json +8 -0
- package/skills/octocode-chrome-devtools/scripts/open-browser.mjs +362 -0
- package/skills/octocode-chrome-devtools/scripts/sourcemap-resolver.mjs +193 -0
- package/skills/octocode-chrome-devtools/scripts/undercover.mjs +226 -0
- package/skills/octocode-design/README.md +2 -2
- package/skills/octocode-documentation-writer/README.md +1 -1
- package/skills/octocode-engineer/README.md +1 -1
- package/skills/octocode-engineer/SKILL.md +4 -2
- package/skills/octocode-engineer/references/output-files.md +3 -3
- package/skills/octocode-engineer/scripts/run.js +136 -136
- package/skills/octocode-engineer/src/reporting/summary-md.test.ts +48 -0
- package/skills/octocode-engineer/src/reporting/summary-md.ts +43 -2
- package/skills/octocode-install/SKILL.md +1 -1
- package/skills/octocode-pull-request-reviewer/README.md +5 -5
- package/skills/octocode-pull-request-reviewer/SKILL.md +1 -1
- package/skills/octocode-research/AGENTS.md +1 -1
- package/skills/octocode-research/README.md +2 -2
- package/skills/octocode-research/docs/ARCHITECTURE.md +1 -1
- package/skills/octocode-research/scripts/server.js +191 -246
- package/skills/octocode-research/src/routes/github.ts +4 -21
- package/skills/octocode-research/src/routes/local.ts +4 -21
- package/skills/octocode-research/src/utils/fileContentTransform.ts +44 -0
- package/skills/octocode-search-skill/SKILL.md +245 -229
- package/skills/octocode-search-skill/references/agent-skills-guide.md +177 -0
- package/skills/octocode-search-skill/references/discovery-surfaces.md +162 -0
- package/skills/octocode-search-skill/references/fetch-and-create-locally.md +57 -0
- package/skills/octocode-search-skill/references/install-reference.md +130 -0
- package/skills/octocode-search-skill/references/references-template.md +27 -0
- package/skills/octocode-search-skill/references/references.md +62 -0
- package/skills/octocode-slides/README.md +307 -0
- package/skills/octocode-slides/SKILL.md +410 -0
- package/skills/octocode-slides/references/01-brief.md +156 -0
- package/skills/octocode-slides/references/02-research.md +149 -0
- package/skills/octocode-slides/references/03-outline.md +172 -0
- package/skills/octocode-slides/references/04-design.md +301 -0
- package/skills/octocode-slides/references/05-implementation.md +213 -0
- package/skills/octocode-slides/references/06-review.md +258 -0
- package/skills/octocode-slides/references/animation.md +281 -0
- package/skills/octocode-slides/references/design-system.md +316 -0
- package/skills/octocode-slides/references/html-templates.md +673 -0
- package/skills/octocode-slides/references/image-generation.md +448 -0
- package/skills/octocode-slides/references/resources.md +840 -0
- package/skills/octocode-slides/references/slide-rules.md +541 -0
- package/skills/octocode-slides/references/wireframes.md +727 -0
- package/skills/octocode-slides/scripts/animation.js +182 -0
- package/skills/octocode-slides/scripts/base.css +353 -0
- package/skills/octocode-slides/scripts/base.html +655 -0
- package/skills/octocode-slides/scripts/generate_image.py +221 -0
- package/skills/octocode-slides/scripts/navbridge.js +79 -0
- package/skills/octocode-slides/scripts/presenter.js +316 -0
- package/skills/octocode-slides/scripts/slide.html +248 -0
- package/skills/octocode-stats/SKILL.md +73 -0
- package/skills/octocode-stats/assets/template.html +1332 -0
- package/skills/octocode-stats/scripts/build_dashboard.mjs +407 -0
- package/assets/example.png +0 -0
- package/out/chunks/chunk-QCY7Q7YW.js +0 -389
- package/out/chunks/command-help-specs-CQ3RBLP6.js +0 -8
- package/out/chunks/commands-OCTZP2TO.js +0 -51
- package/out/chunks/help-XPXP46ZT.js +0 -10
- package/out/chunks/main-help-35HX2UDQ.js +0 -10
- package/out/chunks/tool-command-HOSMVLNK.js +0 -8
- package/skills/octocode-search-skill/INSTALL_REFERENCE.md +0 -112
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# CDP Automation And Scrape Intent Details
|
|
2
|
+
|
|
3
|
+
## automate
|
|
4
|
+
|
|
5
|
+
**Trigger phrases:** "automate", "do X", "click the button", "fill the form", "type into", "submit", "perform this flow", "go through the steps", "interact with", "navigate to X then Y"
|
|
6
|
+
|
|
7
|
+
**Purpose:** Drive the browser through a multi-step user flow. Use JS evaluation for interactions — it's faster and more reliable than CDP Input events for most cases.
|
|
8
|
+
|
|
9
|
+
**Domains:** `Page.enable`, `Runtime.enable`, `Network.enable` (add others as needed for observation)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
**Interaction toolkit — examples, not a fixed framework:**
|
|
13
|
+
|
|
14
|
+
Prefer `Runtime.evaluate` for app-level actions when you have stable selectors. Use `Input.*` from `CDP_AGENT_REFERENCE.md` when the task needs realistic pointer/keyboard behavior.
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
await cdp.send('Runtime.evaluate', {
|
|
18
|
+
expression: `document.querySelector(${JSON.stringify(selector)})?.click()`,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await cdp.send('Runtime.evaluate', {
|
|
22
|
+
expression: `(() => {
|
|
23
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
24
|
+
const set = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
|
|
25
|
+
set.call(el, ${JSON.stringify(value)});
|
|
26
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
27
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
28
|
+
})()`,
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
For robust waits, load only the helper you need from `SCRIPT_PATTERNS.md`: `waitForSelector with Actionability` or `waitForNetworkIdle`.
|
|
33
|
+
|
|
34
|
+
**Flow recipe:**
|
|
35
|
+
|
|
36
|
+
1. Enable `Page`, `Runtime`, and `Network` if observing requests.
|
|
37
|
+
2. Attach error/network listeners before navigation.
|
|
38
|
+
3. Navigate or attach to the current tab.
|
|
39
|
+
4. Wait for the next actionable element, perform one step, emit `[AUTOMATE]`.
|
|
40
|
+
5. Verify the expected outcome, then stop or continue to the next user-requested step.
|
|
41
|
+
|
|
42
|
+
**Output prefixes:** `[AUTOMATE]` `[FINDING]` `[ACTION]` `[METRIC]`
|
|
43
|
+
|
|
44
|
+
**`[AUTOMATE]` prefix:** Emit for each step executed: `[AUTOMATE] clicked "${selector}"`, `[AUTOMATE] typed into "${selector}"`, `[AUTOMATE] waited for "${selector}" — found after Nms`
|
|
45
|
+
|
|
46
|
+
**`[FINDING]` conditions:**
|
|
47
|
+
- `waitFor` times out → `[FINDING] ELEMENT_NOT_FOUND: "${selector}" not present after ${timeout}ms`
|
|
48
|
+
- Navigation results in error page → `[FINDING] NAVIGATION_FAILED: ${url}`
|
|
49
|
+
- Step throws exception → `[FINDING] STEP_FAILED: ${step} — ${error}`
|
|
50
|
+
|
|
51
|
+
**Step example:** navigate -> wait for `body` -> perform one user-requested action -> verify one concrete outcome. Keep each run small; combine with `debug` or `network` when you need evidence during the flow.
|
|
52
|
+
|
|
53
|
+
**Combine with:** `debug` to catch errors mid-flow, `network` to observe API calls during the flow, `screenshot` to capture state after each step.
|
|
54
|
+
|
|
55
|
+
## scrape
|
|
56
|
+
|
|
57
|
+
**Trigger phrases:** "scrape", "extract", "collect data", "pull content", "harvest", "get all X from the page", "list all Y", "export data"
|
|
58
|
+
|
|
59
|
+
**Purpose:** Extract structured data from the live DOM. Faster and more reliable than string parsing — queries the actual rendered state including JS-rendered content.
|
|
60
|
+
|
|
61
|
+
**Domains:** `Runtime.enable`, `DOM.enable`, `Page.enable` (if navigation needed)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
**Adaptive scrape recipe:**
|
|
65
|
+
|
|
66
|
+
Build the extractor around the page's real DOM. Do not cargo-cult selectors from this file. Prefer stable selectors in this order: `data-*` / test ids, semantic HTML, ARIA roles/labels, then text+link fallbacks.
|
|
67
|
+
|
|
68
|
+
For each scrape step, explicitly emit and persist reasoning:
|
|
69
|
+
- what hypothesis you tested
|
|
70
|
+
- what signal confirmed/rejected it
|
|
71
|
+
- what you will do next
|
|
72
|
+
|
|
73
|
+
Write this to both stdout (`[REASON]`) and session metadata (`cdp.addReasoningStep(...)`).
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
export async function run(cdp) {
|
|
77
|
+
const TARGET_URL = 'https://example.com';
|
|
78
|
+
cdp.addReasoningStep?.({
|
|
79
|
+
step: 'scrape-start',
|
|
80
|
+
hypothesis: 'Target page contains scrapeable items in rendered DOM',
|
|
81
|
+
action: `Open ${TARGET_URL} and inspect candidate selectors`,
|
|
82
|
+
result: 'pending',
|
|
83
|
+
nextAction: 'Navigate and extract top-level item list',
|
|
84
|
+
});
|
|
85
|
+
await cdp.send('Runtime.enable', {});
|
|
86
|
+
await cdp.send('Page.enable', {});
|
|
87
|
+
await cdp.send('Page.navigate', { url: TARGET_URL });
|
|
88
|
+
await new Promise(r => setTimeout(r, 1500)); // replace with waitForNetworkIdle when needed
|
|
89
|
+
|
|
90
|
+
const { result } = await cdp.send('Runtime.evaluate', {
|
|
91
|
+
expression: `JSON.stringify({
|
|
92
|
+
url: location.href,
|
|
93
|
+
title: document.title,
|
|
94
|
+
authWall: !!document.querySelector('input[type="password"]'),
|
|
95
|
+
items: [...document.querySelectorAll('article, [data-item], li a')]
|
|
96
|
+
.map(el => ({ text: el.innerText?.trim(), href: el.href || el.querySelector?.('a')?.href }))
|
|
97
|
+
.filter(x => x.text)
|
|
98
|
+
.slice(0, 100)
|
|
99
|
+
})`,
|
|
100
|
+
returnByValue: true,
|
|
101
|
+
});
|
|
102
|
+
const data = JSON.parse(result.value || '{}');
|
|
103
|
+
cdp.upsertResourceMap?.(`page:${data.url || TARGET_URL}`, {
|
|
104
|
+
type: 'page',
|
|
105
|
+
url: data.url || TARGET_URL,
|
|
106
|
+
title: data.title || null,
|
|
107
|
+
notes: 'Scrape target page',
|
|
108
|
+
tabId: cdp.targetInfo.id ?? null,
|
|
109
|
+
});
|
|
110
|
+
if (data.authWall) console.log('[FINDING] SCRAPE_REQUIRES_AUTH: login wall present');
|
|
111
|
+
if (!data.items?.length) console.log('[FINDING] SCRAPE_EMPTY: adapt selectors or wait for rendered content');
|
|
112
|
+
for (const item of data.items || []) console.log(`[SCRAPE] ${item.text} ${item.href || ''}`.trim());
|
|
113
|
+
console.log(`[REASON] extracted=${data.items?.length || 0} authWall=${Boolean(data.authWall)} next=${data.authWall ? 'switch-to-user-auth' : (data.items?.length ? 'refine-or-save' : 'adjust-selectors-or-wait')}`);
|
|
114
|
+
cdp.addReasoningStep?.({
|
|
115
|
+
step: 'scrape-evaluate',
|
|
116
|
+
hypothesis: 'Current selectors should return meaningful rows',
|
|
117
|
+
action: 'Evaluated extraction result and auth-wall signal',
|
|
118
|
+
result: `items=${data.items?.length || 0} authWall=${Boolean(data.authWall)}`,
|
|
119
|
+
nextAction: data.authWall ? 'Run user-auth/login intent and re-run scrape' : (data.items?.length ? 'Persist dataset and optionally paginate' : 'Adjust selectors/waits and rerun'),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Add consent handling, pagination, tables, file output, or authenticated reuse only when the task needs it. For browser APIs like `caches`, `navigator.storage`, or `indexedDB.databases()`, feature-detect before use and verify current docs if behavior matters.
|
|
125
|
+
|
|
126
|
+
**Multi-page loop (add only when pagination is confirmed):**
|
|
127
|
+
```js
|
|
128
|
+
while (hasNext.value) {
|
|
129
|
+
await cdp.send('Runtime.evaluate', {
|
|
130
|
+
expression: `document.querySelector('[class*="next"]:not([disabled]),[aria-label*="Next"]')?.click()` });
|
|
131
|
+
await new Promise(r => setTimeout(r, 1500)); // wait for JS render
|
|
132
|
+
// re-run extraction block and push to results array
|
|
133
|
+
const { result: nextCheck } = await cdp.send('Runtime.evaluate', {
|
|
134
|
+
expression: `!!(document.querySelector('[class*="next"]:not([disabled]),[aria-label*="Next"]'))`,
|
|
135
|
+
returnByValue: true });
|
|
136
|
+
if (!nextCheck.value) break;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Output prefixes:** `[SCRAPE]` `[METRIC]` `[FINDING]`
|
|
141
|
+
|
|
142
|
+
Use `[REASON]` for step-by-step decision logs in long scrape loops.
|
|
143
|
+
|
|
144
|
+
**`[FINDING]` conditions:**
|
|
145
|
+
- Zero items extracted → `[FINDING] SCRAPE_EMPTY: selector returned 0 elements — page may require interaction or login`
|
|
146
|
+
- Page requires login (login form detected) → `[FINDING] SCRAPE_REQUIRES_AUTH: login wall present`
|
|
147
|
+
- GDPR/consent dialog intercepted navigation → `[FINDING] SCRAPE_CONSENT_WALL: consent dialog detected — handle consent, then re-run extraction`
|
|
148
|
+
- Data appears JS-rendered but `readyState` check fails → `[FINDING] SCRAPE_NOT_READY: content may not be loaded yet`
|
|
149
|
+
- Pagination detected → `[METRIC] PAGINATION_DETECTED: next page exists — use multi-page loop`
|
|
150
|
+
|
|
151
|
+
**Agent loop usage:**
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
REASON → what data do I need? which page/selector?
|
|
155
|
+
SCRAPE → set TARGET_URL, run script → read [SCRAPE] + [FINDING] lines
|
|
156
|
+
EXPLAIN → emit [REASON] after each step and append metadata with cdp.addReasoningStep(...)
|
|
157
|
+
SAVE → results auto-saved to cdp.outputDir/scrape-results.json
|
|
158
|
+
MAP → save stable resources/selectors/tab roles via cdp.upsertResourceMap(...)
|
|
159
|
+
PAGINATE → if [METRIC] PAGINATION_DETECTED → add multi-page loop and re-run
|
|
160
|
+
AUTH → if [FINDING] SCRAPE_REQUIRES_AUTH → run login intent first, then re-run
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Stop when: `[SCRAPE] Total: N` is non-zero and no `[FINDING] SCRAPE_EMPTY` and no more pagination.
|
|
164
|
+
|
|
165
|
+
**Combine with:** `login` (authenticate first), `emulate` (scrape as mobile), `screenshot` (capture visual proof).
|
|
166
|
+
|
|
167
|
+
## live-page
|
|
168
|
+
|
|
169
|
+
**Trigger phrases:** "open page", "open this URL", "open and monitor", "watch this page", "open browser and wait", "live check", "open and inspect", "browse with monitoring", "open it and I'll tell you what to check"
|
|
170
|
+
|
|
171
|
+
**Purpose:** Open visible Chrome, let the user drive the page, then attach for focused checks without reloads.
|
|
172
|
+
|
|
173
|
+
**Domains required:** None for the open step. Each on-demand check enables only what it needs.
|
|
174
|
+
|
|
175
|
+
**Rules:** omit `--headless`; use `--keep-tab --target-url <pattern>`; do not call `Page.navigate` in on-demand scripts.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
node <skill-dir>/scripts/open-browser.mjs --url "<url>" [--port 9222]
|
|
179
|
+
node <skill-dir>/scripts/cdp-sandbox.mjs "$TMPDIR/cdp-<task>.mjs" \
|
|
180
|
+
--target-url "<url-pattern>" --keep-tab \
|
|
181
|
+
> "$TMPDIR/cdp-output-<task>.txt" 2>&1
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Tell the user Chrome is open, then wait for their check request. Write a focused script for that request and read current page state via `Runtime.evaluate`; do not navigate.
|
|
185
|
+
|
|
186
|
+
**Minimal on-demand sketch:**
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
export async function run(cdp) {
|
|
190
|
+
const { result: urlRes } = await cdp.send('Runtime.evaluate', {
|
|
191
|
+
expression: 'JSON.stringify({ url: location.href, title: document.title })',
|
|
192
|
+
returnByValue: true,
|
|
193
|
+
});
|
|
194
|
+
console.log(`[FINDING] PAGE_STATE: ${urlRes.value}`);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
| User asks | Add to script |
|
|
199
|
+
|---|---|
|
|
200
|
+
| Screenshot | `Page.captureScreenshot` |
|
|
201
|
+
| Current DOM / elements | `DOM.enable` → `DOM.getDocument` → `Runtime.evaluate` |
|
|
202
|
+
| Cookies | `Network.enable` → `Network.getAllCookies` (names only, never values) |
|
|
203
|
+
| localStorage / sessionStorage | `Runtime.evaluate` with `Object.keys(localStorage)` |
|
|
204
|
+
| Console errors so far | `Runtime.evaluate` → `window.__cdpErrors` if pre-patched, else check DOM |
|
|
205
|
+
| Page performance | `Runtime.evaluate` → `JSON.stringify(performance.getEntriesByType('navigation'))` |
|
|
206
|
+
| Network calls already made | `Runtime.evaluate` → `performance.getEntriesByType('resource')` |
|
|
207
|
+
|
|
208
|
+
> **Note:** Listeners attached after page load miss past events. Use `Runtime.evaluate` to read state already on the page (performance entries, window globals, DOM) instead of waiting for new events.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
node <skill-dir>/scripts/open-browser.mjs --port 9222 --cleanup
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Output prefixes:** `[FINDING]` `[DOM]` `[SCREENSHOT]` `[STORAGE]` `[SECURITY]` `[METRIC]`
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# CDP Debug Intent Details
|
|
2
|
+
|
|
3
|
+
## debug
|
|
4
|
+
|
|
5
|
+
**Trigger phrases:** "debug", "what's wrong", "why is X broken", "investigate", "fix this", "something is not working", "help me understand", "agent loop", "observe", "what happened", "trace this", "why does X fail"
|
|
6
|
+
|
|
7
|
+
**Purpose:** Designed for iterative **Reason → Observe → Act** loops by developers and agents. Captures maximum signal in a single pass — errors, failed requests, DOM state, and source locations — so the next action is usually clear.
|
|
8
|
+
|
|
9
|
+
**Domains:** `Network.enable`, `Runtime.enable`, `Log.enable`, `DOM.enable`, `Page.enable`, `Debugger.enable` (auto-enabled by `createSourceMapResolver` — enrich minified stack frames)
|
|
10
|
+
|
|
11
|
+
**Key events/methods:**
|
|
12
|
+
- `Runtime.exceptionThrown` → full stack trace with `url:line:col:functionName`
|
|
13
|
+
- `Runtime.consoleAPICalled` → filter for `error` and `warn` types only
|
|
14
|
+
- `Log.entryAdded` → all `error`/`warning` entries with source
|
|
15
|
+
- `Network.requestWillBeSent` → capture URL + method
|
|
16
|
+
- `Network.responseReceived` → flag status ≥ 400
|
|
17
|
+
- `Network.loadingFailed` → blocked/failed requests
|
|
18
|
+
- `Network.getRequestPostData(requestId)` → POST body on failed requests (call inside response handler)
|
|
19
|
+
- `DOM.getDocument(depth:1)` → confirm page loaded (root title, body present)
|
|
20
|
+
- `Runtime.evaluate` → `document.title`, `document.readyState`, `document.querySelectorAll('.error, [data-error], [aria-invalid]').length`
|
|
21
|
+
|
|
22
|
+
**Output prefixes:** `[DEBUG]` `[EXCEPTION]` `[EXCEPTION_LOCATION]` `[CONSOLE:ERROR]` `[NETWORK_ERROR]` `[NETWORK_FAILED]` `[DOM]` `[ACTION]` `[METRIC]` `[FINDING]`
|
|
23
|
+
|
|
24
|
+
**Additional prefix — `[ACTION]`:** Emitted when the agent can determine a concrete next step from a finding. Format: `[ACTION] <verb> <target> — <reason>`. Example: `[ACTION] search "TypeError: Cannot read" in localSearchCode — exception at checkout.js:42`.
|
|
25
|
+
|
|
26
|
+
**Agent loop contract:**
|
|
27
|
+
|
|
28
|
+
Each debug run produces one **OBSERVE block** and one **ACT block**:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
[DEBUG] === OBSERVE ===
|
|
32
|
+
[DEBUG] Page: <title> | readyState: <state>
|
|
33
|
+
[DEBUG] Exceptions: N Console errors: N Network errors: N Blocked: N
|
|
34
|
+
... (all [EXCEPTION], [NETWORK_ERROR], [NETWORK_FAILED] lines) ...
|
|
35
|
+
[DEBUG] DOM error indicators: N elements with .error / [aria-invalid]
|
|
36
|
+
|
|
37
|
+
[DEBUG] === ACT ===
|
|
38
|
+
[ACTION] <highest-priority action based on findings>
|
|
39
|
+
[ACTION] <second action if applicable>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The ACT block is what the agent (or developer) should do next. Emit it when there is a concrete next move; if findings are zero, one concise `[ACTION] No errors found — try interacting with the page and re-run` line is enough.
|
|
43
|
+
|
|
44
|
+
**`[FINDING]` conditions to emit (in priority order):**
|
|
45
|
+
|
|
46
|
+
| Priority | Condition | Finding |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| 1 | Uncaught exception with stack trace | `[FINDING] EXCEPTION: ${description} at ${url}:${line}` |
|
|
49
|
+
| 2 | Network request ≥ 400 | `[FINDING] HTTP_ERROR: ${status} ${method} ${url}` |
|
|
50
|
+
| 3 | Request blocked / failed | `[FINDING] BLOCKED: ${url} — ${errorText}` |
|
|
51
|
+
| 4 | `console.error` message | `[FINDING] CONSOLE_ERROR: ${message}` |
|
|
52
|
+
| 5 | DOM contains `.error`, `[aria-invalid]`, `[data-error]` elements | `[FINDING] DOM_ERROR_STATE: ${n} error-state elements visible` |
|
|
53
|
+
| 6 | `document.readyState !== 'complete'` after load wait | `[FINDING] PAGE_NOT_READY: readyState=${state}` |
|
|
54
|
+
| 7 | Zero network requests after navigation | `[FINDING] NO_REQUESTS: page may be offline or blocked` |
|
|
55
|
+
|
|
56
|
+
**`[ACTION]` emit rules:**
|
|
57
|
+
|
|
58
|
+
- For every `[FINDING] EXCEPTION` → `[ACTION] localSearchCode "${functionName}" to find source at ${url}:${line}`
|
|
59
|
+
- For every `[FINDING] HTTP_ERROR` → `[ACTION] check handler for ${method} ${url} — returned ${status}`
|
|
60
|
+
- For every `[FINDING] BLOCKED` → `[ACTION] check CORS / network config for ${url}`
|
|
61
|
+
- For every `[FINDING] CONSOLE_ERROR` → `[ACTION] search "${first 60 chars of message}" in localSearchCode`
|
|
62
|
+
- For every `[FINDING] DOM_ERROR_STATE` → `[ACTION] inspect DOM for .error / [aria-invalid] — user-visible errors present`
|
|
63
|
+
- If zero findings → `[ACTION] No errors detected — interact with the page (click, submit form) and re-run debug`
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
**Adaptive debug recipe:**
|
|
67
|
+
|
|
68
|
+
Use this as a shape, not a fixed script. Start from `SCRIPT_PATTERNS.md` -> `Network Console`, then add only the extra probes needed for the question.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
export async function run(cdp) {
|
|
72
|
+
const TARGET_URL = 'https://example.com';
|
|
73
|
+
await cdp.send('Network.enable', {});
|
|
74
|
+
await cdp.send('Runtime.enable', {});
|
|
75
|
+
await cdp.send('Log.enable', {});
|
|
76
|
+
await cdp.send('Page.enable', {});
|
|
77
|
+
|
|
78
|
+
const requests = new Map();
|
|
79
|
+
const findings = [];
|
|
80
|
+
cdp.on('Network.requestWillBeSent', ({ requestId, request }) =>
|
|
81
|
+
requests.set(requestId, { method: request.method, url: request.url }));
|
|
82
|
+
cdp.on('Network.responseReceived', ({ requestId, response }) => {
|
|
83
|
+
const r = requests.get(requestId);
|
|
84
|
+
if (r && response.status >= 400) findings.push(`HTTP ${response.status} ${r.method} ${r.url}`);
|
|
85
|
+
});
|
|
86
|
+
cdp.on('Runtime.exceptionThrown', ({ exceptionDetails }) =>
|
|
87
|
+
findings.push(exceptionDetails.exception?.description ?? exceptionDetails.text));
|
|
88
|
+
|
|
89
|
+
await cdp.send('Page.navigate', { url: TARGET_URL });
|
|
90
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
91
|
+
|
|
92
|
+
const { result } = await cdp.send('Runtime.evaluate', {
|
|
93
|
+
expression: `JSON.stringify({
|
|
94
|
+
url: location.href,
|
|
95
|
+
title: document.title,
|
|
96
|
+
readyState: document.readyState,
|
|
97
|
+
errorEls: document.querySelectorAll('.error,[aria-invalid="true"],[data-error]').length
|
|
98
|
+
})`,
|
|
99
|
+
returnByValue: true,
|
|
100
|
+
});
|
|
101
|
+
console.log(`[DEBUG] page=${result.value}`);
|
|
102
|
+
for (const f of findings) console.log(`[FINDING] ${f}`);
|
|
103
|
+
if (!findings.length) console.log('[ACTION] No obvious browser errors; interact and rerun if the bug is flow-dependent');
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Add sourcemap, POST body, DOM, or local source tracing only when those signals would change the next action.
|
|
108
|
+
|
|
109
|
+
**Agent loop usage:**
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
REASON → what do I expect to see?
|
|
113
|
+
OBSERVE → run debug script → read [FINDING] + [EXCEPTION] + [NETWORK_ERROR] lines
|
|
114
|
+
ACT → follow [ACTION] lines: localSearchCode, check handler, fix CORS, inspect DOM
|
|
115
|
+
REPEAT → re-run after fix to confirm errors cleared
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Stop the loop when: zero `[FINDING]` lines **and** `readyState: complete` **and** `DOM error indicators: 0`.
|
|
119
|
+
|
|
120
|
+
## network
|
|
121
|
+
|
|
122
|
+
**Trigger phrases:** "check network", "API calls", "requests", "4xx", "5xx", "traffic", "what's being called", "HTTP errors"
|
|
123
|
+
|
|
124
|
+
**Domains:** `Network.enable`
|
|
125
|
+
|
|
126
|
+
**Key events/methods:**
|
|
127
|
+
- `Network.requestWillBeSent` → capture URL, method, request headers
|
|
128
|
+
- `Network.responseReceived` → capture status code
|
|
129
|
+
- `Network.loadingFailed` → capture blocked/failed requests
|
|
130
|
+
- `Network.getRequestPostData(requestId)` → capture actual POST body (call inside `responseReceived`)
|
|
131
|
+
- `Network.getResponseBody(requestId)` → capture response body when needed
|
|
132
|
+
|
|
133
|
+
**Output prefixes:** `[NETWORK]` `[NETWORK_ERROR]` `[NETWORK_FAILED]` `[METRIC]`
|
|
134
|
+
|
|
135
|
+
**`[FINDING]` conditions to emit:**
|
|
136
|
+
- Status ≥ 400 → `[FINDING] HTTP_ERROR: ${status} ${method} ${url}`
|
|
137
|
+
- `loadingFailed` → `[FINDING] REQUEST_BLOCKED: ${url} — ${errorText}`
|
|
138
|
+
- POST body contains `token`, `password`, `secret`, `key` → `[FINDING] SENSITIVE_DATA_IN_REQUEST: ${url}`
|
|
139
|
+
- Request to unexpected third-party domain → `[FINDING] THIRD_PARTY_REQUEST: ${url}`
|
|
140
|
+
|
|
141
|
+
**Pattern:** Use `Network Console` pattern from `SCRIPT_PATTERNS.md`, remove console section if not needed.
|
|
142
|
+
|
|
143
|
+
## console
|
|
144
|
+
|
|
145
|
+
**Trigger phrases:** "console errors", "JS errors", "exceptions", "crashes", "what's broken", "runtime errors"
|
|
146
|
+
|
|
147
|
+
**Domains:** `Runtime.enable`, `Log.enable`
|
|
148
|
+
|
|
149
|
+
**Key events/methods:**
|
|
150
|
+
- `Runtime.consoleAPICalled` → all `console.*` calls
|
|
151
|
+
- `Runtime.exceptionThrown` → uncaught exceptions with stack trace
|
|
152
|
+
- `Log.entryAdded` → browser-level log entries (network errors surfaced here too)
|
|
153
|
+
|
|
154
|
+
**Output prefixes:** `[CONSOLE:ERROR]` `[CONSOLE:WARN]` `[EXCEPTION]` `[EXCEPTION_LOCATION]` `[LOG:ERROR]`
|
|
155
|
+
|
|
156
|
+
**`[FINDING]` conditions to emit:**
|
|
157
|
+
- Any `[EXCEPTION]` → `[FINDING] UNCAUGHT_EXCEPTION: ${description}`
|
|
158
|
+
- `[CONSOLE:ERROR]` count > 0 → `[FINDING] CONSOLE_ERRORS: ${count} errors found`
|
|
159
|
+
- `[LOG:ERROR]` from `security` source → `[FINDING] SECURITY_LOG_ERROR: ${text}`
|
|
160
|
+
- Stack frame points to third-party URL → `[FINDING] THIRD_PARTY_EXCEPTION: ${url}`
|
|
161
|
+
|
|
162
|
+
**Pattern:** Use `Network Console` pattern from `SCRIPT_PATTERNS.md`, remove network section if not needed.
|
|
163
|
+
|
|
164
|
+
## performance
|
|
165
|
+
|
|
166
|
+
**Trigger phrases:** "slow", "performance", "metrics", "long tasks", "layout thrashing", "FPS", "script duration", "rendering", "CPU"
|
|
167
|
+
|
|
168
|
+
**Domains:** `Performance.enable`, `Runtime.enable`
|
|
169
|
+
|
|
170
|
+
**Key events/methods:**
|
|
171
|
+
- `Performance.getMetrics` → JSHeapUsedSize, TaskDuration, ScriptDuration, LayoutCount, RecalcStyleCount, Nodes
|
|
172
|
+
- `Tracing.start` / `Tracing.end` + `Tracing.dataCollected` → full timeline trace (use for deep profiling)
|
|
173
|
+
|
|
174
|
+
**Output prefixes:** `[PERFORMANCE]` `[METRIC]` `[FINDING]`
|
|
175
|
+
|
|
176
|
+
**`[FINDING]` conditions to emit:**
|
|
177
|
+
- `JSHeapUsedSize > 50MB` → `[FINDING] HIGH_MEMORY: JS heap ${MB}MB`
|
|
178
|
+
- `ScriptDuration > 2s` → `[FINDING] SLOW_SCRIPTS: ${s}s script execution`
|
|
179
|
+
- `LayoutCount > 20` → `[FINDING] LAYOUT_THRASHING: ${n} forced layouts`
|
|
180
|
+
- `RecalcStyleCount > 30` → `[FINDING] STYLE_RECALC: ${n} style recalculations`
|
|
181
|
+
- `Nodes > 1500` → `[FINDING] LARGE_DOM: ${n} DOM nodes`
|
|
182
|
+
|
|
183
|
+
**Pattern:** Use `Performance Audit` pattern from `SCRIPT_PATTERNS.md`.
|
|
184
|
+
|
|
185
|
+
**Cold-load accuracy — do this before navigating for first-visit metrics:**
|
|
186
|
+
```js
|
|
187
|
+
// Ensure cold-cache metrics (real TTFB, resource timing, FCP)
|
|
188
|
+
await cdp.send('Network.clearBrowserCache', {});
|
|
189
|
+
await cdp.send('Network.clearBrowserCookies', {});
|
|
190
|
+
// Then navigate — DNS/TCP/TLS/FCP will now reflect a real first visit
|
|
191
|
+
```
|
|
192
|
+
Without this, a second navigation reuses the HTTP cache and connection pool, making DNS = 0ms, TCP = 0ms, all resource durations = 0ms, and FCP = null.
|
|
193
|
+
|
|
194
|
+
**FCP — two approaches (pick one):**
|
|
195
|
+
|
|
196
|
+
*Approach A (recommended) — read from the page's own performance timeline after load:*
|
|
197
|
+
```js
|
|
198
|
+
const { result } = await cdp.send('Runtime.evaluate', {
|
|
199
|
+
expression: `JSON.stringify({
|
|
200
|
+
fcp: performance.getEntriesByType('paint').find(e => e.name === 'first-contentful-paint')?.startTime,
|
|
201
|
+
lcp: performance.getEntriesByType('largest-contentful-paint').slice(-1)[0]?.startTime,
|
|
202
|
+
})`,
|
|
203
|
+
returnByValue: true,
|
|
204
|
+
});
|
|
205
|
+
const { fcp, lcp } = JSON.parse(result.value || '{}');
|
|
206
|
+
if (fcp != null) console.log(`[METRIC] FCP: ${Math.round(fcp)}ms`);
|
|
207
|
+
if (lcp != null) console.log(`[METRIC] LCP: ${Math.round(lcp)}ms`);
|
|
208
|
+
// Values are already in ms from navigationStart — no reference-frame conversion needed
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
*Approach B — CDP lifecycle events (needs `Page.setLifecycleEventsEnabled`):*
|
|
212
|
+
```js
|
|
213
|
+
await cdp.send('Page.enable', {});
|
|
214
|
+
await cdp.send('Page.setLifecycleEventsEnabled', { enabled: true }); // required!
|
|
215
|
+
let navStartTs = null;
|
|
216
|
+
cdp.on('Page.lifecycleEvent', ({ name, timestamp }) => {
|
|
217
|
+
if (name === 'commit') navStartTs = timestamp;
|
|
218
|
+
if (name === 'firstContentfulPaint' && navStartTs !== null)
|
|
219
|
+
console.log(`[METRIC] FCP: ${Math.round((timestamp - navStartTs) * 1000)}ms`);
|
|
220
|
+
});
|
|
221
|
+
// Attach BEFORE Page.navigate; both timestamps use CDP seconds (same reference frame).
|
|
222
|
+
// Do NOT use performance.now() — Node process time has a different origin than CDP timestamps.
|
|
223
|
+
// Caveat: 'commit' may fire before listener registration on a reused tab; prefer Approach A.
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## memory
|
|
227
|
+
|
|
228
|
+
**Trigger phrases:** "memory leak", "heap", "detached nodes", "retained objects", "growing memory", "GC pressure"
|
|
229
|
+
|
|
230
|
+
**Domains:** `HeapProfiler.enable`
|
|
231
|
+
|
|
232
|
+
**Key events/methods:**
|
|
233
|
+
- `HeapProfiler.takeHeapSnapshot` → fires `HeapProfiler.addHeapSnapshotChunk` events → parse JSON
|
|
234
|
+
- Fields: `snapshot.strings`, `snapshot.nodes` (node_fields: type, name, id, self_size, edge_count, detachedness)
|
|
235
|
+
- Detachedness field value `1` = detached DOM node
|
|
236
|
+
|
|
237
|
+
**Output prefixes:** `[PERFORMANCE]` `[METRIC]` `[FINDING]`
|
|
238
|
+
|
|
239
|
+
**`[FINDING]` conditions to emit:**
|
|
240
|
+
- Any type retains > 5MB → `[FINDING] HIGH_RETENTION: "${name}" ${MB}MB`
|
|
241
|
+
- Detached DOM nodes > 50 → `[FINDING] DETACHED_NODES: ${n} detached nodes`
|
|
242
|
+
- `(closure)` or `Array` in top-10 retained types with high size → `[FINDING] POSSIBLE_CLOSURE_LEAK`
|
|
243
|
+
|
|
244
|
+
**Pattern:** Use `Heap Memory Audit` pattern from `SCRIPT_PATTERNS.md`.
|
|
245
|
+
|
|
246
|
+
## dom
|
|
247
|
+
|
|
248
|
+
**Trigger phrases:** "DOM", "elements", "structure", "HTML", "rendering issues", "layout", "selectors", "what's on the page"
|
|
249
|
+
|
|
250
|
+
**Domains:** `DOM.enable`, `Runtime.enable`
|
|
251
|
+
|
|
252
|
+
**Key events/methods:**
|
|
253
|
+
- `DOM.getDocument(depth:2)` → root node tree
|
|
254
|
+
- `Runtime.evaluate` → arbitrary DOM queries (`querySelectorAll`, `innerHTML`, `textContent`)
|
|
255
|
+
|
|
256
|
+
**Output prefixes:** `[DOM]` `[FINDING]` `[METRIC]`
|
|
257
|
+
|
|
258
|
+
**`[FINDING]` conditions to emit:**
|
|
259
|
+
- Total elements > 1500 → `[FINDING] LARGE_DOM: ${n} elements` *(suggested default — complex apps may legitimately exceed this)*
|
|
260
|
+
- `img:not([alt])` count > 0 → `[FINDING] ACCESSIBILITY: ${n} images missing alt`
|
|
261
|
+
- `input:not([aria-label]):not([id])` > 0 → `[FINDING] ACCESSIBILITY: ${n} inputs missing label`
|
|
262
|
+
- `button:empty` count > 0 → `[FINDING] ACCESSIBILITY: ${n} empty buttons`
|
|
263
|
+
- `script:not([src])` count > 0 → `[FINDING] INLINE_SCRIPTS: ${n} inline scripts present`
|
|
264
|
+
|
|
265
|
+
**Pattern:** Use `DOM Accessibility Audit` pattern from `SCRIPT_PATTERNS.md`.
|
|
266
|
+
|
|
267
|
+
## css-coverage
|
|
268
|
+
|
|
269
|
+
**Trigger phrases:** "unused CSS", "CSS coverage", "dead styles", "remove CSS", "style bloat"
|
|
270
|
+
|
|
271
|
+
**Domains:** `DOM.enable`, `CSS.enable` (**DOM first**)
|
|
272
|
+
|
|
273
|
+
**Key events/methods:**
|
|
274
|
+
- `CSS.startRuleUsageTracking` → start before navigation
|
|
275
|
+
- `CSS.stopRuleUsageTracking` → returns `ruleUsage[]` with `{styleSheetId, used: bool}`
|
|
276
|
+
- `CSS.getStyleSheetText(styleSheetId)` → get actual CSS text for unused rules
|
|
277
|
+
|
|
278
|
+
**Output prefixes:** `[CSS]` `[METRIC]` `[FINDING]`
|
|
279
|
+
|
|
280
|
+
**`[FINDING]` conditions to emit:**
|
|
281
|
+
- Unused rules > 50% of total → `[FINDING] CSS_BLOAT: ${pct}% unused CSS rules` *(adjust % for your app's load pattern)*
|
|
282
|
+
- Single stylesheet with > 200 unused rules → `[FINDING] LARGE_UNUSED_SHEET: ${url}`
|
|
283
|
+
|
|
284
|
+
## js-coverage
|
|
285
|
+
|
|
286
|
+
**Trigger phrases:** "unused JS", "dead code", "JS coverage", "which functions run", "code not executed"
|
|
287
|
+
|
|
288
|
+
**Domains:** `Profiler.enable`, `Debugger.enable` (for source map resolution — enabled automatically by `createSourceMapResolver`)
|
|
289
|
+
|
|
290
|
+
**Key events/methods:**
|
|
291
|
+
- `Profiler.startPreciseCoverage(detailed:true, allowTriggeredUpdates:true)` → start before navigation
|
|
292
|
+
- `Profiler.takePreciseCoverage` → returns `result[]` → `{url, scriptId, functions[{functionName, ranges[{count, startOffset}]}]}`
|
|
293
|
+
|
|
294
|
+
**Output prefixes:** `[METRIC]` `[FINDING]` `[SOURCEMAP]`
|
|
295
|
+
|
|
296
|
+
**`[FINDING]` conditions to emit:**
|
|
297
|
+
- Function with `count === 0` in a non-vendor file → `[FINDING] DEAD_CODE: ${displayName} in ${loc} never called`
|
|
298
|
+
- Script where > 80% of functions have count = 0 → `[FINDING] DEAD_SCRIPT: ${url} mostly unused`
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
**Minified bundle caveat — use source map resolver when the bundle is minified:**
|
|
302
|
+
|
|
303
|
+
Production bundles often mangle function names to 1-2 characters. Create `createSourceMapResolver(cdp)` before navigation, call `resolver.settle()` before analysis, and skip unresolved mangled names instead of reporting noisy dead-code findings.
|
|
304
|
+
|
|
305
|
+
**Adaptive coverage sketch:**
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
await cdp.send('Profiler.enable', {});
|
|
309
|
+
await cdp.send('Profiler.startPreciseCoverage', { detailed: true, allowTriggeredUpdates: true });
|
|
310
|
+
// Navigate/interact, then:
|
|
311
|
+
const { result } = await cdp.send('Profiler.takePreciseCoverage', {});
|
|
312
|
+
await cdp.send('Profiler.stopPreciseCoverage', {});
|
|
313
|
+
await cdp.send('Profiler.disable', {});
|
|
314
|
+
|
|
315
|
+
for (const script of result) {
|
|
316
|
+
if (!script.url || /node_modules|\/vendor\//i.test(script.url)) continue;
|
|
317
|
+
const unused = script.functions.filter(fn => fn.ranges.every(r => r.count === 0));
|
|
318
|
+
if (unused.length / Math.max(1, script.functions.length) > 0.8)
|
|
319
|
+
console.log(`[FINDING] DEAD_SCRIPT: ${script.url.split('/').pop()} mostly unused`);
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Report a named function only when it has a readable name or resolves through the source map. Otherwise emit a file-level metric, not dozens of mangled symbols.
|
|
324
|
+
|
|
325
|
+
**Always stop coverage when done** (already included in the pre-built script above):
|
|
326
|
+
```js
|
|
327
|
+
await cdp.send('Profiler.stopPreciseCoverage', {});
|
|
328
|
+
await cdp.send('Profiler.disable', {});
|
|
329
|
+
```
|