@birdybeep/cli 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -129,7 +129,7 @@ function filteredActivity() {
129
129
  function describeFilteredActivity(activity) {
130
130
  const types = Object.entries(activity.byType).sort(([, a], [, b]) => b - a).map(([type, n]) => `${type} \xD7${n}`).join(", ");
131
131
  const since = new Date(activity.firstAt).toISOString();
132
- return `${activity.count} local-only event(s) since ${since}${types ? ` (${types})` : ""} \u2014 hooks are firing; these types never beep, so they are not sent.`;
132
+ return `${activity.count} local-only event(s) since ${since}${types ? ` (${types})` : ""}. Hooks are firing; these types never produce notifications, so they are not sent.`;
133
133
  }
134
134
  function machineIdentity() {
135
135
  return (0, import_agent_core.getMachineIdentity)();
@@ -217,18 +217,18 @@ function describeSurface(state) {
217
217
  function describeSurfaceCoverage(state, group) {
218
218
  if (state.coverage === "active") {
219
219
  const last = state.lastAt !== void 0 ? `, last ${new Date(state.lastAt).toISOString()}` : "";
220
- return `covered \u2014 ${state.events} event(s) from this build${last}`;
220
+ return `covered: ${state.events} event(s) from this build${last}`;
221
221
  }
222
222
  if (state.coverage === "wired") {
223
- return state.surface.shadowed === true ? `${group.displayName}'s hooks are installed and this build shares them, but another install comes first on PATH \u2014 it only runs if that order changes` : `${group.displayName}'s hooks are installed and this build shares them; nothing has fired from it yet`;
223
+ return state.surface.shadowed === true ? `${group.displayName}'s hooks are installed and this build shares them, but another install comes first on PATH. This build runs only if that order changes.` : `${group.displayName}'s hooks are installed and this build shares them; nothing has fired from it yet`;
224
224
  }
225
225
  if (!CONFIGURED_STATUSES.has(group.status)) {
226
- return `not covered \u2014 ${group.displayName} carries no BirdyBeep hooks, so this build cannot beep`;
226
+ return `not covered: ${group.displayName} carries no BirdyBeep hooks, so this build cannot produce notifications`;
227
227
  }
228
228
  const active = group.surfaces.filter((s) => s.coverage === "active").map(describeSurface);
229
229
  const delivering = active.join(", ");
230
230
  const verb = active.length === 1 ? "is" : "are";
231
- return `not covered \u2014 nothing has ever fired from this build, while ${delivering} ${verb} delivering through the same config`;
231
+ return `not covered: nothing has fired from this build, while ${delivering} ${verb} delivering through the same config`;
232
232
  }
233
233
  function installTarget(harness) {
234
234
  return harness === "claude_code" ? "claude" : harness;
@@ -237,7 +237,7 @@ function surfaceRemedy(state, group) {
237
237
  if (state.coverage !== "uncovered") return void 0;
238
238
  if (!CONFIGURED_STATUSES.has(group.status)) return void 0;
239
239
  const install = `\`birdybeep agent install ${installTarget(group.harness)}\``;
240
- return state.surface.kind === "desktop" ? `Run a turn in ${state.surface.label}. If it stays uncovered, that build cannot run the hook command: a desktop app spawns its engine with your LOGIN shell's PATH, not an interactive shell's, so a bare command is invisible to it. Re-run ${install} from a shell where \`birdybeep\` resolves \u2014 it rewrites the entry with absolute paths that need no PATH at all.` : `Run a turn in ${state.surface.label}. If it stays uncovered, re-run ${install} from a shell where \`birdybeep\` resolves, then check that ${state.surface.enginePath} is the build you are actually running.`;
240
+ return state.surface.kind === "desktop" ? `Run a turn in ${state.surface.label}. If it stays uncovered, that build cannot run the hook command: a desktop app spawns its engine with your LOGIN shell's PATH, not an interactive shell's, so a bare command is invisible to it. Re-run ${install} from a shell where \`birdybeep\` resolves. This rewrites the entry with absolute paths that do not depend on PATH.` : `Run a turn in ${state.surface.label}. If it stays uncovered, re-run ${install} from a shell where \`birdybeep\` resolves, then check that ${state.surface.enginePath} is the build you are actually running.`;
241
241
  }
242
242
 
243
243
  // src/framework.ts
@@ -322,7 +322,7 @@ function renderRootHelp(version, commands) {
322
322
  const lines = commands.map((c) => ` ${c.name.padEnd(width)} ${c.summary}`);
323
323
  const featured = commands.filter((c) => c.gettingStarted !== void 0);
324
324
  return [
325
- `birdybeep ${version} \u2014 stream coding-agent lifecycle events to BirdyBeep.`,
325
+ `birdybeep ${version}: phone alerts for coding agents.`,
326
326
  "",
327
327
  "Usage:",
328
328
  " birdybeep <command> [options]",
@@ -354,7 +354,7 @@ function commandFlagTokens(...commands) {
354
354
  }
355
355
  function renderCommandHelp(path, command) {
356
356
  const lines = [
357
- `birdybeep ${path} \u2014 ${command.summary}`,
357
+ `birdybeep ${path}: ${command.summary}`,
358
358
  "",
359
359
  "Usage:",
360
360
  ` ${command.usage ?? `birdybeep ${path} [options]`}`
@@ -533,12 +533,12 @@ async function installSelected(adapters, ctx, tokenOptions) {
533
533
  return EXIT.OK;
534
534
  }
535
535
  if (outcomes.length === 0 || outcomes.every((o) => !o.detected)) {
536
- ctx.io.line("No supported harnesses detected \u2014 nothing to install.");
536
+ ctx.io.line("No supported harnesses detected. Nothing was installed.");
537
537
  }
538
538
  for (const o of outcomes) {
539
539
  if (!o.detected) {
540
540
  ctx.io.line(
541
- `\u2013 ${o.displayName}: not detected (skipped) \u2014 install it, then run \`birdybeep agent install ${installTarget2(o.harness)}\``
541
+ `\u2013 ${o.displayName}: not detected (skipped). Install it, then run \`birdybeep agent install ${installTarget2(o.harness)}\`.`
542
542
  );
543
543
  continue;
544
544
  }
@@ -548,7 +548,7 @@ async function installSelected(adapters, ctx, tokenOptions) {
548
548
  }
549
549
  if (!paired) {
550
550
  ctx.io.line(
551
- "\u26A0 This machine is not paired, so nothing these hooks produce can reach you. Run `birdybeep setup` \u2014 it pairs, wires up every agent, and sends a test Beep."
551
+ "\u26A0 This machine is not paired. Run `birdybeep setup` before expecting notifications."
552
552
  );
553
553
  }
554
554
  return EXIT.OK;
@@ -739,15 +739,15 @@ function createDoctorCommand(deps = {}) {
739
739
  name: "Events lost while unpaired",
740
740
  ok: false,
741
741
  detail: describeUnpairedActivity(unpaired),
742
- remedy: "Run `birdybeep pair`. Events that fired before pairing are gone \u2014 a first pairing does not replay them."
742
+ remedy: "Run `birdybeep pair`. Events that fired before pairing were not saved and will not be replayed."
743
743
  });
744
744
  }
745
745
  if (await cursorBridgeOnly(deps.detectCursor ? { detect: deps.detectCursor } : {})) {
746
746
  checks.push({
747
747
  name: "Approval beeps from Cursor",
748
748
  ok: false,
749
- detail: "Cursor is running your agent through the Claude Code hooks \u2014 that is why Cursor events arrive without a Cursor install. Its bridge drops Notification and PermissionRequest, so approvals never reach you.",
750
- remedy: "Run `birdybeep agent install cursor` to get approval beeps from Cursor's own shell and MCP permission prompts. Keeping both installed is safe \u2014 duplicate events are collapsed."
749
+ detail: "Cursor is running your agent through the Claude Code hooks. This is why Cursor events arrive without a Cursor install. Its bridge drops Notification and PermissionRequest, so approvals never reach you.",
750
+ remedy: "Run `birdybeep agent install cursor` to get approval beeps from Cursor's own shell and MCP permission prompts. Keeping both installed is safe; duplicate events are collapsed."
751
751
  });
752
752
  }
753
753
  const filtered = filteredActivity();
@@ -796,7 +796,7 @@ function createDoctorCommand(deps = {}) {
796
796
  name: "Backend reachable",
797
797
  ok: false,
798
798
  detail: `Could not reach ${apiUrl}.`,
799
- remedy: "Check your network; queued events will retry automatically."
799
+ remedy: "Check your network. Queued events retry automatically."
800
800
  }
801
801
  );
802
802
  const ok = checks.every((c) => c.ok);
@@ -821,10 +821,10 @@ function createDoctorCommand(deps = {}) {
821
821
  });
822
822
  } else {
823
823
  for (const c of checks) {
824
- ctx.io.line(`${c.ok ? "\u2713" : "\u2717"} ${c.name}${c.detail ? ` \u2014 ${c.detail}` : ""}`);
824
+ ctx.io.line(`${c.ok ? "\u2713" : "\u2717"} ${c.name}${c.detail ? `: ${c.detail}` : ""}`);
825
825
  if (!c.ok && c.remedy) ctx.io.line(` \u2192 ${c.remedy}`);
826
826
  }
827
- ctx.io.line(ok ? "\nAll checks passed." : "\nSome checks failed \u2014 see fixes above.");
827
+ ctx.io.line(ok ? "\nAll checks passed." : "\nSome checks failed. See fixes above.");
828
828
  }
829
829
  return ok ? EXIT.OK : EXIT.ERROR;
830
830
  }
@@ -999,7 +999,7 @@ function createHookCommand(deps = {}) {
999
999
  const copilotEventName = harness === "copilot" && (0, import_copilot3.isCopilotHookEventName)(ctx.args[1]) ? ctx.args[1] : void 0;
1000
1000
  if (harness === "copilot" && copilotEventName === void 0) {
1001
1001
  ctx.io.errline(
1002
- `birdybeep hook copilot: second argument must be a Copilot hook event name, got ${JSON.stringify(ctx.args[1] ?? "(none)")} \u2014 nothing was sent.`
1002
+ `birdybeep hook copilot: second argument must be a Copilot hook event name, got ${JSON.stringify(ctx.args[1] ?? "(none)")}. Nothing was sent.`
1003
1003
  );
1004
1004
  return EXIT.USAGE;
1005
1005
  }
@@ -1017,7 +1017,7 @@ function createHookCommand(deps = {}) {
1017
1017
  }
1018
1018
  const drop = (reason, detail) => {
1019
1019
  ctx.io.result({ harness, outcome: "skipped", reason });
1020
- ctx.io.errline(`birdybeep hook ${harness}: ${detail} \u2014 nothing was sent.`);
1020
+ ctx.io.errline(`birdybeep hook ${harness}: ${detail}. Nothing was sent.`);
1021
1021
  return EXIT.ERROR;
1022
1022
  };
1023
1023
  if (read === null) {
@@ -1047,7 +1047,7 @@ function createHookCommand(deps = {}) {
1047
1047
  });
1048
1048
  const article = handler === "opencode" ? "an" : "a";
1049
1049
  ctx.io.errline(
1050
- `birdybeep hook ${harness}: ${describeDiscriminator(payload)} is not ${article} ${handler} hook event \u2014 nothing was sent. Check which tool is running this hook.`
1050
+ `birdybeep hook ${harness}: ${describeDiscriminator(payload)} is not ${article} ${handler} hook event. Nothing was sent. Check which tool is running this hook.`
1051
1051
  );
1052
1052
  return EXIT.ERROR;
1053
1053
  }
@@ -1061,17 +1061,23 @@ function createHookCommand(deps = {}) {
1061
1061
  eventType: result.eventType,
1062
1062
  ...result.send?.decision ? { decision: result.send.decision } : {},
1063
1063
  ...result.send?.status !== void 0 ? { status: result.send.status } : {},
1064
+ ...result.send?.queueCause !== void 0 ? { queueCause: result.send.queueCause } : {},
1064
1065
  ...result.send?.tokenStoreUnavailable !== void 0 ? { tokenStore: "unavailable" } : {}
1065
1066
  });
1066
1067
  if (result.outcome === "unpaired") {
1067
1068
  ctx.io.errline(
1068
- "birdybeep: this machine is not paired \u2014 the event was not sent and was not queued. Run `birdybeep pair` (or `birdybeep doctor` to see how many events this has cost)."
1069
+ "birdybeep: this machine is not paired. The event was not sent or queued. Run `birdybeep pair`, or run `birdybeep doctor` to see how many events were missed."
1069
1070
  );
1070
1071
  }
1071
- const unavailable = result.send?.tokenStoreUnavailable;
1072
+ if (result.outcome === "failed") {
1073
+ ctx.io.errline(
1074
+ "birdybeep: the event could not be sent or saved locally. It will not retry. Check that BirdyBeep can write to its user data directory, then run `birdybeep doctor`."
1075
+ );
1076
+ }
1077
+ const unavailable = result.outcome === "queued" ? result.send?.tokenStoreUnavailable : void 0;
1072
1078
  if (unavailable !== void 0) {
1073
1079
  ctx.io.errline(
1074
- `birdybeep: could not read the machine token (${unavailable.reason}) \u2014 the event was QUEUED, not sent. It will deliver once the token store is readable (unlock your keychain); \`birdybeep doctor\` drains the queue.`
1080
+ `birdybeep: the machine token is unreadable (${unavailable.reason}). The event is queued. Restore token-store access; \`birdybeep doctor\` drains the queue.`
1075
1081
  );
1076
1082
  }
1077
1083
  return EXIT.OK;
@@ -1089,7 +1095,7 @@ function createLogoutCommand(deps = {}) {
1089
1095
  usage: "birdybeep logout",
1090
1096
  run: async (ctx) => {
1091
1097
  await (0, import_agent_core6.clearToken)(deps.tokenOptions ?? {});
1092
- ctx.io.emit("Logged out \u2014 the machine token was removed.", { loggedOut: true });
1098
+ ctx.io.emit("Logged out. The machine token was removed.", { loggedOut: true });
1093
1099
  return EXIT.OK;
1094
1100
  }
1095
1101
  };
@@ -1116,14 +1122,14 @@ function createUnpairCommand(deps = {}) {
1116
1122
  const timeoutMs = deps.timeoutMs ?? 1e4;
1117
1123
  return {
1118
1124
  name: "unpair",
1119
- summary: "Unpair this machine \u2014 revoke it server-side and remove the local token",
1125
+ summary: "Revoke this machine and remove its local token",
1120
1126
  usage: "birdybeep unpair",
1121
1127
  run: async (ctx) => {
1122
1128
  const token = await (0, import_agent_core6.getToken)(deps.tokenOptions ?? {});
1123
1129
  const outcome = token === null ? "no_token" : await revokeSelf(token, fetchImpl, timeoutMs);
1124
1130
  await (0, import_agent_core6.clearToken)(deps.tokenOptions ?? {});
1125
1131
  const serverRevoked = outcome === "revoked";
1126
- const human = outcome === "revoked" ? "Unpaired \u2014 the machine was revoked and removed from your account." : outcome === "no_token" ? "Already unpaired \u2014 there was no local token to remove." : outcome === "unreachable" ? "Unpaired locally, but the server was unreachable \u2014 the machine may still show in the app. Open BirdyBeep and revoke it there to fully remove it." : "Unpaired locally, but the server didn't confirm removal \u2014 if the machine still shows in the app, revoke it there.";
1132
+ const human = outcome === "revoked" ? "Unpaired. The machine was revoked and removed from your account." : outcome === "no_token" ? "Already unpaired. There was no local token to remove." : outcome === "unreachable" ? "Unpaired locally, but the server was unreachable. The machine may still appear in the app. Open BirdyBeep and revoke it there." : "Unpaired locally, but the server did not confirm removal. If the machine still appears in the app, revoke it there.";
1127
1133
  ctx.io.emit(human, { unpaired: true, serverRevoked });
1128
1134
  return EXIT.OK;
1129
1135
  }
@@ -1206,7 +1212,7 @@ async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint,
1206
1212
  }
1207
1213
 
1208
1214
  // src/version.ts
1209
- var CLI_VERSION = "0.8.0".length > 0 ? "0.8.0" : "0.0.0";
1215
+ var CLI_VERSION = "0.8.1".length > 0 ? "0.8.1" : "0.0.0";
1210
1216
 
1211
1217
  // src/commands/setup.ts
1212
1218
  var import_claude_code5 = require("@birdybeep/claude-code");
@@ -1231,8 +1237,8 @@ function buildTestEvent(opts = {}) {
1231
1237
  source_session_id: `birdybeep-cli-test-${(0, import_node_crypto2.randomUUID)()}`,
1232
1238
  machine: { label: machine.label, os: machine.os },
1233
1239
  workspace: { cwd: process.cwd() },
1234
- title: "BirdyBeep test event",
1235
- body: "If you can see this, your machine is wired up correctly.",
1240
+ title: "BirdyBeep test",
1241
+ body: "Notifications from this machine are working.",
1236
1242
  metadata: { test: true }
1237
1243
  },
1238
1244
  opts
@@ -1267,43 +1273,43 @@ function createTestCommand(deps = {}) {
1267
1273
  });
1268
1274
  if (reach.state === "ok" && reach.data.active_device_count === 0) {
1269
1275
  ctx.io.line(
1270
- "\u26A0 The backend accepted the test event, but NO device on this account can receive it \u2014 nothing will arrive. Open the BirdyBeep app on your phone to register it (if it says the device limit is full, free a slot in Settings \u203A devices)."
1276
+ "\u26A0 No active device can receive a Beep. Open BirdyBeep on your phone and sign in to register it. If the device limit is full, free a slot in Settings \u203A devices."
1271
1277
  );
1272
1278
  } else if (reach.state === "ok") {
1273
1279
  ctx.io.line(
1274
- `\u2713 Test event accepted and queued for ${String(reach.data.active_device_count)} registered device(s) \u2014 check your phone for a test Beep.`
1280
+ `\u2713 Test event accepted for ${String(reach.data.active_device_count)} registered device(s). Check your phone for a test Beep.`
1275
1281
  );
1276
1282
  } else {
1277
- ctx.io.line("\u2713 Test event accepted by the backend \u2014 check your phone for a test Beep.");
1283
+ ctx.io.line("\u2713 Test event accepted by the backend. Check your phone for a test Beep.");
1278
1284
  }
1279
1285
  } else if (result.decision === "suppressed") {
1280
1286
  ctx.io.line(
1281
- "\u26A0 The backend accepted the test event but suppressed the push \u2014 this machine or integration is probably muted. Check mutes in the app, or run `birdybeep doctor`."
1287
+ "\u26A0 Test event accepted; push suppressed. Check machine and integration mutes in the app."
1282
1288
  );
1283
1289
  } else if (result.decision === "deduped") {
1284
- ctx.io.line(
1285
- "\u26A0 The backend accepted the test event but folded it into a recent duplicate \u2014 wait ~30s and run `birdybeep test` again."
1286
- );
1290
+ ctx.io.line("\u26A0 Test event matched a recent duplicate. Retry in 30 seconds.");
1287
1291
  } else {
1288
1292
  ctx.io.line(
1289
- `\u26A0 The backend accepted the test event but decided "${result.decision}" \u2014 no push was sent. Run \`birdybeep doctor\`.`
1293
+ `\u26A0 Test event accepted; push decision was "${result.decision}". Run \`birdybeep doctor\`.`
1290
1294
  );
1291
1295
  }
1292
1296
  } else if (result.outcome === "unpaired") {
1293
1297
  ctx.io.line(
1294
- "\u2717 NOT PAIRED \u2014 this machine has no BirdyBeep machine token, so nothing was sent (and nothing was queued). Run `birdybeep pair`."
1298
+ "\u2717 This machine is not paired. Run `birdybeep pair`. No event was sent or queued."
1295
1299
  );
1296
- } else if (result.tokenStoreUnavailable !== void 0) {
1300
+ } else if (result.outcome === "failed") {
1297
1301
  ctx.io.line(
1298
- `\u2022 Could not read the machine token (${result.tokenStoreUnavailable.reason}) \u2014 the test event was queued and delivers once the token store is readable. If your keychain is locked, unlock it and run \`birdybeep test\` again.`
1302
+ "\u2717 The test event could not be sent or saved locally. Check BirdyBeep's data-directory permissions, then retry."
1299
1303
  );
1300
- } else if (result.outcome === "queued" && result.queueCause === "backend") {
1301
- const status = result.status !== void 0 ? ` (HTTP ${String(result.status)})` : "";
1304
+ } else if (result.tokenStoreUnavailable !== void 0) {
1302
1305
  ctx.io.line(
1303
- result.code === "rate_limited" || result.status === 429 ? `\u2022 Throttled by the backend${status} \u2014 test event queued; it retries on its own. Not your network: the request got through.` : `\u2022 The backend is having trouble${status} \u2014 test event queued; it retries on its own. Not your network: the request got through.`
1306
+ `\u2022 The machine token is unreadable (${result.tokenStoreUnavailable.reason}). The test event is queued. Restore token-store access, then retry.`
1304
1307
  );
1308
+ } else if (result.outcome === "queued" && result.queueCause === "backend") {
1309
+ const status = result.status !== void 0 ? ` HTTP ${String(result.status)}` : " an error";
1310
+ ctx.io.line(`\u2022 Backend returned${status}. The test event is queued for retry.`);
1305
1311
  } else if (result.outcome === "queued") {
1306
- ctx.io.line("\u2022 Offline \u2014 test event queued; it will deliver when you reconnect.");
1312
+ ctx.io.line("\u2022 Could not reach the backend. The test event is queued.");
1307
1313
  } else if (result.code === "quota_exceeded") {
1308
1314
  const reach = await (0, import_agent_core8.fetchPushReachability)({
1309
1315
  baseUrl,
@@ -1311,18 +1317,18 @@ function createTestCommand(deps = {}) {
1311
1317
  ...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
1312
1318
  });
1313
1319
  const quota = reach.state === "ok" ? reach.data.quota : void 0;
1314
- const exhausted = quota ? (0, import_agent_core8.describeExhaustedQuota)(quota) : void 0;
1320
+ const exhausted = quota !== void 0 && quota.beeps_limit !== null ? (0, import_agent_core8.describeExhaustedQuota)({ ...quota, beeps_limit: quota.beeps_limit }) : void 0;
1315
1321
  ctx.io.line(
1316
- quota && exhausted ? `\u2717 Test event REJECTED \u2014 this account's monthly beep quota is used up (${String(quota.beeps_accepted)}/${String(quota.beeps_limit)} beeps on the ${quota.plan} plan, period ${exhausted.window}). Every notifiable event is being rejected. ${exhausted.remedy}` : (
1322
+ quota?.beeps_limit === null ? "\u2717 Test event rejected. The backend rejected this event for quota, but this account now reports unlimited beeps on Plus. The plan changed between those requests; run `birdybeep test` again. If it repeats, run `birdybeep doctor`." : quota && exhausted ? `\u2717 Test event rejected. This account's monthly beep quota is used up (${String(quota.beeps_accepted)}/${String(quota.beeps_limit)} beeps on the ${quota.plan} plan, period ${exhausted.window}). Every notifiable event is being rejected. ${exhausted.remedy}` : (
1317
1323
  // An older backend reports no quota, so there is no period and no reset date to
1318
1324
  // give — and `doctor` cannot supply them either, since it reads this same response.
1319
- "\u2717 Test event REJECTED \u2014 this account's monthly beep quota is used up, so no beep can be sent. This server does not report the period or the reset date; check your usage in the BirdyBeep app."
1325
+ "\u2717 Test event rejected. This account's monthly beep quota is used up, so no Beep can be sent. This server does not report the period or the reset date; check your usage in the BirdyBeep app."
1320
1326
  )
1321
1327
  );
1322
1328
  } else {
1323
1329
  ctx.io.line("\u2717 Test event was rejected by the backend. Run `birdybeep doctor`.");
1324
1330
  }
1325
- return result.outcome === "dropped" || result.outcome === "unpaired" ? EXIT.ERROR : EXIT.OK;
1331
+ return result.outcome === "dropped" || result.outcome === "unpaired" || result.outcome === "failed" ? EXIT.ERROR : EXIT.OK;
1326
1332
  }
1327
1333
  };
