devharness 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/block-events.d.ts +42 -0
- package/build/block-events.d.ts.map +1 -0
- package/build/block-events.js +67 -0
- package/build/block-events.js.map +1 -0
- package/build/block-events.test.d.ts +2 -0
- package/build/block-events.test.d.ts.map +1 -0
- package/build/block-events.test.js +131 -0
- package/build/block-events.test.js.map +1 -0
- package/build/index.js +33 -2
- package/build/index.js.map +1 -1
- package/build/server-block-cleanup.test.d.ts +2 -0
- package/build/server-block-cleanup.test.d.ts.map +1 -0
- package/build/server-block-cleanup.test.js +143 -0
- package/build/server-block-cleanup.test.js.map +1 -0
- package/build/server-manager.d.ts +8 -0
- package/build/server-manager.d.ts.map +1 -1
- package/build/server-manager.js +49 -0
- package/build/server-manager.js.map +1 -1
- package/build/startup-gate.d.ts +29 -0
- package/build/startup-gate.d.ts.map +1 -0
- package/build/startup-gate.js +49 -0
- package/build/startup-gate.js.map +1 -0
- package/build/startup-gate.test.d.ts +2 -0
- package/build/startup-gate.test.d.ts.map +1 -0
- package/build/startup-gate.test.js +73 -0
- package/build/startup-gate.test.js.map +1 -0
- package/build/tool-response.d.ts +3 -0
- package/build/tool-response.d.ts.map +1 -1
- package/build/tool-response.js +46 -1
- package/build/tool-response.js.map +1 -1
- package/build/tools/assert-tools.d.ts +3 -3
- package/build/tools/breakpoint-tools.d.ts +3 -3
- package/build/tools/config-tools.d.ts +3 -3
- package/build/tools/console-tools.d.ts +9 -9
- package/build/tools/content-tools.d.ts +3 -3
- package/build/tools/input-tools.d.ts +9 -9
- package/build/tools/issues-tools.d.ts +6 -6
- package/build/tools/page-tools.d.ts +3 -3
- package/build/tools/replay-tools.d.ts +9 -9
- package/build/tools/request-tools.d.ts +3 -3
- package/build/tools/server-tools.d.ts +9 -9
- package/build/tools/storage-tools.d.ts +3 -3
- package/build/tools/wait-tools.d.ts +6 -6
- package/docs/instructions.md +23 -0
- package/package.json +1 -1
- package/plugin/skills/devharness/SKILL.md +90 -158
|
@@ -49,19 +49,19 @@ export declare function createWaitTools(resolveConnectionFromReason: (connection
|
|
|
49
49
|
pollIntervalMs: z.ZodOptional<z.ZodNumber>;
|
|
50
50
|
connectionReason: z.ZodOptional<z.ZodString>;
|
|
51
51
|
}, "strict", z.ZodTypeAny, {
|
|
52
|
-
timeoutMs?: number | undefined;
|
|
53
|
-
expression?: string | undefined;
|
|
54
52
|
selector?: string | undefined;
|
|
55
53
|
selectorGone?: string | undefined;
|
|
54
|
+
expression?: string | undefined;
|
|
56
55
|
ms?: number | undefined;
|
|
56
|
+
timeoutMs?: number | undefined;
|
|
57
57
|
connectionReason?: string | undefined;
|
|
58
58
|
pollIntervalMs?: number | undefined;
|
|
59
59
|
}, {
|
|
60
|
-
timeoutMs?: number | undefined;
|
|
61
|
-
expression?: string | undefined;
|
|
62
60
|
selector?: string | undefined;
|
|
63
61
|
selectorGone?: string | undefined;
|
|
62
|
+
expression?: string | undefined;
|
|
64
63
|
ms?: number | undefined;
|
|
64
|
+
timeoutMs?: number | undefined;
|
|
65
65
|
connectionReason?: string | undefined;
|
|
66
66
|
pollIntervalMs?: number | undefined;
|
|
67
67
|
}>;
|
|
@@ -72,11 +72,11 @@ export declare function createWaitTools(resolveConnectionFromReason: (connection
|
|
|
72
72
|
} | undefined;
|
|
73
73
|
};
|
|
74
74
|
handler: (args: {
|
|
75
|
-
timeoutMs?: number | undefined;
|
|
76
|
-
expression?: string | undefined;
|
|
77
75
|
selector?: string | undefined;
|
|
78
76
|
selectorGone?: string | undefined;
|
|
77
|
+
expression?: string | undefined;
|
|
79
78
|
ms?: number | undefined;
|
|
79
|
+
timeoutMs?: number | undefined;
|
|
80
80
|
connectionReason?: string | undefined;
|
|
81
81
|
pollIntervalMs?: number | undefined;
|
|
82
82
|
}, abortSignal?: AbortSignal) => Promise<any>;
|
package/docs/instructions.md
CHANGED
|
@@ -120,6 +120,29 @@ Chrome DevTools Protocol debugging for JavaScript/TypeScript in Chrome, Node.js,
|
|
|
120
120
|
- **Missing/invalid parameters**: the error includes a `continuationToken` and `missingParameters` (name/type/description/enum). Retry with just `{ continuationToken, <missing/bad field(s)> }` - don't resend everything. Expires after 5 min.
|
|
121
121
|
- **A validated call gets blocked by a guard** (port failure, dead server, breakpoint pause): the response footer shows `**Repeat:** replay({ action: 'repeat', indices: [N] })`. Acknowledge the guard (e.g. `server({ action: 'acknowledgePort' })`), then use that hint to resume the exact call. Don't reuse a `continuationToken` here - that's for fixing bad input, not for retrying an already-valid call.
|
|
122
122
|
|
|
123
|
+
## Getting notified when a guard blocks
|
|
124
|
+
|
|
125
|
+
Guards only surface on the *next* tool call, so a server that dies while you're editing files stays invisible until you call back into devharness. Every new block also appends one JSON line to `.devharness/logs/blocks.jsonl`:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{"ts":"2026-08-08T03:21:45.588Z","guard":"pendingStartup","tool":"navigate","detail":"died before port detected: \"web\"","resolve":"server({ action: 'acknowledgeStartup', serverId: 'web' })"}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`guard` is one of `port`, `breakpoint`, `pendingStartup`, `bug`, `duplicateSession`. Lines are deduplicated: one per *new* block, not one per blocked call. The same block re-firing on later calls stays quiet until a call clears every guard and the block recurs.
|
|
132
|
+
|
|
133
|
+
In Claude Code, tail it with a persistent Monitor so blocks arrive as notifications while you work on something else:
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
Monitor({
|
|
137
|
+
command: "mkdir -p .devharness/logs && touch .devharness/logs/blocks.jsonl && tail -f -n0 .devharness/logs/blocks.jsonl",
|
|
138
|
+
description: "devharness guard blocks",
|
|
139
|
+
persistent: true,
|
|
140
|
+
timeout_ms: 3600000
|
|
141
|
+
})
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Any other client can watch the same file. `-n0` skips existing history so you only see blocks from that point on.
|
|
145
|
+
|
|
123
146
|
## Restarting devharness
|
|
124
147
|
|
|
125
148
|
If devharness itself seems stuck or broken (not the target app), restart it yourself rather than asking the user to reconnect: `config({ action: 'restart' })`. Falls back to `kill -USR2 $(cat .devharness/mcp-supervisor.pid)` via Bash if that action reports `CONFIG_RESTART_NOT_SUPERVISED` (e.g. a bare `node build/index.js`, not through the supervisor). Editing devharness's own source and running `npm run build` triggers the same restart automatically via its postbuild hook - `config({ action: 'status' })` reports which build is actually answering (entry file, its timestamp, server and supervisor pids), so a rebuild that signalled the wrong supervisor is visible rather than silent. Either way, this kills any Chrome instances it launched (relaunch with `launchChrome`) but managed dev servers (`server` tool) survive and reattach automatically.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devharness",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "MCP server that connects AI assistants to Chrome DevTools Protocol for runtime debugging - set breakpoints, inspect variables, monitor network traffic, and automate browser interactions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -2,194 +2,126 @@
|
|
|
2
2
|
name: devharness
|
|
3
3
|
description: Drive and debug a running app via the devharness MCP server - launch or attach to Chrome and Node.js, set breakpoints and logpoints, inspect call stacks and variables, watch console and network, manage dev servers, replay any earlier tool call by its history index, and record reproduction sequences that verify a fix. Use whenever a task involves running or debugging a live app, reproducing or verifying a bug, re-driving setup you already did (relaunching, re-logging in, refilling a form), or the user mentions breakpoints, Chrome DevTools, CDP, replay sequences, or devharness tools (launchChrome, navigate, breakpoint, inspect, replay, server, issues, etc.).
|
|
4
4
|
compatibility: Requires the devharness MCP server to be connected (tools such as launchChrome, breakpoint, inspect, replay, server, issues). Previously published as cdp-tools-mcp.
|
|
5
|
-
version: 0.9.
|
|
5
|
+
version: 0.9.4
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# devharness
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
CDP debugging for JS/TS in Chrome, Node.js, or any CDP target.
|
|
11
11
|
|
|
12
|
-
## Quick
|
|
12
|
+
## Quick start
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
1. launchChrome({ reference: "your-descriptive-name" }) # Auto-connects, ready immediately
|
|
17
|
-
2. navigate({ action: 'goto', connectionReason: "your-descriptive-name", url: "..." })
|
|
18
|
-
# Navigation automatically caches interactive elements (links, buttons, inputs) for the page
|
|
19
|
-
3. content({ action: 'findInteractive', connectionReason: "your-descriptive-name" })
|
|
20
|
-
# Shows summary of all interactive elements. Use search/types to filter
|
|
21
|
-
4. content({ action: 'extractText', mode: 'outline' }) # Read page content (preferred over screenshot)
|
|
22
|
-
5. Use other tools as needed with connectionReason parameter
|
|
23
|
-
```
|
|
14
|
+
Every tool takes `connectionReason` — the name you gave the connection.
|
|
24
15
|
|
|
25
|
-
**Alternative (rename later):**
|
|
26
16
|
```
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
launchChrome({ reference: "app" }) # launches AND connects; do NOT then call connectDebugger
|
|
18
|
+
navigate({ action: 'goto', connectionReason: "app", url }) # caches interactive elements
|
|
19
|
+
content({ action: 'findInteractive' }) # summary; filter with search/types
|
|
20
|
+
content({ action: 'extractText', mode: 'outline' }) # prefer over screenshot
|
|
30
21
|
```
|
|
31
22
|
|
|
32
|
-
|
|
23
|
+
Node: `node --inspect=9229 app.js` → `connectDebugger({ reference: "api", port: 9229 })`.
|
|
24
|
+
`connectDebugger` is only for existing Node/remote debuggers.
|
|
25
|
+
|
|
26
|
+
Launched without a reference? `tab({ action: 'rename', reference: "unnamed-connection-default", newReference: "app" })`.
|
|
27
|
+
|
|
28
|
+
Paused: `inspect({ action: 'getCallStack' })` → `getVariables` → `evaluateExpression`.
|
|
29
|
+
Watch: `console({ action: 'list' })`, `network({ action: 'list' })` (needs `network({ action: 'enable' })` first).
|
|
30
|
+
|
|
31
|
+
## `.devharness/` must be git-ignored
|
|
32
|
+
|
|
33
|
+
State lands in `.devharness/` — config, server claims, logs, sequences, issues. Machine-local; carries pids, ports, and local paths into what may be a public repo.
|
|
34
|
+
|
|
35
|
+
Before the first tool that writes there (`server`, `replay` record, `issues`, `setDebugLogging`) in a git repo:
|
|
36
|
+
|
|
33
37
|
```
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
3. breakpoint({ action: 'set', connectionReason: "my-app-debug", ... })
|
|
38
|
+
git check-ignore -q .devharness && echo ignored || echo NOT ignored
|
|
39
|
+
git ls-files .devharness # already tracked?
|
|
37
40
|
```
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
##
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- Clean up with `breakpoint({ action: 'remove' })` or check `breakpoint({ action: 'list' })`
|
|
57
|
-
|
|
58
|
-
**DOM/Event/XHR Breakpoints (Chrome only):**
|
|
59
|
-
- `breakpoint({ action: 'setDOMBreakpoint' })`: Pause when element changes
|
|
60
|
-
- `subtree-modified`: Children added/removed
|
|
61
|
-
- `attribute-modified`: Attributes changed (class, style, etc.)
|
|
62
|
-
- `node-removed`: Element deleted from DOM
|
|
63
|
-
- `breakpoint({ action: 'setEventBreakpoint' })`: Pause when events fire (click, submit, input, keydown, etc.)
|
|
64
|
-
- `breakpoint({ action: 'setXHRBreakpoint' })`: Pause when XHR/Fetch URL contains pattern
|
|
65
|
-
- Example: `breakpoint({ action: 'setDOMBreakpoint', selector: '.todo-list', domBreakpointType: 'subtree-modified' })`
|
|
66
|
-
- Note: DOM breakpoints use nodeIds which are invalidated on page reload
|
|
67
|
-
|
|
68
|
-
**Code search:**
|
|
69
|
-
- `inspect({ action: 'searchCode' })`: Find patterns
|
|
70
|
-
- `inspect({ action: 'searchFunctions' })`: Locate definitions
|
|
71
|
-
- `getSourceCode`: View context
|
|
72
|
-
|
|
73
|
-
**Modal handling:**
|
|
74
|
-
- Use `handleModals: true` on `input({ action: 'click' | 'type' | 'hover' })`
|
|
75
|
-
- Strategies: `auto` (smart), `accept`, `reject`, `close`, `remove`
|
|
76
|
-
- Example: `input({ action: 'click', selector: '.btn', handleModals: true, dismissStrategy: 'auto' })`
|
|
77
|
-
- Limitation: English-only, no Shadow DOM/iframes
|
|
78
|
-
|
|
79
|
-
**Multiple connections:**
|
|
80
|
-
- `listConnections` → `switchConnection`
|
|
81
|
-
- Each connection = separate tab/process
|
|
82
|
-
|
|
83
|
-
**Re-running work you already did:**
|
|
84
|
-
- Every tool response carries its own history index in the footer:
|
|
85
|
-
`**Repeat:** replay({ action: 'repeat', indices: [58] })`. That is not only
|
|
86
|
-
for failures - it is on every call, all the time. (The `replay` tool's own
|
|
87
|
-
responses are the exception: replay calls are not recorded into history)
|
|
88
|
-
- `indices` takes a **list**, so a whole stretch of work replays in one call:
|
|
89
|
-
`replay({ action: 'repeat', indices: [58, 59, 60, 61] })` re-runs those four
|
|
90
|
-
steps in order
|
|
91
|
-
- Reach for this whenever you are about to redo something you already did -
|
|
92
|
-
a browser relaunch, re-logging in, retyping a form, getting back to the
|
|
93
|
-
screen where a bug appears. Re-issuing the calls by hand is slower, and
|
|
94
|
-
retyped arguments drift from what actually ran
|
|
95
|
-
- `replay({ action: 'history' })` lists the indices when they have scrolled
|
|
96
|
-
out of view
|
|
97
|
-
- Each repeated call replays against the connection it was recorded with, so a
|
|
98
|
-
batch spanning two browsers stays on both. Pass `connectionReason` on every
|
|
99
|
-
call while driving multiple browsers and this holds; drive one implicitly and
|
|
100
|
-
those calls have no connection to replay against
|
|
101
|
-
- If the stretch is worth keeping, turn it into a sequence:
|
|
102
|
-
`replay({ action: 'create', name: '...', indices: [58, 59, 60, 61] })`
|
|
103
|
-
|
|
104
|
-
## Common Patterns
|
|
105
|
-
|
|
106
|
-
**Bug debugging:**
|
|
107
|
-
1. `launchChrome` → `navigate({ action: 'goto' })`
|
|
108
|
-
2. `inspect({ action: 'searchCode' | 'searchFunctions' })`
|
|
109
|
-
3. `breakpoint({ action: 'set' | 'setLogpoint' })`
|
|
110
|
-
4. Trigger bug
|
|
111
|
-
5. `inspect({ action: 'getCallStack' })` + `inspect({ action: 'getVariables' })`
|
|
112
|
-
6. `inspect({ action: 'evaluateExpression' })`
|
|
113
|
-
|
|
114
|
-
**Performance:**
|
|
115
|
-
1. `network({ action: 'enable' })`
|
|
116
|
-
2. `navigate({ action: 'goto' })`
|
|
117
|
-
3. `network({ action: 'search' })` (find slow)
|
|
118
|
-
4. `network({ action: 'get' })` (timing)
|
|
119
|
-
5. `breakpoint({ action: 'setLogpoint' })` in slow paths
|
|
120
|
-
|
|
121
|
-
**Frontend state:**
|
|
122
|
-
1. `dom({ action: 'querySelector' })` + `dom({ action: 'getProperties' })`
|
|
123
|
-
2. `storage({ action: 'getLocalStorage' })` + `storage({ action: 'getCookies' })`
|
|
124
|
-
3. `inspect({ action: 'evaluateExpression' })`
|
|
125
|
-
4. `dom({ action: 'snapshot' })`
|
|
126
|
-
|
|
127
|
-
**UI verification:**
|
|
128
|
-
1. `content({ action: 'verify' })` - Run all default checks
|
|
129
|
-
2. Reports: dead buttons, small touch targets, overflow clipping, dead links, viewport issues
|
|
130
|
-
3. Filter checks: `checks: ['handlers', 'touch']` for specific issues
|
|
131
|
-
4. Available checks: `handlers`, `viewport`, `touch`, `overflow`, `clickability`, `links`, `scroll`
|
|
132
|
-
|
|
133
|
-
## Important Notes
|
|
134
|
-
|
|
135
|
-
- **After `launchChrome()`**: You are ALREADY connected. Do NOT call `connectDebugger()`. Use the `reference` parameter when launching, or rename later with `tab({ action: 'rename' })`
|
|
136
|
-
- **Interactive elements cache**: Navigation (goto, reload, back, forward) automatically caches all interactive elements. Cache expires after 5 minutes. `findInteractive` shows a summary by default; use `search` or `types` parameters to filter elements
|
|
137
|
-
- **Logpoint limits**: Default 20 executions. Use `breakpoint({ action: 'resetCounter' })` or adjust `maxExecutions`
|
|
138
|
-
- **Expression failures**: Wrapped in try-catch, shows `[Error: message]`. Search: `console({ action: 'search', pattern: "Logpoint Error" })`
|
|
139
|
-
- **CDP line mapping**: May map to nearest valid line. Use `breakpoint({ action: 'validate' })` first
|
|
140
|
-
- **Source maps**: Auto-handled. Use `loadSourceMaps` for manual
|
|
141
|
-
- **File paths**: Full URLs (`http://localhost:3000/app.js`) or `file://`
|
|
142
|
-
- **Network monitoring**: Must enable with `network({ action: 'enable' })`
|
|
143
|
-
- **Working an issue**: `comment` on it as you go - once when you start (what you're about to change and why) and once when you finish (what you actually changed, files touched, tests added, and anything that contradicts the issue as written). The issue is the durable record; someone reviewing later reads the timeline, not your diff
|
|
144
|
-
- **Closing an issue**: `issues({ action: 'resolve' })` waits on a browser overlay only a human can click - don't call it unattended, use `issues({ action: 'comment' })` to record findings instead
|
|
145
|
-
|
|
146
|
-
## Recovering from a failed tool call
|
|
147
|
-
|
|
148
|
-
Two different mechanisms fix two different failure points - don't confuse them.
|
|
149
|
-
|
|
150
|
-
**1. Missing/invalid parameters -> `continuationToken` (fix and resubmit, cheaply)**
|
|
151
|
-
|
|
152
|
-
If a call fails validation (`code: 'MISSING_PARAMETERS'` or `'INVALID_PARAMS'`), the error includes a `continuationToken` and a `missingParameters` list (name/type/description/enum). Don't resend the whole call - retry with just:
|
|
42
|
+
Not ignored → **ask** before adding it to `.gitignore`. Already tracked → say so; `git rm -r --cached .devharness` untracks, but anything pushed stays in history.
|
|
43
|
+
|
|
44
|
+
## Repeat instead of retyping
|
|
45
|
+
|
|
46
|
+
Every response footer carries its own index: `**Repeat:** replay({ action: 'repeat', indices: [58] })`. On every call, not just failures (`replay`'s own calls aren't recorded).
|
|
47
|
+
|
|
48
|
+
- `indices` takes a list: `[58, 59, 60, 61]` re-runs four steps in order
|
|
49
|
+
- Use it for anything you already did — relaunch, re-login, refilling a form, getting back to the bug. Retyped arguments drift from what actually ran
|
|
50
|
+
- `replay({ action: 'history' })` when indices scrolled away
|
|
51
|
+
- Each call replays on the connection it was recorded with, so pass `connectionReason` explicitly when driving several browsers — implicit calls have no connection to replay against
|
|
52
|
+
- Worth keeping: `replay({ action: 'create', name, indices })`
|
|
53
|
+
|
|
54
|
+
## Recovering from a failed call
|
|
55
|
+
|
|
56
|
+
Two mechanisms, two different failure points — don't mix them.
|
|
57
|
+
|
|
58
|
+
**Validation failed** (`MISSING_PARAMETERS`, `INVALID_PARAMS`) → the error carries a `continuationToken` and `missingParameters`. Resend only what was missing:
|
|
153
59
|
```
|
|
154
|
-
{ continuationToken: '<token>', <
|
|
60
|
+
{ continuationToken: '<token>', <missing field(s)> }
|
|
155
61
|
```
|
|
156
|
-
|
|
62
|
+
Server merges and re-validates. Same token, repeat until it passes. Expires in 5 min.
|
|
63
|
+
|
|
64
|
+
**Guard blocked a valid call** (dead port, breakpoint pause) → it was already recorded. Acknowledge (`server({ action: 'acknowledgePort' })`, `acknowledgeStartup`), then use the footer's `replay` hint. Don't rebuild the arguments; don't use a `continuationToken` here.
|
|
157
65
|
|
|
158
|
-
|
|
66
|
+
## Get told the moment a guard blocks
|
|
67
|
+
|
|
68
|
+
Guards only surface on your *next* devharness call, so a server that dies while you're editing files stays invisible until you happen to call back. Every new block also appends one JSON line to `.devharness/logs/blocks.jsonl` — `{ts, guard, tool, detail, resolve}`, one of `port`, `breakpoint`, `pendingStartup`, `bug`, `duplicateSession`.
|
|
69
|
+
|
|
70
|
+
Arm a Claude Code Monitor once, right after `launchChrome`/`server start`:
|
|
159
71
|
|
|
160
|
-
Once a call passes validation, devharness records it (even if a guard then blocks it before the handler runs) and every response footer includes a hint like:
|
|
161
72
|
```
|
|
162
|
-
|
|
73
|
+
Monitor({
|
|
74
|
+
command: "mkdir -p .devharness/logs && touch .devharness/logs/blocks.jsonl && tail -f -n0 .devharness/logs/blocks.jsonl",
|
|
75
|
+
description: "devharness guard blocks",
|
|
76
|
+
persistent: true,
|
|
77
|
+
timeout_ms: 3600000
|
|
78
|
+
})
|
|
163
79
|
```
|
|
164
|
-
Acknowledge whatever blocked it (e.g. `server({ action: 'acknowledgePort' })`, `server({ action: 'acknowledgeStartup' })`), then use that `replay` hint to resume the exact same call - do not reconstruct the arguments by hand, and do not try to reuse a `continuationToken` for this case (that mechanism is for fixing bad input, not for retrying a call that was already valid).
|
|
165
80
|
|
|
166
|
-
|
|
81
|
+
`-n0` skips history — you only want blocks from now on. One line per *new* block, not per blocked call; the same block re-firing stays quiet until a call gets through all guards and it recurs. Act on the event's `resolve` field.
|
|
167
82
|
|
|
168
83
|
## Restarting devharness
|
|
169
84
|
|
|
170
|
-
If devharness itself
|
|
85
|
+
If devharness itself is stuck (not the target app), restart it — don't wait to be asked.
|
|
86
|
+
|
|
87
|
+
- `config({ action: 'restart' })` — SIGUSR2s the supervisor via `.devharness/mcp-supervisor.pid`; it replays the `initialize` handshake so the host never reconnects
|
|
88
|
+
- `CONFIG_RESTART_NOT_SUPERVISED` → `kill -USR2 $(cat .devharness/mcp-supervisor.pid)`
|
|
89
|
+
- `CONFIG_RESTART_STALE_PID` → supervisor died dirty; ask the user to run `/mcp`
|
|
90
|
+
|
|
91
|
+
**The triggering call returns an error — that's normal.** You get `MCP error -32000: MCP server is restarting...` instead of `CONFIG_RESTART_REQUESTED`, because the old process dies before flushing. Retry; the next call hits the new process. Expect a new PID in footers, and acknowledged port failures to reset.
|
|
92
|
+
|
|
93
|
+
`config({ action: 'status' })` reports version, entry file, its timestamp, and pids — check the timestamp before believing a rebuild landed; a build signals the supervisor in its own project's pidfile, not always this session's.
|
|
94
|
+
|
|
95
|
+
Restart kills Chrome instances this session launched (relaunch with `launchChrome`); managed servers survive and reattach. `config({ action: 'reload' })` hot-applies most config edits — restart is only needed for `tools.enabled`/`tools.disabled` or a genuinely stuck process.
|
|
96
|
+
|
|
97
|
+
## Practices
|
|
98
|
+
|
|
99
|
+
**Breakpoints** — conditional: `condition: "userId === '123'"`. Loops/hot paths: `setLogpoint` (20 executions default; `resetCounter` or `maxExecutions`). Clean up with `remove`, audit with `list`. CDP may snap to the nearest line — `validate` first. Source maps auto-load; `loadSourceMaps` to force. Paths are full URLs (`http://localhost:3000/app.js`) or `file://`.
|
|
100
|
+
|
|
101
|
+
**DOM/Event/XHR breakpoints** (Chrome only) — `setDOMBreakpoint` (`subtree-modified`, `attribute-modified`, `node-removed`), `setEventBreakpoint` (click, submit, input, keydown…), `setXHRBreakpoint` (URL substring). Example: `breakpoint({ action: 'setDOMBreakpoint', selector: '.todo-list', domBreakpointType: 'subtree-modified' })`. nodeIds die on reload.
|
|
171
102
|
|
|
172
|
-
|
|
173
|
-
- If that returns `CONFIG_RESTART_NOT_SUPERVISED` (this server isn't running through the supervisor - e.g. a bare `node build/index.js`), fall back to Bash: `kill -USR2 $(cat .devharness/mcp-supervisor.pid)`.
|
|
174
|
-
- If it returns `CONFIG_RESTART_STALE_PID`, the supervisor died without cleaning up its pidfile - ask the user to run `/mcp` to reconnect.
|
|
103
|
+
**Expressions** — wrapped in try-catch, surface as `[Error: message]`. Find them: `console({ action: 'search', pattern: "Logpoint Error" })`.
|
|
175
104
|
|
|
176
|
-
**
|
|
105
|
+
**Element cache** — populated by goto/reload/back/forward, expires after 5 min.
|
|
177
106
|
|
|
178
|
-
|
|
107
|
+
**Modals** — `handleModals: true` on `input` click/type/hover, `dismissStrategy`: `auto` | `accept` | `reject` | `close` | `remove`. English-only, no Shadow DOM or iframes.
|
|
179
108
|
|
|
180
|
-
|
|
109
|
+
**Code search** — `inspect({ action: 'searchCode' | 'searchFunctions' })`, then `getSourceCode`.
|
|
181
110
|
|
|
182
|
-
|
|
111
|
+
**Connections** — `listConnections` → `switchConnection`. One connection per tab/process.
|
|
183
112
|
|
|
184
|
-
|
|
113
|
+
**Issues** — `comment` when you start (what you're changing, why) and when you finish (what changed, files, tests, anything contradicting the issue). The timeline is the durable record, not your diff. `resolve` waits on a browser overlay only a human can click — never call it unattended; `comment` instead.
|
|
185
114
|
|
|
186
|
-
|
|
115
|
+
## Patterns
|
|
187
116
|
|
|
188
|
-
|
|
117
|
+
| Task | Sequence |
|
|
118
|
+
|---|---|
|
|
119
|
+
| Bug | `launchChrome` → `goto` → `searchCode`/`searchFunctions` → `set`/`setLogpoint` → trigger → `getCallStack` + `getVariables` → `evaluateExpression` |
|
|
120
|
+
| Performance | `network enable` → `goto` → `network search` → `network get` (timing) → `setLogpoint` in slow paths |
|
|
121
|
+
| Frontend state | `dom querySelector` + `getProperties` → `storage getLocalStorage`/`getCookies` → `evaluateExpression` → `dom snapshot` |
|
|
122
|
+
| UI audit | `content({ action: 'verify' })` — dead buttons, touch targets, overflow, dead links, viewport. Filter: `checks: ['handlers','touch']` from `handlers`, `viewport`, `touch`, `overflow`, `clickability`, `links`, `scroll` |
|
|
189
123
|
|
|
190
|
-
|
|
191
|
-
multi-device flow - has its own workflow: capturing values mid-run with
|
|
192
|
-
`saveAs`, per-step `connectionReason`, conditionals, and verifying an issue's
|
|
193
|
-
fix. Load it when the task involves sequences:
|
|
124
|
+
## Load on demand
|
|
194
125
|
|
|
195
|
-
[references/
|
|
126
|
+
- Full tool/action catalogue: [references/tool-categories.md](references/tool-categories.md)
|
|
127
|
+
- Recording/replaying sequences — `saveAs`, per-step `connectionReason`, conditionals, verifying an issue fix: [references/sequences.md](references/sequences.md)
|