@seybel/breakhound 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +123 -21
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6420,7 +6420,7 @@ async function startBridge(opts) {
|
|
|
6420
6420
|
case "/breakhound/checks/progress": {
|
|
6421
6421
|
const check = checks.get(String(body.id));
|
|
6422
6422
|
if (check && typeof body.done === "number" && typeof body.message === "string") {
|
|
6423
|
-
check.onProgress?.(body.done, body.message.slice(0,
|
|
6423
|
+
check.onProgress?.(body.done, body.message.slice(0, 400));
|
|
6424
6424
|
}
|
|
6425
6425
|
return reply({});
|
|
6426
6426
|
}
|
|
@@ -6526,6 +6526,9 @@ async function startBridge(opts) {
|
|
|
6526
6526
|
};
|
|
6527
6527
|
}
|
|
6528
6528
|
|
|
6529
|
+
// src/icon.ts
|
|
6530
|
+
var ICON_PNG = "data:image/png;base64,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";
|
|
6531
|
+
|
|
6529
6532
|
// src/mcp.ts
|
|
6530
6533
|
var PROTOCOL_VERSIONS = ["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"];
|
|
6531
6534
|
var INSTRUCTIONS = `breakhound finds responsive layout bugs by resizing pages in a real browser, and tells you the exact widths where they happen.
|
|
@@ -6538,12 +6541,15 @@ How to fix issues with it:
|
|
|
6538
6541
|
5. When verify is clean for a route, checkpoint it. That commits the fix on its own, so it can be undone later with undo.
|
|
6539
6542
|
Before finishing, run verify with no routes to check every page.
|
|
6540
6543
|
|
|
6544
|
+
scan and verify answer within about 45 seconds. When one says it's still running, it carries on: call it again with the same arguments to get its result, rather than something else. It says what it's waiting for; if that's the developer's browser tab, ask them to bring it into view.
|
|
6545
|
+
|
|
6541
6546
|
Issues sent from the extension are checked again in the developer's own browser: verify asks the open breakhound panel to scan the pages, so their login and browser carry over. If no panel is open, verify says so; ask the developer to open it rather than calling scan, which replaces their issues with a headless scan.`;
|
|
6542
6547
|
var FIX_PROMPT = `Fix the responsive layout issues breakhound found in this project.
|
|
6543
6548
|
|
|
6544
6549
|
Start with list_issues: if I sent them from the breakhound extension they are already there, so don't scan again. Only scan when there are none.
|
|
6545
6550
|
Work through the errors first, then the warnings. For each one: get_issue for the details, fix the cause in the code (not overflow: hidden), verify the routes you touched, and checkpoint each route once verify is clean for it.
|
|
6546
6551
|
Before you finish, verify with no routes to check every page, and tell me what you fixed and anything you couldn't.`;
|
|
6552
|
+
var REPLY_WITHIN_MS = 45e3;
|
|
6547
6553
|
var text = (t, isError = false) => ({
|
|
6548
6554
|
content: [{ type: "text", text: t }],
|
|
6549
6555
|
...isError ? { isError } : {}
|
|
@@ -6602,6 +6608,7 @@ function createTools() {
|
|
|
6602
6608
|
return [
|
|
6603
6609
|
{
|
|
6604
6610
|
name: "scan",
|
|
6611
|
+
kind: "long",
|
|
6605
6612
|
description: "Find the app (starting its dev server if needed), discover its routes and scan each one at every width from 320 to 1920px in a headless browser. Returns every issue with its id. The result is the baseline that verify compares with, replacing any issues the developer sent from the browser extension, so check list_issues first. Takes from seconds to a few minutes on large apps.",
|
|
6606
6613
|
inputSchema: {
|
|
6607
6614
|
type: "object",
|
|
@@ -6629,6 +6636,7 @@ function createTools() {
|
|
|
6629
6636
|
},
|
|
6630
6637
|
{
|
|
6631
6638
|
name: "list_routes",
|
|
6639
|
+
kind: "read",
|
|
6632
6640
|
description: "Routes from the last scan, with their status and number of issues.",
|
|
6633
6641
|
inputSchema: { type: "object", properties: {} },
|
|
6634
6642
|
async run(_args, ctx) {
|
|
@@ -6638,6 +6646,7 @@ function createTools() {
|
|
|
6638
6646
|
},
|
|
6639
6647
|
{
|
|
6640
6648
|
name: "list_issues",
|
|
6649
|
+
kind: "read",
|
|
6641
6650
|
description: "Issues from the last scan or verify, each listed once even when it shows up on several routes. Filter by route or severity.",
|
|
6642
6651
|
inputSchema: {
|
|
6643
6652
|
type: "object",
|
|
@@ -6657,6 +6666,7 @@ function createTools() {
|
|
|
6657
6666
|
},
|
|
6658
6667
|
{
|
|
6659
6668
|
name: "get_issue",
|
|
6669
|
+
kind: "read",
|
|
6660
6670
|
description: "Everything about one issue: the widths, the element and where it is in the code (when known), measurements, computed styles, a likely fix, every page it is on, and a screenshot.",
|
|
6661
6671
|
inputSchema: {
|
|
6662
6672
|
type: "object",
|
|
@@ -6687,6 +6697,7 @@ function createTools() {
|
|
|
6687
6697
|
},
|
|
6688
6698
|
{
|
|
6689
6699
|
name: "verify",
|
|
6700
|
+
kind: "long",
|
|
6690
6701
|
description: "Scan routes again and compare with the baseline from scan: which issues are fixed, which are still there, and which are new (a change broke something, maybe at another width or on another route). Pass the routes you changed for a quick check, and nothing to check every route before you finish.",
|
|
6691
6702
|
inputSchema: {
|
|
6692
6703
|
type: "object",
|
|
@@ -6861,6 +6872,8 @@ async function runMcpServer(version, io = { input: process.stdin, output: proces
|
|
|
6861
6872
|
let clientCanListRoots = false;
|
|
6862
6873
|
let root = process.env.BREAKHOUND_ROOT ?? null;
|
|
6863
6874
|
let queue = Promise.resolve();
|
|
6875
|
+
let job = null;
|
|
6876
|
+
const replyWithinMs = Number(process.env.BREAKHOUND_REPLY_WITHIN_MS) || REPLY_WITHIN_MS;
|
|
6864
6877
|
const send = (message) => io.output.write(`${JSON.stringify({ jsonrpc: "2.0", ...message })}
|
|
6865
6878
|
`);
|
|
6866
6879
|
const request = (method, params = {}) => new Promise((resolve8, reject) => {
|
|
@@ -6890,13 +6903,17 @@ async function runMcpServer(version, io = { input: process.stdin, output: proces
|
|
|
6890
6903
|
root ??= cwd;
|
|
6891
6904
|
return root;
|
|
6892
6905
|
}
|
|
6893
|
-
|
|
6894
|
-
const tool = tools.find((t) => t.name === params.name);
|
|
6895
|
-
if (!tool) {
|
|
6896
|
-
send({ id, error: { code: -32602, message: `Unknown tool: ${String(params.name)}` } });
|
|
6897
|
-
return;
|
|
6898
|
-
}
|
|
6906
|
+
const progressFor = (params) => {
|
|
6899
6907
|
const token = params._meta?.progressToken;
|
|
6908
|
+
return (done, message) => {
|
|
6909
|
+
if (token !== void 0)
|
|
6910
|
+
send({
|
|
6911
|
+
method: "notifications/progress",
|
|
6912
|
+
params: { progressToken: token, progress: done, message }
|
|
6913
|
+
});
|
|
6914
|
+
};
|
|
6915
|
+
};
|
|
6916
|
+
async function runTool(tool, params, progress) {
|
|
6900
6917
|
try {
|
|
6901
6918
|
const dir = await projectRoot();
|
|
6902
6919
|
const { config, warnings } = await loadConfig(dir);
|
|
@@ -6907,23 +6924,94 @@ async function runMcpServer(version, io = { input: process.stdin, output: proces
|
|
|
6907
6924
|
root: dir,
|
|
6908
6925
|
config,
|
|
6909
6926
|
bridge,
|
|
6910
|
-
progress
|
|
6911
|
-
if (token !== void 0)
|
|
6912
|
-
send({
|
|
6913
|
-
method: "notifications/progress",
|
|
6914
|
-
params: { progressToken: token, progress: done, message }
|
|
6915
|
-
});
|
|
6916
|
-
}
|
|
6927
|
+
progress
|
|
6917
6928
|
})
|
|
6918
6929
|
);
|
|
6919
6930
|
if (warnings.length)
|
|
6920
6931
|
result.content.push({ type: "text", text: `Config warnings: ${warnings.join(" ")}` });
|
|
6921
|
-
|
|
6932
|
+
return result;
|
|
6922
6933
|
} catch (error) {
|
|
6923
6934
|
if (error instanceof MemoryError) server.current = null;
|
|
6924
|
-
|
|
6935
|
+
return text(error instanceof Error ? error.message : String(error), true);
|
|
6925
6936
|
}
|
|
6926
6937
|
}
|
|
6938
|
+
const running = (j) => `${j.label} has been running for ${Math.round((Date.now() - j.startedAt) / 1e3)}s${j.latest ? ` (${j.latest})` : ""}.`;
|
|
6939
|
+
async function collect(j, params) {
|
|
6940
|
+
const late = new Promise((r) => setTimeout(() => r(null), replyWithinMs).unref());
|
|
6941
|
+
j.waiting = progressFor(params);
|
|
6942
|
+
const result = await Promise.race([j.done, late]);
|
|
6943
|
+
j.waiting = null;
|
|
6944
|
+
if (result) {
|
|
6945
|
+
if (job === j) job = null;
|
|
6946
|
+
return result;
|
|
6947
|
+
}
|
|
6948
|
+
return text(
|
|
6949
|
+
`Still running: ${running(j)} It carries on by itself. Call ${j.name} again with the same arguments to get its result; list_issues and get_issue answer meanwhile.`
|
|
6950
|
+
);
|
|
6951
|
+
}
|
|
6952
|
+
async function callTool(id, params) {
|
|
6953
|
+
const tool = tools.find((t) => t.name === params.name);
|
|
6954
|
+
if (!tool) {
|
|
6955
|
+
send({ id, error: { code: -32602, message: `Unknown tool: ${String(params.name)}` } });
|
|
6956
|
+
return;
|
|
6957
|
+
}
|
|
6958
|
+
const key = `${tool.name} ${JSON.stringify(params.arguments ?? {})}`;
|
|
6959
|
+
const busy = job && !job.finished ? job : null;
|
|
6960
|
+
if (tool.kind === "read") {
|
|
6961
|
+
const result = await runTool(tool, params, progressFor(params));
|
|
6962
|
+
if (busy)
|
|
6963
|
+
result.content.push({
|
|
6964
|
+
type: "text",
|
|
6965
|
+
text: `Note: ${running(busy)} Call ${busy.name} again with the same arguments for its result.`
|
|
6966
|
+
});
|
|
6967
|
+
send({ id, result });
|
|
6968
|
+
return;
|
|
6969
|
+
}
|
|
6970
|
+
if (job?.key === key) {
|
|
6971
|
+
send({ id, result: await collect(job, params) });
|
|
6972
|
+
return;
|
|
6973
|
+
}
|
|
6974
|
+
if (busy) {
|
|
6975
|
+
send({
|
|
6976
|
+
id,
|
|
6977
|
+
result: text(
|
|
6978
|
+
`Not yet: ${running(busy)} Call ${busy.name} again with the same arguments to get its result first.`,
|
|
6979
|
+
true
|
|
6980
|
+
)
|
|
6981
|
+
});
|
|
6982
|
+
return;
|
|
6983
|
+
}
|
|
6984
|
+
if (tool.kind !== "long") {
|
|
6985
|
+
job = null;
|
|
6986
|
+
queue = queue.then(() => runTool(tool, params, progressFor(params)));
|
|
6987
|
+
send({ id, result: await queue });
|
|
6988
|
+
return;
|
|
6989
|
+
}
|
|
6990
|
+
const args = params.arguments ?? {};
|
|
6991
|
+
const routes = strings(args.routes);
|
|
6992
|
+
const j = {
|
|
6993
|
+
key,
|
|
6994
|
+
name: tool.name,
|
|
6995
|
+
label: `${tool.name}${routes.length ? ` of ${routes.join(", ")}` : ""}`,
|
|
6996
|
+
startedAt: Date.now(),
|
|
6997
|
+
latest: "",
|
|
6998
|
+
finished: false,
|
|
6999
|
+
waiting: null,
|
|
7000
|
+
done: Promise.resolve(text(""))
|
|
7001
|
+
};
|
|
7002
|
+
j.done = queue.then(
|
|
7003
|
+
() => runTool(tool, params, (done, message) => {
|
|
7004
|
+
j.latest = message;
|
|
7005
|
+
j.waiting?.(done, message);
|
|
7006
|
+
})
|
|
7007
|
+
);
|
|
7008
|
+
j.done.then(() => {
|
|
7009
|
+
j.finished = true;
|
|
7010
|
+
});
|
|
7011
|
+
queue = j.done;
|
|
7012
|
+
job = j;
|
|
7013
|
+
send({ id, result: await collect(j, params) });
|
|
7014
|
+
}
|
|
6927
7015
|
function handle(message) {
|
|
6928
7016
|
const { id, method } = message;
|
|
6929
7017
|
const params = message.params ?? {};
|
|
@@ -6948,7 +7036,13 @@ async function runMcpServer(version, io = { input: process.stdin, output: proces
|
|
|
6948
7036
|
result: {
|
|
6949
7037
|
protocolVersion: PROTOCOL_VERSIONS.includes(asked) ? asked : PROTOCOL_VERSIONS[0],
|
|
6950
7038
|
capabilities: { tools: { listChanged: false }, prompts: { listChanged: false } },
|
|
6951
|
-
serverInfo: {
|
|
7039
|
+
serverInfo: {
|
|
7040
|
+
name: "breakhound",
|
|
7041
|
+
version: VERSION,
|
|
7042
|
+
// Shown beside the server by clients that show icons. No sizes: some clients' older
|
|
7043
|
+
// schemas expect another shape for it, and an icon isn't worth a failed connection.
|
|
7044
|
+
icons: [{ src: ICON_PNG, mimeType: "image/png" }]
|
|
7045
|
+
},
|
|
6952
7046
|
instructions: INSTRUCTIONS
|
|
6953
7047
|
}
|
|
6954
7048
|
});
|
|
@@ -6996,7 +7090,7 @@ async function runMcpServer(version, io = { input: process.stdin, output: proces
|
|
|
6996
7090
|
});
|
|
6997
7091
|
return;
|
|
6998
7092
|
case "tools/call":
|
|
6999
|
-
|
|
7093
|
+
void callTool(id, params);
|
|
7000
7094
|
return;
|
|
7001
7095
|
default:
|
|
7002
7096
|
send({ id, error: { code: -32601, message: `Method not found: ${String(method)}` } });
|
|
@@ -7140,6 +7234,7 @@ function show(path, cwd) {
|
|
|
7140
7234
|
const rel = relative7(cwd, path);
|
|
7141
7235
|
return rel.startsWith("..") ? path.replace(homedir(), "~") : rel;
|
|
7142
7236
|
}
|
|
7237
|
+
var NOT_SHOWING_UP = "Not showing up? Your agent runs breakhound only while this folder is open in it, and with breakhound switched on in its MCP settings (Cursor and VS Code may add it switched off), so check both. How, for each agent: https://seybel.github.io/breakhound/guide/agents#not-connected";
|
|
7143
7238
|
async function installMcp(cwd, options = {}) {
|
|
7144
7239
|
const home = options.home ?? homedir();
|
|
7145
7240
|
const clients = options.clients?.length ? allClients(cwd, home).filter((c) => options.clients?.includes(c.id)) : detectClients(cwd, home);
|
|
@@ -7151,20 +7246,26 @@ async function installMcp(cwd, options = {}) {
|
|
|
7151
7246
|
return 1;
|
|
7152
7247
|
}
|
|
7153
7248
|
const changes = [];
|
|
7249
|
+
let alreadySetUp = false;
|
|
7154
7250
|
for (const client of clients) {
|
|
7155
7251
|
const current = existsSync15(client.file) ? readFileSync18(client.file, "utf8") : null;
|
|
7156
7252
|
try {
|
|
7157
7253
|
const next = client.update(current);
|
|
7158
|
-
if (next === null)
|
|
7254
|
+
if (next === null) {
|
|
7255
|
+
alreadySetUp = true;
|
|
7159
7256
|
console.log(
|
|
7160
7257
|
`${styleText8("green", "\u2714")} ${client.name}: already set up in ${show(client.file, cwd)}`
|
|
7161
7258
|
);
|
|
7162
|
-
else changes.push({ client, next });
|
|
7259
|
+
} else changes.push({ client, next });
|
|
7163
7260
|
} catch (error) {
|
|
7164
7261
|
console.log(`${styleText8("yellow", "!")} ${client.name}: ${error.message}`);
|
|
7165
7262
|
}
|
|
7166
7263
|
}
|
|
7167
|
-
if (!changes.length)
|
|
7264
|
+
if (!changes.length) {
|
|
7265
|
+
if (alreadySetUp) console.log(`
|
|
7266
|
+
${NOT_SHOWING_UP}`);
|
|
7267
|
+
return 0;
|
|
7268
|
+
}
|
|
7168
7269
|
console.log("\nbreakhound will add its MCP server to:\n");
|
|
7169
7270
|
const { command, args } = serverCommand();
|
|
7170
7271
|
for (const { client } of changes) {
|
|
@@ -7195,6 +7296,7 @@ async function installMcp(cwd, options = {}) {
|
|
|
7195
7296
|
`
|
|
7196
7297
|
Restart your agent (or reload its MCP servers), then ask it to "fix the responsive issues breakhound finds".`
|
|
7197
7298
|
);
|
|
7299
|
+
console.log(NOT_SHOWING_UP);
|
|
7198
7300
|
return 0;
|
|
7199
7301
|
}
|
|
7200
7302
|
|