1328
1334
  }
@@ -1422,7 +1428,7 @@ function buildHarnessReports(installs, groups) {
1422
1428
  }
1423
1429
  ] : surfaces.map((state) => {
1424
1430
  const graded = rowState(state, group, status);
1425
- const remedy = surfaceRemedy(state, group) ?? (graded === "not covered" ? `${adapter.displayName} carries no BirdyBeep hooks \u2014 re-run \`birdybeep agent install ${installTarget2(adapter.id)}\` from a shell where \`birdybeep\` resolves.` : void 0);
1431
+ const remedy = surfaceRemedy(state, group) ?? (graded === "not covered" ? `${adapter.displayName} carries no BirdyBeep hooks. Re-run \`birdybeep agent install ${installTarget2(adapter.id)}\` from a shell where \`birdybeep\` resolves.` : void 0);
1426
1432
  return {
1427
1433
  harness: adapter.id,
1428
1434
  displayName: adapter.displayName,
@@ -1468,12 +1474,12 @@ function describeMissing(reports) {
1468
1474
  const names = missing.map((r) => r.displayName);
1469
1475
  if (missing.length === reports.length) {
1470
1476
  return [
1471
- "No supported coding agent is installed on this machine, so there was nothing to wire up.",
1472
- `Install one of ${names.join(", ")}, then run \`birdybeep setup\` again \u2014 pairing is already done, so it picks up from here.`
1477
+ "No supported coding agent is installed on this machine.",
1478
+ `Install one of ${names.join(", ")}, then run \`birdybeep setup\` again.`
1473
1479
  ];
1474
1480
  }
1475
1481
  return [
1476
- `Not installed: ${names.join(", ")}. Install any of them, then run \`birdybeep setup\` again to wire it up.`
1482
+ `Not installed: ${names.join(", ")}. Install any of them, then run \`birdybeep setup\` again.`
1477
1483
  ];
1478
1484
  }
1479
1485
  async function runHarnessSetup(ctx, options, deps = {}) {
@@ -1696,7 +1702,7 @@ function createPairingCommand(verb, deps = {}) {
1696
1702
  },
1697
1703
  {
1698
1704
  flag: "--no-install",
1699
- summary: "Stop after pairing \u2014 don't detect or wire up any coding agent"
1705
+ summary: "Stop after pairing; do not install coding-agent hooks"
1700
1706
  },
1701
1707
  {
1702
1708
  flag: "--no-test",
@@ -1716,7 +1722,7 @@ function createPairingCommand(verb, deps = {}) {
1716
1722
  };
1717
1723
  if (verb.skipWhenPaired && await (0, import_agent_core9.getToken)(deps.tokenOptions ?? {}) !== null) {
1718
1724
  ctx.io.line(
1719
- chain !== void 0 ? "\u2713 Already paired \u2014 checking which coding agents are wired up." : "\u2713 Already paired. Nothing else to do with --no-install."
1725
+ chain !== void 0 ? "\u2713 Already paired. Checking installed coding-agent hooks." : "\u2713 Already paired. Nothing else to do with --no-install."
1720
1726
  );
1721
1727
  const report2 = chain !== void 0 ? await runSetupChain(ctx, setupDeps, pairFlags) : void 0;
1722
1728
  ctx.io.result({
@@ -1752,7 +1758,7 @@ function createPairingCommand(verb, deps = {}) {
1752
1758
  ctx.io.line(
1753
1759
  ` Session code (display only; cannot approve by itself): ${start.user_code}`
1754
1760
  );
1755
- ctx.io.line("Waiting for you to approve this machine in the app\u2026");
1761
+ ctx.io.line("Waiting for approval in the BirdyBeep app.");
1756
1762
  }
1757
1763
  const deadline = Date.parse(start.expires_at);
1758
1764
  const startedAt = clock();
@@ -1781,7 +1787,7 @@ function createPairingCommand(verb, deps = {}) {
1781
1787
  }
1782
1788
  if (!ctx.flags.json && nowMs - lastBeat >= HEARTBEAT_MS) {
1783
1789
  ctx.io.line(
1784
- poll.status === "error" ? ` still trying \u2014 the server is busy (${poll.message}). approve in the app when you can\u2026` : " still waiting \u2014 approve this machine in the BirdyBeep app\u2026"
1790
+ poll.status === "error" ? ` Backend unavailable (${poll.message}); retrying.` : " Waiting for approval in the BirdyBeep app."
1785
1791
  );
1786
1792
  lastBeat = nowMs;
1787
1793
  }
@@ -1821,7 +1827,7 @@ function createPairingCommand(verb, deps = {}) {
1821
1827
  if (decision.action === "prompt" && !isAffirmative(await promptLine(decision.question, decision.on))) {
1822
1828
  ctx.io.result({ paired: false, reason: "declined" });
1823
1829
  ctx.io.errline(
1824
- "Pairing declined \u2014 the machine token was NOT stored, and this machine will send no events. The machine may still appear in the BirdyBeep app; revoke it there if you did not intend to pair it."
1830
+ "Pairing declined. The machine token was NOT stored, and this machine will send no events. The machine may still appear in the BirdyBeep app; revoke it there if you did not intend to pair it."
1825
1831
  );
1826
1832
  return EXIT.ERROR;
1827
1833
  }
@@ -1830,8 +1836,8 @@ function createPairingCommand(verb, deps = {}) {
1830
1836
  const discarded = new import_agent_core9.LocalEventQueue().discardBefore(clock());
1831
1837
  (0, import_agent_core9.clearUnpairedNotice)();
1832
1838
  const humanSuffix = approvedBy !== void 0 ? ` to ${approvedBy}` : "";
1833
- const discardedSuffix = discarded > 0 ? ` Discarded ${discarded} event(s) queued before pairing \u2014 you won't be beeped about them.` : "";
1834
- const nextStep = chain === void 0 ? " Run `birdybeep setup` to wire up your coding agents." : "";
1839
+ const discardedSuffix = discarded > 0 ? ` Discarded ${discarded} event(s) queued before pairing. They will not produce notifications.` : "";
1840
+ const nextStep = chain === void 0 ? " Run `birdybeep setup` to install coding-agent hooks." : "";
1835
1841
  ctx.io.line(`\u2713 Paired${humanSuffix}.${nextStep}${discardedSuffix}`);
1836
1842
  const report = chain !== void 0 ? await runSetupChain(ctx, setupDeps, pairFlags) : void 0;
1837
1843
  ctx.io.result({
@@ -1849,7 +1855,7 @@ function createPairCommand(deps = {}) {
1849
1855
  return createPairingCommand(
1850
1856
  {
1851
1857
  name: "pair",
1852
- summary: "Pair this machine and wire up every coding agent on it",
1858
+ summary: "Pair this machine and install detected coding-agent hooks",
1853
1859
  usage: "birdybeep pair [--yes] [--expect-email <addr>] [--no-install] [--no-test] [--json]",
1854
1860
  skipWhenPaired: false
1855
1861
  },
@@ -1860,9 +1866,9 @@ function createSetupCommand(deps = {}) {
1860
1866
  return createPairingCommand(
1861
1867
  {
1862
1868
  name: "setup",
1863
- summary: "Set up BirdyBeep here: pair, wire up every coding agent, test",
1869
+ summary: "Set up BirdyBeep on this machine",
1864
1870
  usage: "birdybeep setup [--yes] [--expect-email <addr>] [--no-install] [--no-test] [--json]",
1865
- gettingStarted: "Pair this machine, wire up every coding agent it finds, and send a test Beep.",
1871
+ gettingStarted: "Connect this machine and install hooks for detected coding agents.",
1866
1872
  skipWhenPaired: true
1867
1873
  },
1868
1874
  deps
@@ -1935,7 +1941,7 @@ function createReportStatusCommand(deps = {}) {
1935
1941
  run: async (ctx) => {
1936
1942
  const token = await (0, import_agent_core11.getToken)(deps.tokenOptions ?? {});
1937
1943
  if (token === null) {
1938
- ctx.io.errline("No machine token \u2014 run `birdybeep pair` first.");
1944
+ ctx.io.errline("No machine token. Run `birdybeep pair` first.");
1939
1945
  return EXIT.ERROR;
1940
1946
  }
1941
1947
  const items = await gatherItems(adapters);
@@ -1980,12 +1986,12 @@ function createReportStatusCommand(deps = {}) {
1980
1986
  });
1981
1987
  } else if (outcome === "terminal") {
1982
1988
  ctx.io.errline(
1983
- `Report rejected (${errorCode ?? "auth"}) \u2014 your token may be revoked. Re-run \`birdybeep pair\`.`
1989
+ `Report rejected (${errorCode ?? "auth"}). Your token may be revoked. Re-run \`birdybeep pair\`.`
1984
1990
  );
1985
1991
  } else {
1986
1992
  for (const e of effective) {
1987
1993
  ctx.io.line(
1988
- outcome === "reported" ? `\u2713 ${e.harness}: ${e.status} (reported)` : `\u2022 ${e.harness}: ${e.status} (deferred \u2014 backend unreachable)`
1994
+ outcome === "reported" ? `\u2713 ${e.harness}: ${e.status} (reported)` : `\u2022 ${e.harness}: ${e.status} (deferred; backend unreachable)`
1989
1995
  );
1990
1996
  }
1991
1997
  }
@@ -2045,7 +2051,7 @@ function createStatusCommand(deps = {}) {
2045
2051
  } else {
2046
2052
  ctx.io.line(`Machine: ${machine.label} (${machine.os})`);
2047
2053
  ctx.io.line(
2048
- pairing.state === "paired" ? "Paired: yes" : pairing.state === "unpaired" ? "Paired: no \u2014 run `birdybeep pair`" : `Paired: unknown \u2014 ${describeTokenStoreUnavailable(pairing)}`
2054
+ pairing.state === "paired" ? "Paired: yes" : pairing.state === "unpaired" ? "Paired: no. Run `birdybeep pair`." : `Paired: unknown. ${describeTokenStoreUnavailable(pairing)}`
2049
2055
  );
2050
2056
  ctx.io.line("Integrations:");
2051
2057
  for (const i of integrations) {
@@ -2053,7 +2059,7 @@ function createStatusCommand(deps = {}) {
2053
2059
  const group = surfaces.find((g) => g.harness === i.harness);
2054
2060
  for (const state of group?.surfaces ?? []) {
2055
2061
  const mark = state.coverage === "active" ? "\u2713" : state.coverage === "wired" ? "\xB7" : "\u2717";
2056
- ctx.io.line(` ${mark} ${describeSurface(state)} \u2014 ${state.coverage}`);
2062
+ ctx.io.line(` ${mark} ${describeSurface(state)}: ${state.coverage}`);
2057
2063
  }
2058
2064
  }
2059
2065
  ctx.io.line(