octocode-cli 1.3.0 → 1.5.0

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.
Files changed (106) hide show
  1. package/README.md +129 -28
  2. package/out/chunks/chunk-7476PETK.js +309 -0
  3. package/out/chunks/chunk-CVNNNSMQ.js +26 -0
  4. package/out/chunks/chunk-OQBJTZWK.js +60 -0
  5. package/out/chunks/chunk-UCZCF3BQ.js +9 -0
  6. package/out/chunks/command-help-specs-JZXVSLZ5.js +8 -0
  7. package/out/chunks/commands-XBFPLHSQ.js +8 -0
  8. package/out/chunks/help-P7TCOYAJ.js +10 -0
  9. package/out/chunks/main-help-ULF5PAQY.js +10 -0
  10. package/out/chunks/prompts-5E6VKRX5.js +8 -0
  11. package/out/chunks/spinner-URV2OX6O.js +8 -0
  12. package/out/chunks/tool-command-M6VA7P2F.js +8 -0
  13. package/out/octocode-cli.js +1 -1
  14. package/package.json +5 -3
  15. package/skills/README.md +60 -58
  16. package/skills/agentic-flow-best-practices/SKILL.md +280 -0
  17. package/skills/agentic-flow-best-practices/references/agent-collaboration-patterns.md +75 -0
  18. package/skills/agentic-flow-best-practices/references/pr-review-agent-example.md +47 -0
  19. package/skills/agentic-flow-best-practices/references/resources.md +112 -0
  20. package/skills/octocode-brainstorming/.env.example +11 -0
  21. package/skills/octocode-brainstorming/SKILL.md +262 -0
  22. package/skills/octocode-brainstorming/scripts/tavily-search.mjs +138 -0
  23. package/skills/octocode-chrome-devtools/README.md +541 -0
  24. package/skills/octocode-chrome-devtools/SKILL.md +197 -0
  25. package/skills/octocode-chrome-devtools/agents/openai.yaml +7 -0
  26. package/skills/octocode-chrome-devtools/references/CDP_AGENT_REFERENCE.md +401 -0
  27. package/skills/octocode-chrome-devtools/references/CHROME_FLAGS.md +234 -0
  28. package/skills/octocode-chrome-devtools/references/INTENTS.md +108 -0
  29. package/skills/octocode-chrome-devtools/references/INTENTS_AUTH.md +179 -0
  30. package/skills/octocode-chrome-devtools/references/INTENTS_AUTOMATION.md +214 -0
  31. package/skills/octocode-chrome-devtools/references/INTENTS_DEBUG.md +329 -0
  32. package/skills/octocode-chrome-devtools/references/INTENTS_ENVIRONMENT.md +237 -0
  33. package/skills/octocode-chrome-devtools/references/INTENTS_INSPECT.md +263 -0
  34. package/skills/octocode-chrome-devtools/references/INTENTS_STORAGE_CONSENT.md +214 -0
  35. package/skills/octocode-chrome-devtools/references/RECOVERY.md +39 -0
  36. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS.md +43 -0
  37. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_ASYNC_WORKERS.md +345 -0
  38. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_BROWSER.md +403 -0
  39. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_OBSERVE.md +275 -0
  40. package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_SPECIAL.md +18 -0
  41. package/skills/octocode-chrome-devtools/scripts/cdp-runner.mjs +503 -0
  42. package/skills/octocode-chrome-devtools/scripts/cdp-sandbox.mjs +123 -0
  43. package/skills/octocode-chrome-devtools/scripts/cdp-template.mjs +81 -0
  44. package/skills/octocode-chrome-devtools/scripts/octocode-chrome-devtools.vpn.example.json +8 -0
  45. package/skills/octocode-chrome-devtools/scripts/open-browser.mjs +362 -0
  46. package/skills/octocode-chrome-devtools/scripts/sourcemap-resolver.mjs +193 -0
  47. package/skills/octocode-chrome-devtools/scripts/undercover.mjs +226 -0
  48. package/skills/octocode-design/README.md +2 -2
  49. package/skills/octocode-documentation-writer/README.md +1 -1
  50. package/skills/octocode-engineer/README.md +1 -1
  51. package/skills/octocode-engineer/SKILL.md +137 -306
  52. package/skills/octocode-engineer/references/cli-reference.md +13 -0
  53. package/skills/octocode-engineer/references/output-files.md +3 -3
  54. package/skills/octocode-engineer/scripts/run.js +146 -146
  55. package/skills/octocode-engineer/src/pipeline/main.ts +1 -17
  56. package/skills/octocode-engineer/src/pipeline/progress.ts +4 -0
  57. package/skills/octocode-engineer/src/reporting/summary-md.test.ts +48 -0
  58. package/skills/octocode-engineer/src/reporting/summary-md.ts +43 -2
  59. package/skills/octocode-install/SKILL.md +1 -1
  60. package/skills/octocode-pull-request-reviewer/README.md +5 -5
  61. package/skills/octocode-pull-request-reviewer/SKILL.md +1 -1
  62. package/skills/octocode-research/AGENTS.md +1 -1
  63. package/skills/octocode-research/README.md +2 -2
  64. package/skills/octocode-research/docs/ARCHITECTURE.md +1 -1
  65. package/skills/octocode-research/scripts/server.js +184 -239
  66. package/skills/octocode-research/src/routes/github.ts +4 -21
  67. package/skills/octocode-research/src/routes/local.ts +4 -21
  68. package/skills/octocode-research/src/utils/fileContentTransform.ts +44 -0
  69. package/skills/octocode-search-skill/SKILL.md +337 -0
  70. package/skills/octocode-search-skill/references/agent-skills-guide.md +177 -0
  71. package/skills/octocode-search-skill/references/discovery-surfaces.md +162 -0
  72. package/skills/octocode-search-skill/references/fetch-and-create-locally.md +57 -0
  73. package/skills/octocode-search-skill/references/install-reference.md +130 -0
  74. package/skills/octocode-search-skill/references/references-template.md +27 -0
  75. package/skills/octocode-search-skill/references/references.md +62 -0
  76. package/skills/octocode-slides/README.md +307 -0
  77. package/skills/octocode-slides/SKILL.md +410 -0
  78. package/skills/octocode-slides/references/01-brief.md +156 -0
  79. package/skills/octocode-slides/references/02-research.md +149 -0
  80. package/skills/octocode-slides/references/03-outline.md +172 -0
  81. package/skills/octocode-slides/references/04-design.md +301 -0
  82. package/skills/octocode-slides/references/05-implementation.md +213 -0
  83. package/skills/octocode-slides/references/06-review.md +258 -0
  84. package/skills/octocode-slides/references/animation.md +281 -0
  85. package/skills/octocode-slides/references/design-system.md +316 -0
  86. package/skills/octocode-slides/references/html-templates.md +673 -0
  87. package/skills/octocode-slides/references/image-generation.md +448 -0
  88. package/skills/octocode-slides/references/resources.md +840 -0
  89. package/skills/octocode-slides/references/slide-rules.md +541 -0
  90. package/skills/octocode-slides/references/wireframes.md +727 -0
  91. package/skills/octocode-slides/scripts/animation.js +182 -0
  92. package/skills/octocode-slides/scripts/base.css +353 -0
  93. package/skills/octocode-slides/scripts/base.html +655 -0
  94. package/skills/octocode-slides/scripts/generate_image.py +221 -0
  95. package/skills/octocode-slides/scripts/navbridge.js +79 -0
  96. package/skills/octocode-slides/scripts/presenter.js +316 -0
  97. package/skills/octocode-slides/scripts/slide.html +248 -0
  98. package/skills/octocode-stats/SKILL.md +73 -0
  99. package/skills/octocode-stats/assets/template.html +1332 -0
  100. package/skills/octocode-stats/scripts/build_dashboard.mjs +407 -0
  101. package/out/chunks/chunk-LH4AZJPA.js +0 -389
  102. package/out/chunks/command-help-specs-CQ3RBLP6.js +0 -8
  103. package/out/chunks/commands-M3QTWKWE.js +0 -51
  104. package/out/chunks/help-XPXP46ZT.js +0 -10
  105. package/out/chunks/main-help-HXFAFHPG.js +0 -10
  106. package/out/chunks/tool-command-VHFLPIHY.js +0 -8
@@ -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
+ ```