@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.
Files changed (2) hide show
  1. package/dist/cli.js +123 -21
  2. 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, 200));
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
- async function callTool(id, params) {
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(done, message) {
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
- send({ id, result });
6932
+ return result;
6922
6933
  } catch (error) {
6923
6934
  if (error instanceof MemoryError) server.current = null;
6924
- send({ id, result: text(error instanceof Error ? error.message : String(error), true) });
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: { name: "breakhound", version: VERSION },
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
- queue = queue.then(() => callTool(id, params));
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) return 0;
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seybel/breakhound",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Find where your layout breaks across every viewport width, then hand the fix to your coding agent.",
5
5
  "keywords": [
6
6
  "responsive",