@wibeco/bridge 0.2.5 → 0.2.7

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.
@@ -15,7 +15,14 @@ var hookEventKindSchema = z.enum([
15
15
  "shell.completed",
16
16
  "mcp.started",
17
17
  "mcp.completed",
18
+ "response.completed",
18
19
  "progress.shared",
20
+ "git.branch_changed",
21
+ "git.commit_created",
22
+ "git.push_completed",
23
+ "git.merge_completed",
24
+ "git.rebase_completed",
25
+ "git.stash_created",
19
26
  "unknown"
20
27
  ]);
21
28
  var safeScalarSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
@@ -33,7 +40,10 @@ var canonicalHookEventSchema = z.object({
33
40
  root: z.string().min(1),
34
41
  remote: z.string().min(1).optional(),
35
42
  branch: z.string().min(1).optional(),
36
- commit: z.string().min(1).optional()
43
+ commit: z.string().min(1).optional(),
44
+ defaultBranch: z.string().min(1).optional(),
45
+ baseCommit: z.string().min(1).optional(),
46
+ behindBy: z.number().int().nonnegative().optional()
37
47
  }).optional(),
38
48
  outcome: z.enum(["success", "failure", "cancelled", "unknown"]).optional(),
39
49
  durationMs: z.number().nonnegative().optional(),
@@ -153,7 +163,7 @@ var events = {
153
163
  "pre-tool-use": "tool.started",
154
164
  "post-tool-use": "tool.completed",
155
165
  "post-tool-use-failure": "tool.completed",
156
- notification: "lifecycle.after"
166
+ notification: "unknown"
157
167
  };
158
168
  var safeKeys = [
159
169
  "hook_event_name",
@@ -168,12 +178,16 @@ var safeKeys = [
168
178
  "session_started_at",
169
179
  "heartbeat_at",
170
180
  "heartbeat_interval_ms",
171
- "session_elapsed_ms"
181
+ "session_elapsed_ms",
182
+ "title"
172
183
  ];
173
184
  function mapClaudeCodeHook(eventName, input) {
174
185
  const payload = objectPayload(input);
175
186
  const toolInput = objectPayload(payload.tool_input);
176
187
  const sessionId = safeIdentifier(payload.session_id);
188
+ const title = safeIdentifier(
189
+ payload.session_name ?? payload.title ?? payload.name
190
+ );
177
191
  const model = safeModel(payload.model);
178
192
  const path = safeRelativePath(
179
193
  toolInput.file_path ?? payload.file_path,
@@ -188,6 +202,7 @@ function mapClaudeCodeHook(eventName, input) {
188
202
  {
189
203
  ...payload,
190
204
  ...sessionId ? { session_id: sessionId } : {},
205
+ ...title ? { title } : {},
191
206
  ...model ? { model } : {},
192
207
  ...path ? { path } : {},
193
208
  ...duration !== void 0 ? { duration_ms: duration } : {},
@@ -217,8 +232,8 @@ var events2 = {
217
232
  "user-prompt-submit": "lifecycle.before",
218
233
  "pre-tool-use": "tool.started",
219
234
  "post-tool-use": "tool.completed",
220
- "subagent-start": "lifecycle.before",
221
- "subagent-stop": "lifecycle.after",
235
+ "subagent-start": "unknown",
236
+ "subagent-stop": "unknown",
222
237
  stop: "lifecycle.after"
223
238
  };
224
239
  var safeKeys2 = [
@@ -234,7 +249,8 @@ var safeKeys2 = [
234
249
  "session_started_at",
235
250
  "heartbeat_at",
236
251
  "heartbeat_interval_ms",
237
- "session_elapsed_ms"
252
+ "session_elapsed_ms",
253
+ "title"
238
254
  ];
239
255
  function mapCodexHook(eventName, input) {
240
256
  const payload = objectPayload(input);
@@ -242,6 +258,9 @@ function mapCodexHook(eventName, input) {
242
258
  const threadId = safeIdentifier(
243
259
  payload["thread-id"] ?? payload.thread_id ?? payload.session_id
244
260
  );
261
+ const title = safeIdentifier(
262
+ payload["thread-name"] ?? payload.thread_name ?? payload.title
263
+ );
245
264
  const turnId = safeIdentifier(payload["turn-id"] ?? payload.turn_id);
246
265
  const model = safeModel(payload.model);
247
266
  const path = safeRelativePath(
@@ -254,6 +273,7 @@ function mapCodexHook(eventName, input) {
254
273
  {
255
274
  ...payload,
256
275
  ...threadId ? { session_id: threadId } : {},
276
+ ...title ? { title } : {},
257
277
  ...turnId ? { turn_id: turnId } : {},
258
278
  ...model ? { model } : {},
259
279
  ...path ? { path } : {},
@@ -293,11 +313,15 @@ var safeKeys3 = [
293
313
  "session_started_at",
294
314
  "heartbeat_at",
295
315
  "heartbeat_interval_ms",
296
- "session_elapsed_ms"
316
+ "session_elapsed_ms",
317
+ "title"
297
318
  ];
298
319
  function mapCursorHook(eventName, input) {
299
320
  const payload = objectPayload(input);
300
321
  const sessionId = safeIdentifier(payload.conversation_id ?? payload.session_id);
322
+ const title = safeIdentifier(
323
+ payload.conversation_name ?? payload.title ?? payload.name
324
+ );
301
325
  const model = safeModel(payload.model);
302
326
  const workspaceRoot = Array.isArray(payload.workspace_roots) ? payload.workspace_roots.find(
303
327
  (root) => typeof root === "string"
@@ -312,6 +336,7 @@ function mapCursorHook(eventName, input) {
312
336
  {
313
337
  ...payload,
314
338
  ...sessionId ? { session_id: sessionId } : {},
339
+ ...title ? { title } : {},
315
340
  ...model ? { model } : {},
316
341
  ...path ? { path } : {},
317
342
  ...typeof payload.lines_added === "number" ? { lines_added: payload.lines_added } : {},
@@ -329,7 +354,11 @@ function eventTypeForHook(event) {
329
354
  if (event.kind === "presence.heartbeat") return "presence.heartbeat";
330
355
  if (event.kind === "session.ended") return "presence.stopped";
331
356
  if (event.kind === "file.changed") return "workspace.files_changed";
357
+ if (event.kind === "response.completed") return "agent.response_completed";
332
358
  if (event.kind === "progress.shared") return "agent.progress_shared";
359
+ if (event.kind === "git.branch_changed" || event.kind === "git.commit_created" || event.kind === "git.push_completed" || event.kind === "git.merge_completed" || event.kind === "git.rebase_completed" || event.kind === "git.stash_created") {
360
+ return event.kind;
361
+ }
333
362
  if (event.kind === "shell.completed" && event.outcome && isTestHookEvent(event)) {
334
363
  return "workspace.test_completed";
335
364
  }
@@ -366,7 +395,18 @@ function toEnvelope(event, options) {
366
395
  agent_name: event.source,
367
396
  hook_kind: event.kind,
368
397
  lines_added: typeof event.metadata.lines_added === "number" ? event.metadata.lines_added : void 0,
369
- lines_deleted: typeof event.metadata.lines_deleted === "number" ? event.metadata.lines_deleted : void 0
398
+ lines_deleted: typeof event.metadata.lines_deleted === "number" ? event.metadata.lines_deleted : void 0,
399
+ task_lines_added: typeof event.metadata.task_lines_added === "number" ? event.metadata.task_lines_added : void 0,
400
+ task_lines_deleted: typeof event.metadata.task_lines_deleted === "number" ? event.metadata.task_lines_deleted : void 0,
401
+ task_paths: Array.isArray(event.metadata.task_paths) ? event.metadata.task_paths : void 0
402
+ } : event.kind === "response.completed" ? {
403
+ title: event.metadata.title,
404
+ paths,
405
+ lines_added: event.metadata.lines_added,
406
+ lines_deleted: event.metadata.lines_deleted,
407
+ tool: event.source,
408
+ agent_name: event.source,
409
+ hook_kind: event.kind
370
410
  } : event.kind === "presence.heartbeat" ? {
371
411
  verification: event.metadata.verification,
372
412
  sequence: event.metadata.sequence,
@@ -390,6 +430,21 @@ function toEnvelope(event, options) {
390
430
  tool: event.source,
391
431
  agent_name: event.source,
392
432
  hook_kind: event.kind
433
+ } : event.kind.startsWith("git.") ? {
434
+ title: event.metadata.title,
435
+ previous_branch: event.metadata.previous_branch,
436
+ branch: event.repo?.branch,
437
+ previous_commit: event.metadata.previous_commit,
438
+ commit_sha: event.metadata.commit_sha ?? event.repo?.commit,
439
+ stash_sha: event.metadata.stash_sha,
440
+ paths_known: event.metadata.paths_known,
441
+ paths,
442
+ changes: event.metadata.changes,
443
+ lines_added: event.metadata.lines_added,
444
+ lines_deleted: event.metadata.lines_deleted,
445
+ tool: event.source,
446
+ agent_name: event.source,
447
+ hook_kind: event.kind
393
448
  } : {
394
449
  paths,
395
450
  tool: event.source,
@@ -410,15 +465,26 @@ function toEnvelope(event, options) {
410
465
  source: "local_collector",
411
466
  type,
412
467
  visibility: "project",
413
- idempotency_key: `${event.source}:${event.id}`,
468
+ idempotency_key: gitIdempotencyKey(event, options) ?? `${event.source}:${event.id}`,
414
469
  correlation: {
415
470
  session_id: event.sessionId,
416
471
  branch: event.repo?.branch,
417
- commit_sha: event.repo?.commit
472
+ commit_sha: event.repo?.commit,
473
+ default_branch: event.repo?.defaultBranch,
474
+ base_commit_sha: event.repo?.baseCommit,
475
+ behind_by: event.repo?.behindBy
418
476
  },
419
477
  payload
420
478
  };
421
479
  }
480
+ function gitIdempotencyKey(event, options) {
481
+ if (!event.kind.startsWith("git.")) return void 0;
482
+ const identity = event.kind === "git.stash_created" ? event.metadata.stash_sha : event.metadata.commit_sha ?? event.repo?.commit;
483
+ if (typeof identity !== "string" || !identity) return void 0;
484
+ const repository = options.repositoryId ?? "repository";
485
+ const branch = event.repo?.branch ?? "detached";
486
+ return `git:${event.kind.slice(4)}:${repository}:${branch}:${identity}`;
487
+ }
422
488
  var SignedBatchClient = class {
423
489
  constructor(options) {
424
490
  this.options = options;
@@ -584,6 +650,9 @@ async function startPresenceSession(source, sessionId, cwd = process.cwd()) {
584
650
  sequence: 0,
585
651
  linesAdded: 0,
586
652
  linesDeleted: 0,
653
+ taskLinesAdded: 0,
654
+ taskLinesDeleted: 0,
655
+ taskPaths: [],
587
656
  paths: [],
588
657
  modelSeconds: {},
589
658
  toolCounts: {},
@@ -636,6 +705,20 @@ async function updatePresenceSession(source, sessionId, event, cwd = process.cwd
636
705
  const workingLinesDeleted = optionalCount(
637
706
  event.metadata.working_tree_lines_deleted
638
707
  );
708
+ if (event.kind === "lifecycle.before") {
709
+ state.taskBaselineLinesAdded = workingLinesAdded;
710
+ state.taskBaselineLinesDeleted = workingLinesDeleted;
711
+ state.taskLinesAdded = 0;
712
+ state.taskLinesDeleted = 0;
713
+ state.taskPaths = [];
714
+ }
715
+ if (event.kind === "file.changed") {
716
+ state.taskLinesAdded += safeCount(event.metadata.lines_added);
717
+ state.taskLinesDeleted += safeCount(event.metadata.lines_deleted);
718
+ if (typeof event.metadata.path === "string") {
719
+ state.taskPaths = [.../* @__PURE__ */ new Set([...state.taskPaths, event.metadata.path])];
720
+ }
721
+ }
639
722
  if (workingLinesAdded !== void 0) {
640
723
  state.baselineLinesAdded ??= workingLinesAdded;
641
724
  const workingDelta = Math.max(
@@ -652,6 +735,18 @@ async function updatePresenceSession(source, sessionId, event, cwd = process.cwd
652
735
  );
653
736
  state.linesDeleted = isHeartbeat && state.linesDeleted > MAX_SESSION_LINE_COUNT ? workingDelta : Math.max(state.linesDeleted, workingDelta);
654
737
  }
738
+ if (event.kind === "file.changed" && workingLinesAdded !== void 0 && state.taskBaselineLinesAdded !== void 0) {
739
+ state.taskLinesAdded = Math.max(
740
+ state.taskLinesAdded,
741
+ Math.max(0, workingLinesAdded - state.taskBaselineLinesAdded)
742
+ );
743
+ }
744
+ if (event.kind === "file.changed" && workingLinesDeleted !== void 0 && state.taskBaselineLinesDeleted !== void 0) {
745
+ state.taskLinesDeleted = Math.max(
746
+ state.taskLinesDeleted,
747
+ Math.max(0, workingLinesDeleted - state.taskBaselineLinesDeleted)
748
+ );
749
+ }
655
750
  const paths = [
656
751
  ...Array.isArray(event.metadata.paths) ? event.metadata.paths.filter(
657
752
  (path) => typeof path === "string"
@@ -670,8 +765,16 @@ async function updatePresenceSession(source, sessionId, event, cwd = process.cwd
670
765
  if (event.outcome === "success") state.testsPassed += 1;
671
766
  if (event.outcome === "failure") state.testsFailed += 1;
672
767
  }
768
+ const metrics = metricsFromState(state, measuredAt);
769
+ if (event.kind === "lifecycle.after") {
770
+ state.taskBaselineLinesAdded = workingLinesAdded;
771
+ state.taskBaselineLinesDeleted = workingLinesDeleted;
772
+ state.taskLinesAdded = 0;
773
+ state.taskLinesDeleted = 0;
774
+ state.taskPaths = [];
775
+ }
673
776
  await writePresenceState(statePath, state);
674
- return metricsFromState(state, measuredAt);
777
+ return metrics;
675
778
  }
676
779
  async function stopPresenceSession(source, sessionId, cwd = process.cwd()) {
677
780
  const statePath = presenceStatePath(source, sessionId, cwd);
@@ -730,6 +833,13 @@ async function readPresenceState(path) {
730
833
  linesDeleted: safeCount(value.linesDeleted),
731
834
  baselineLinesAdded: optionalCount(value.baselineLinesAdded),
732
835
  baselineLinesDeleted: optionalCount(value.baselineLinesDeleted),
836
+ taskBaselineLinesAdded: optionalCount(value.taskBaselineLinesAdded),
837
+ taskBaselineLinesDeleted: optionalCount(value.taskBaselineLinesDeleted),
838
+ taskLinesAdded: safeCount(value.taskLinesAdded),
839
+ taskLinesDeleted: safeCount(value.taskLinesDeleted),
840
+ taskPaths: Array.isArray(value.taskPaths) ? value.taskPaths.filter(
841
+ (path2) => typeof path2 === "string"
842
+ ) : [],
733
843
  paths: Array.isArray(value.paths) ? value.paths.filter((path2) => typeof path2 === "string") : [],
734
844
  model: typeof value.model === "string" ? canonicalModelName(value.model) : void 0,
735
845
  modelSeconds: canonicalModelSeconds(value.modelSeconds),
@@ -791,6 +901,9 @@ function metricsFromState(state, measuredAt) {
791
901
  active_seconds: Math.floor(sessionElapsedMs / 1e3),
792
902
  lines_added: state.linesAdded,
793
903
  lines_deleted: state.linesDeleted,
904
+ task_lines_added: state.taskLinesAdded,
905
+ task_lines_deleted: state.taskLinesDeleted,
906
+ task_paths: state.taskPaths,
794
907
  changed_files: state.paths.length,
795
908
  model_seconds: state.modelSeconds,
796
909
  tool_counts: state.toolCounts,
@@ -841,35 +954,240 @@ function delay(milliseconds) {
841
954
  }
842
955
 
843
956
  // src/repo.ts
957
+ import { createHash as createHash2 } from "crypto";
844
958
  import { execFile } from "child_process";
959
+ import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
960
+ import { homedir as homedir2 } from "os";
961
+ import { join as join2 } from "path";
845
962
  import { promisify } from "util";
846
963
  var execFileAsync = promisify(execFile);
964
+ function classifyHeadTransition(reflogAction, headParentCount) {
965
+ const reflog = reflogAction?.toLowerCase() ?? "";
966
+ if (reflog.startsWith("rebase")) return "git.rebase_completed";
967
+ if (reflog.startsWith("merge") && headParentCount && headParentCount > 1) {
968
+ return "git.merge_completed";
969
+ }
970
+ return reflog.startsWith("commit") ? "git.commit_created" : void 0;
971
+ }
972
+ function isObservedPush(previous, current, branchChanged = false) {
973
+ const reflog = current.reflogAction?.toLowerCase() ?? "";
974
+ return Boolean(
975
+ !branchChanged && !reflog.startsWith("pull") && !reflog.startsWith("fetch") && current.commit && current.upstreamCommit === current.commit && previous.upstreamCommit !== current.upstreamCommit
976
+ );
977
+ }
847
978
  async function git(cwd, args) {
848
979
  try {
849
980
  const { stdout } = await execFileAsync("git", args, {
850
981
  cwd,
851
982
  encoding: "utf8",
852
983
  timeout: 2e3,
853
- maxBuffer: 64 * 1024
984
+ maxBuffer: 4 * 1024 * 1024
854
985
  });
855
986
  return stdout.trim() || void 0;
856
987
  } catch {
857
988
  return void 0;
858
989
  }
859
990
  }
991
+ async function detectDefaultBranch(root) {
992
+ const symbolic = await git(root, [
993
+ "symbolic-ref",
994
+ "--short",
995
+ "refs/remotes/origin/HEAD"
996
+ ]);
997
+ const candidates = [
998
+ symbolic?.replace(/^origin\//, ""),
999
+ "main",
1000
+ "master"
1001
+ ].filter((value) => Boolean(value));
1002
+ for (const branch of [...new Set(candidates)]) {
1003
+ const commit = await git(root, [
1004
+ "rev-parse",
1005
+ "--verify",
1006
+ `refs/remotes/origin/${branch}`
1007
+ ]);
1008
+ if (commit) return { branch, commit };
1009
+ }
1010
+ return void 0;
1011
+ }
860
1012
  async function detectRepository(cwd = process.cwd()) {
861
1013
  const root = await git(cwd, ["rev-parse", "--show-toplevel"]);
862
1014
  if (!root) return void 0;
863
- const [remote, branch, commit] = await Promise.all([
1015
+ const [
1016
+ remote,
1017
+ branch,
1018
+ commit,
1019
+ upstreamCommit,
1020
+ stashCommit,
1021
+ parents,
1022
+ reflogAction,
1023
+ commitTitle
1024
+ ] = await Promise.all([
864
1025
  git(root, ["remote", "get-url", "origin"]),
865
1026
  git(root, ["branch", "--show-current"]),
866
- git(root, ["rev-parse", "HEAD"])
1027
+ git(root, ["rev-parse", "HEAD"]),
1028
+ git(root, ["rev-parse", "@{upstream}"]),
1029
+ git(root, ["rev-parse", "refs/stash"]),
1030
+ git(root, ["rev-list", "--parents", "-n", "1", "HEAD"]),
1031
+ git(root, ["reflog", "-1", "--format=%gs"]),
1032
+ git(root, ["log", "-1", "--format=%s"])
867
1033
  ]);
1034
+ const defaultBranch = await detectDefaultBranch(root);
1035
+ const baseCommit = branch && commit && defaultBranch ? branch === defaultBranch.branch ? commit : await git(root, [
1036
+ "merge-base",
1037
+ "HEAD",
1038
+ `refs/remotes/origin/${defaultBranch.branch}`
1039
+ ]) : void 0;
1040
+ const behindValue = commit && defaultBranch ? await git(root, [
1041
+ "rev-list",
1042
+ "--count",
1043
+ `HEAD..refs/remotes/origin/${defaultBranch.branch}`
1044
+ ]) : void 0;
1045
+ const behindBy = behindValue !== void 0 && /^\d+$/.test(behindValue) ? Number(behindValue) : void 0;
868
1046
  return {
869
1047
  root,
870
1048
  ...remote ? { remote: sanitizeRemote(remote) } : {},
871
1049
  ...branch ? { branch } : {},
872
- ...commit ? { commit } : {}
1050
+ ...commit ? { commit } : {},
1051
+ ...defaultBranch ? { defaultBranch: defaultBranch.branch } : {},
1052
+ ...baseCommit ? { baseCommit } : {},
1053
+ ...behindBy !== void 0 ? { behindBy } : {},
1054
+ ...upstreamCommit ? { upstreamCommit } : {},
1055
+ ...stashCommit ? { stashCommit } : {},
1056
+ ...parents ? { headParentCount: Math.max(0, parents.split(/\s+/).length - 1) } : {},
1057
+ ...reflogAction ? { reflogAction } : {},
1058
+ ...commitTitle ? { commitTitle } : {}
1059
+ };
1060
+ }
1061
+ async function observeRepositoryTransitions(source, sessionId, current) {
1062
+ const directory = process.env.WIBE_GIT_STATE_DIR ?? join2(homedir2(), ".wibe", "git-state");
1063
+ const key = createHash2("sha256").update(`${source}\0${sessionId ?? ""}\0${current.root}`).digest("hex");
1064
+ const statePath = join2(directory, `${key}.json`);
1065
+ let previous;
1066
+ try {
1067
+ previous = JSON.parse(await readFile3(statePath, "utf8"));
1068
+ } catch {
1069
+ previous = void 0;
1070
+ }
1071
+ await mkdir3(directory, { recursive: true, mode: 448 });
1072
+ await writeFile3(statePath, `${JSON.stringify(current)}
1073
+ `, { mode: 384 });
1074
+ if (!previous) return [];
1075
+ const transitions = [];
1076
+ const branchChanged = Boolean(current.branch) && Boolean(previous.branch) && current.branch !== previous.branch;
1077
+ if (branchChanged) {
1078
+ transitions.push({
1079
+ kind: "git.branch_changed",
1080
+ metadata: {
1081
+ title: "Changed branch",
1082
+ previous_branch: previous.branch,
1083
+ branch: current.branch
1084
+ }
1085
+ });
1086
+ }
1087
+ if (current.stashCommit && current.stashCommit !== previous.stashCommit) {
1088
+ transitions.push({
1089
+ kind: "git.stash_created",
1090
+ metadata: {
1091
+ title: "Stashed changes",
1092
+ stash_sha: current.stashCommit
1093
+ }
1094
+ });
1095
+ }
1096
+ if (!branchChanged && current.commit && previous.commit && current.commit !== previous.commit) {
1097
+ const change = await detectRevisionChanges(
1098
+ current.root,
1099
+ previous.commit,
1100
+ current.commit
1101
+ );
1102
+ const kind = classifyHeadTransition(
1103
+ current.reflogAction,
1104
+ current.headParentCount
1105
+ );
1106
+ if (kind) {
1107
+ transitions.push({
1108
+ kind,
1109
+ metadata: {
1110
+ title: kind === "git.rebase_completed" ? "Rebased" : kind === "git.merge_completed" ? "Merged" : current.commitTitle || "Committed",
1111
+ previous_commit: previous.commit,
1112
+ commit_sha: current.commit,
1113
+ paths_known: change.pathsKnown,
1114
+ paths: change.paths,
1115
+ changes: change.changes,
1116
+ lines_added: change.linesAdded,
1117
+ lines_deleted: change.linesDeleted
1118
+ }
1119
+ });
1120
+ }
1121
+ }
1122
+ if (isObservedPush(previous, current, branchChanged) && current.commit) {
1123
+ const change = previous.upstreamCommit ? await detectRevisionChanges(
1124
+ current.root,
1125
+ previous.upstreamCommit,
1126
+ current.commit
1127
+ ) : {
1128
+ pathsKnown: false,
1129
+ paths: [],
1130
+ changes: [],
1131
+ linesAdded: 0,
1132
+ linesDeleted: 0
1133
+ };
1134
+ transitions.push({
1135
+ kind: "git.push_completed",
1136
+ metadata: {
1137
+ title: `Pushed to ${current.branch ?? "remote"}`,
1138
+ commit_sha: current.commit,
1139
+ paths_known: change.pathsKnown,
1140
+ paths: change.paths,
1141
+ changes: change.changes,
1142
+ lines_added: change.linesAdded,
1143
+ lines_deleted: change.linesDeleted
1144
+ }
1145
+ });
1146
+ }
1147
+ return transitions;
1148
+ }
1149
+ async function detectRevisionChanges(root, previous, current) {
1150
+ const [numstat, nameStatus] = await Promise.all([
1151
+ git(root, ["diff", "--numstat", previous, current, "--"]),
1152
+ git(root, ["diff", "--name-status", previous, current, "--"])
1153
+ ]);
1154
+ const pathsKnown = numstat !== void 0 && nameStatus !== void 0;
1155
+ const statusByPath = /* @__PURE__ */ new Map();
1156
+ for (const line of nameStatus?.split("\n") ?? []) {
1157
+ if (!line) continue;
1158
+ const [status, ...pathParts] = line.split(" ");
1159
+ const path = pathParts.at(-1);
1160
+ if (!path || isSensitivePath(path)) continue;
1161
+ statusByPath.set(
1162
+ path,
1163
+ status?.startsWith("A") ? "added" : status?.startsWith("D") ? "deleted" : "modified"
1164
+ );
1165
+ }
1166
+ let linesAdded = 0;
1167
+ let linesDeleted = 0;
1168
+ const changes = [];
1169
+ for (const line of numstat?.split("\n") ?? []) {
1170
+ if (!line) continue;
1171
+ const [added, deleted, ...pathParts] = line.split(" ");
1172
+ const path = pathParts.join(" ");
1173
+ if (!path || isSensitivePath(path)) continue;
1174
+ const additions = /^\d+$/.test(added) ? Number(added) : 0;
1175
+ const deletions = /^\d+$/.test(deleted) ? Number(deleted) : 0;
1176
+ linesAdded += additions;
1177
+ linesDeleted += deletions;
1178
+ changes.push({
1179
+ path,
1180
+ change_type: statusByPath.get(path) ?? "modified",
1181
+ additions,
1182
+ deletions
1183
+ });
1184
+ }
1185
+ return {
1186
+ pathsKnown,
1187
+ paths: changes.map((change) => change.path).slice(0, 2e3),
1188
+ changes: changes.slice(0, 2e3),
1189
+ linesAdded,
1190
+ linesDeleted
873
1191
  };
874
1192
  }
875
1193
  async function detectWorkingTreeMetrics(root) {
@@ -1097,7 +1415,10 @@ export {
1097
1415
  stopPresenceSession,
1098
1416
  runPresenceHeartbeat,
1099
1417
  presenceStatePath,
1418
+ classifyHeadTransition,
1419
+ isObservedPush,
1100
1420
  detectRepository,
1421
+ observeRepositoryTransitions,
1101
1422
  detectWorkingTreeMetrics,
1102
1423
  sanitizeRemote,
1103
1424
  normalizeGitHubRepository,
@@ -12,12 +12,13 @@ import {
12
12
  mapCursorHook,
13
13
  matchesGitHubRepository,
14
14
  normalizeGitHubRepository,
15
+ observeRepositoryTransitions,
15
16
  pollDeviceToken,
16
17
  requestDeviceAuthorization,
17
18
  startPresenceSession,
18
19
  stopPresenceSession,
19
20
  updatePresenceSession
20
- } from "./chunk-RI4FCH2F.js";
21
+ } from "./chunk-O4MHMJZZ.js";
21
22
 
22
23
  // src/cli/commands.ts
23
24
  import { access, cp, mkdir, readFile, writeFile } from "fs/promises";
@@ -193,7 +194,7 @@ async function emitCommand(adapter, eventName, input) {
193
194
  const mapper = adapter === "cursor" ? mapCursorHook : adapter === "claude-code" ? mapClaudeCodeHook : mapCodexHook;
194
195
  let mappedEvent = mapper(eventName, input);
195
196
  const repo = await detectRepository(process.cwd());
196
- if (repo && (mappedEvent.kind === "session.started" || mappedEvent.kind === "session.ended" || mappedEvent.kind === "presence.heartbeat" || mappedEvent.kind === "file.changed")) {
197
+ if (repo && (mappedEvent.kind === "session.started" || mappedEvent.kind === "session.ended" || mappedEvent.kind === "lifecycle.before" || mappedEvent.kind === "presence.heartbeat" || mappedEvent.kind === "file.changed")) {
197
198
  const workingTree = await detectWorkingTreeMetrics(repo.root);
198
199
  if (workingTree) {
199
200
  mappedEvent = {
@@ -232,18 +233,41 @@ async function emitCommand(adapter, eventName, input) {
232
233
  };
233
234
  }
234
235
  } else {
235
- const metrics = await updatePresenceSession(
236
+ let metrics = await updatePresenceSession(
236
237
  adapter,
237
238
  mappedEvent.sessionId,
238
239
  mappedEvent
239
240
  );
241
+ if (!metrics && mappedEvent.kind === "lifecycle.before") {
242
+ await startPresenceSession(adapter, mappedEvent.sessionId);
243
+ metrics = await updatePresenceSession(
244
+ adapter,
245
+ mappedEvent.sessionId,
246
+ mappedEvent
247
+ );
248
+ }
240
249
  if (mappedEvent.kind === "file.changed" && metrics) {
241
250
  mappedEvent = {
242
251
  ...mappedEvent,
243
252
  metadata: {
244
253
  ...mappedEvent.metadata,
245
254
  lines_added: metrics.lines_added,
246
- lines_deleted: metrics.lines_deleted
255
+ lines_deleted: metrics.lines_deleted,
256
+ task_lines_added: metrics.task_lines_added,
257
+ task_lines_deleted: metrics.task_lines_deleted,
258
+ task_paths: metrics.task_paths
259
+ }
260
+ };
261
+ } else if (mappedEvent.kind === "lifecycle.after" && metrics && metrics.task_paths.length > 0) {
262
+ mappedEvent = {
263
+ ...mappedEvent,
264
+ kind: "response.completed",
265
+ metadata: {
266
+ ...mappedEvent.metadata,
267
+ title: "Agent update",
268
+ paths: metrics.task_paths,
269
+ lines_added: metrics.task_lines_added,
270
+ lines_deleted: metrics.task_lines_deleted
247
271
  }
248
272
  };
249
273
  } else if (mappedEvent.kind === "presence.heartbeat" && metrics) {
@@ -259,19 +283,36 @@ async function emitCommand(adapter, eventName, input) {
259
283
  if (mappedEvent.kind === "session.started" || mappedEvent.kind === "session.ended") {
260
284
  const publicMetadata = { ...mappedEvent.metadata };
261
285
  delete publicMetadata.working_tree_paths;
286
+ delete publicMetadata.task_paths;
262
287
  mappedEvent = { ...mappedEvent, metadata: publicMetadata };
263
288
  }
264
289
  const event = repo ? { ...mappedEvent, repo } : mappedEvent;
290
+ const repositoryEvents = repo ? (await observeRepositoryTransitions(
291
+ adapter,
292
+ mappedEvent.sessionId,
293
+ repo
294
+ )).map(
295
+ (transition) => createHookEvent({
296
+ source: adapter,
297
+ kind: transition.kind,
298
+ ...mappedEvent.sessionId ? { sessionId: mappedEvent.sessionId } : {},
299
+ repo,
300
+ metadata: transition.metadata
301
+ })
302
+ ) : [];
303
+ const publishableEvents = [event, ...repositoryEvents].filter(
304
+ (candidate) => eventTypeForHook(candidate)
305
+ );
265
306
  const queue = new JsonFileOfflineQueue(queuePath());
266
307
  const credential = await loadCredential(process.cwd());
267
308
  if (!credential) {
268
- if (eventTypeForHook(event)) await queue.enqueue([event]);
309
+ if (publishableEvents.length) await queue.enqueue(publishableEvents);
269
310
  return {
270
311
  exitCode: 0,
271
- message: eventTypeForHook(event) ? `Queued ${event.kind}; this repository is not connected to Wibe.` : `Ignored operational ${event.kind} hook.`
312
+ message: publishableEvents.length ? `Queued ${publishableEvents.length} event(s); this repository is not connected to Wibe.` : `Ignored operational ${event.kind} hook.`
272
313
  };
273
314
  }
274
- const result = await new SignedBatchClient({
315
+ const client = new SignedBatchClient({
275
316
  endpoint: `${credential.appUrl}/api/events/batch`,
276
317
  accessToken: credential.accessToken,
277
318
  organizationId: credential.organizationId,
@@ -279,10 +320,19 @@ async function emitCommand(adapter, eventName, input) {
279
320
  repositoryId: credential.repositoryId,
280
321
  deviceId: credential.deviceId,
281
322
  queue
282
- }).capture(event);
323
+ });
324
+ let sent = 0;
325
+ let remaining = await queue.size();
326
+ let deliveryError;
327
+ for (const candidate of publishableEvents) {
328
+ const result = await client.capture(candidate);
329
+ sent += result.sent;
330
+ remaining = result.remaining;
331
+ deliveryError ??= result.error;
332
+ }
283
333
  return {
284
- exitCode: result.error ? 1 : 0,
285
- message: result.error ? `Queued ${event.kind}; delivery failed: ${result.error}` : `Delivered ${result.sent} event(s); ${result.remaining} queued.`
334
+ exitCode: deliveryError ? 1 : 0,
335
+ message: deliveryError ? `Queued ${publishableEvents.length} event(s); delivery failed: ${deliveryError}` : publishableEvents.length ? `Delivered ${sent} event(s); ${remaining} queued.` : `Ignored operational ${event.kind} hook.`
286
336
  };
287
337
  }
288
338
  async function shareProgressCommand(options, cwd = process.cwd()) {
package/dist/cli.js CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  setupCommand,
7
7
  shareProgressCommand,
8
8
  statusCommand
9
- } from "./chunk-ZFVW6RVP.js";
9
+ } from "./chunk-VBQSVC34.js";
10
10
  import {
11
11
  runPresenceHeartbeat
12
- } from "./chunk-RI4FCH2F.js";
12
+ } from "./chunk-O4MHMJZZ.js";
13
13
 
14
14
  // src/cli.ts
15
15
  var HELP = `wibe-bridge <command>
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  emitCommand
4
- } from "./chunk-ZFVW6RVP.js";
5
- import "./chunk-RI4FCH2F.js";
4
+ } from "./chunk-VBQSVC34.js";
5
+ import "./chunk-O4MHMJZZ.js";
6
6
 
7
7
  // src/codex-hook.ts
8
8
  async function main() {
package/dist/index.d.ts CHANGED
@@ -19,7 +19,14 @@ declare const hookEventKindSchema: z.ZodEnum<{
19
19
  "shell.completed": "shell.completed";
20
20
  "mcp.started": "mcp.started";
21
21
  "mcp.completed": "mcp.completed";
22
+ "response.completed": "response.completed";
22
23
  "progress.shared": "progress.shared";
24
+ "git.branch_changed": "git.branch_changed";
25
+ "git.commit_created": "git.commit_created";
26
+ "git.push_completed": "git.push_completed";
27
+ "git.merge_completed": "git.merge_completed";
28
+ "git.rebase_completed": "git.rebase_completed";
29
+ "git.stash_created": "git.stash_created";
23
30
  unknown: "unknown";
24
31
  }>;
25
32
  type HookEventKind = z.infer<typeof hookEventKindSchema>;
@@ -49,7 +56,14 @@ declare const canonicalHookEventSchema: z.ZodObject<{
49
56
  "shell.completed": "shell.completed";
50
57
  "mcp.started": "mcp.started";
51
58
  "mcp.completed": "mcp.completed";
59
+ "response.completed": "response.completed";
52
60
  "progress.shared": "progress.shared";
61
+ "git.branch_changed": "git.branch_changed";
62
+ "git.commit_created": "git.commit_created";
63
+ "git.push_completed": "git.push_completed";
64
+ "git.merge_completed": "git.merge_completed";
65
+ "git.rebase_completed": "git.rebase_completed";
66
+ "git.stash_created": "git.stash_created";
53
67
  unknown: "unknown";
54
68
  }>;
55
69
  occurredAt: z.ZodString;
@@ -59,6 +73,9 @@ declare const canonicalHookEventSchema: z.ZodObject<{
59
73
  remote: z.ZodOptional<z.ZodString>;
60
74
  branch: z.ZodOptional<z.ZodString>;
61
75
  commit: z.ZodOptional<z.ZodString>;
76
+ defaultBranch: z.ZodOptional<z.ZodString>;
77
+ baseCommit: z.ZodOptional<z.ZodString>;
78
+ behindBy: z.ZodOptional<z.ZodNumber>;
62
79
  }, z.core.$strip>>;
63
80
  outcome: z.ZodOptional<z.ZodEnum<{
64
81
  unknown: "unknown";
@@ -76,7 +93,7 @@ declare function mapClaudeCodeHook(eventName: string, input: unknown): {
76
93
  id: string;
77
94
  version: 1;
78
95
  source: "cursor" | "claude-code" | "codex";
79
- kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "progress.shared" | "unknown";
96
+ kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "response.completed" | "progress.shared" | "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created" | "unknown";
80
97
  occurredAt: string;
81
98
  metadata: Record<string, SafeValue>;
82
99
  sessionId?: string | undefined;
@@ -85,6 +102,9 @@ declare function mapClaudeCodeHook(eventName: string, input: unknown): {
85
102
  remote?: string | undefined;
86
103
  branch?: string | undefined;
87
104
  commit?: string | undefined;
105
+ defaultBranch?: string | undefined;
106
+ baseCommit?: string | undefined;
107
+ behindBy?: number | undefined;
88
108
  } | undefined;
89
109
  outcome?: "unknown" | "success" | "failure" | "cancelled" | undefined;
90
110
  durationMs?: number | undefined;
@@ -94,7 +114,7 @@ declare function mapCodexHook(eventName: string, input: unknown): {
94
114
  id: string;
95
115
  version: 1;
96
116
  source: "cursor" | "claude-code" | "codex";
97
- kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "progress.shared" | "unknown";
117
+ kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "response.completed" | "progress.shared" | "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created" | "unknown";
98
118
  occurredAt: string;
99
119
  metadata: Record<string, SafeValue>;
100
120
  sessionId?: string | undefined;
@@ -103,6 +123,9 @@ declare function mapCodexHook(eventName: string, input: unknown): {
103
123
  remote?: string | undefined;
104
124
  branch?: string | undefined;
105
125
  commit?: string | undefined;
126
+ defaultBranch?: string | undefined;
127
+ baseCommit?: string | undefined;
128
+ behindBy?: number | undefined;
106
129
  } | undefined;
107
130
  outcome?: "unknown" | "success" | "failure" | "cancelled" | undefined;
108
131
  durationMs?: number | undefined;
@@ -112,7 +135,7 @@ declare function mapCursorHook(eventName: string, input: unknown): {
112
135
  id: string;
113
136
  version: 1;
114
137
  source: "cursor" | "claude-code" | "codex";
115
- kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "progress.shared" | "unknown";
138
+ kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "response.completed" | "progress.shared" | "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created" | "unknown";
116
139
  occurredAt: string;
117
140
  metadata: Record<string, SafeValue>;
118
141
  sessionId?: string | undefined;
@@ -121,6 +144,9 @@ declare function mapCursorHook(eventName: string, input: unknown): {
121
144
  remote?: string | undefined;
122
145
  branch?: string | undefined;
123
146
  commit?: string | undefined;
147
+ defaultBranch?: string | undefined;
148
+ baseCommit?: string | undefined;
149
+ behindBy?: number | undefined;
124
150
  } | undefined;
125
151
  outcome?: "unknown" | "success" | "failure" | "cancelled" | undefined;
126
152
  durationMs?: number | undefined;
@@ -239,7 +265,7 @@ interface SignedBatchClientOptions {
239
265
  batchSize?: number;
240
266
  timeoutMs?: number;
241
267
  }
242
- declare function eventTypeForHook(event: CanonicalHookEvent): "presence.heartbeat" | "presence.started" | "presence.stopped" | "workspace.files_changed" | "agent.progress_shared" | "workspace.test_completed" | null;
268
+ declare function eventTypeForHook(event: CanonicalHookEvent): "presence.heartbeat" | "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created" | "presence.started" | "presence.stopped" | "workspace.files_changed" | "agent.response_completed" | "agent.progress_shared" | "workspace.test_completed" | null;
243
269
  interface FlushResult {
244
270
  sent: number;
245
271
  remaining: number;
@@ -263,6 +289,9 @@ interface PresenceMetrics {
263
289
  active_seconds: number;
264
290
  lines_added: number;
265
291
  lines_deleted: number;
292
+ task_lines_added: number;
293
+ task_lines_deleted: number;
294
+ task_paths: string[];
266
295
  changed_files: number;
267
296
  model_seconds: Record<string, number>;
268
297
  tool_counts: Record<string, number>;
@@ -289,16 +318,31 @@ interface RepositoryInfo {
289
318
  remote?: string;
290
319
  branch?: string;
291
320
  commit?: string;
321
+ defaultBranch?: string;
322
+ baseCommit?: string;
323
+ behindBy?: number;
324
+ upstreamCommit?: string;
325
+ stashCommit?: string;
326
+ headParentCount?: number;
327
+ reflogAction?: string;
328
+ commitTitle?: string;
292
329
  }
293
330
  interface WorkingTreeMetrics {
294
331
  linesAdded: number;
295
332
  linesDeleted: number;
296
333
  paths: string[];
297
334
  }
335
+ interface RepositoryTransition {
336
+ kind: Extract<HookEventKind, "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created">;
337
+ metadata: Record<string, SafeValue>;
338
+ }
339
+ declare function classifyHeadTransition(reflogAction: string | undefined, headParentCount: number | undefined): RepositoryTransition["kind"] | undefined;
340
+ declare function isObservedPush(previous: RepositoryInfo, current: RepositoryInfo, branchChanged?: boolean): boolean;
298
341
  declare function detectRepository(cwd?: string): Promise<RepositoryInfo | undefined>;
342
+ declare function observeRepositoryTransitions(source: AgentSource, sessionId: string | undefined, current: RepositoryInfo): Promise<RepositoryTransition[]>;
299
343
  declare function detectWorkingTreeMetrics(root: string): Promise<WorkingTreeMetrics | undefined>;
300
344
  declare function sanitizeRemote(remote: string): string;
301
345
  declare function normalizeGitHubRepository(value: string): string | undefined;
302
346
  declare function matchesGitHubRepository(remote: string | undefined, expected: string): boolean;
303
347
 
304
- export { type AgentSource, type CanonicalHookEvent, type CredentialStore, type DeviceAuthorization, type DeviceTokenResponse, type FlushResult, type HookEventKind, JsonFileOfflineQueue, MemoryOfflineQueue, type OfflineQueue, PRESENCE_HEARTBEAT_INTERVAL_MS, type PresenceMetrics, type RedactionOptions, type RepositoryInfo, type SafeValue, SignedBatchClient, type SignedBatchClientOptions, type StoredCredential, SystemCredentialStore, type WorkingTreeMetrics, agentSourceSchema, canonicalHookEventSchema, createHookEvent, detectRepository, detectWorkingTreeMetrics, deviceAuthorizationSchema, deviceTokenResponseSchema, eventTypeForHook, hookEventKindSchema, mapClaudeCodeHook, mapCodexHook, mapCursorHook, matchesGitHubRepository, normalizeGitHubRepository, pollDeviceToken, presenceStatePath, redact, requestDeviceAuthorization, runPresenceHeartbeat, safeMetadata, safeValueSchema, sanitizeRemote, startPresenceSession, stopPresenceSession, updatePresenceSession };
348
+ export { type AgentSource, type CanonicalHookEvent, type CredentialStore, type DeviceAuthorization, type DeviceTokenResponse, type FlushResult, type HookEventKind, JsonFileOfflineQueue, MemoryOfflineQueue, type OfflineQueue, PRESENCE_HEARTBEAT_INTERVAL_MS, type PresenceMetrics, type RedactionOptions, type RepositoryInfo, type RepositoryTransition, type SafeValue, SignedBatchClient, type SignedBatchClientOptions, type StoredCredential, SystemCredentialStore, type WorkingTreeMetrics, agentSourceSchema, canonicalHookEventSchema, classifyHeadTransition, createHookEvent, detectRepository, detectWorkingTreeMetrics, deviceAuthorizationSchema, deviceTokenResponseSchema, eventTypeForHook, hookEventKindSchema, isObservedPush, mapClaudeCodeHook, mapCodexHook, mapCursorHook, matchesGitHubRepository, normalizeGitHubRepository, observeRepositoryTransitions, pollDeviceToken, presenceStatePath, redact, requestDeviceAuthorization, runPresenceHeartbeat, safeMetadata, safeValueSchema, sanitizeRemote, startPresenceSession, stopPresenceSession, updatePresenceSession };
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  SystemCredentialStore,
7
7
  agentSourceSchema,
8
8
  canonicalHookEventSchema,
9
+ classifyHeadTransition,
9
10
  createHookEvent,
10
11
  detectRepository,
11
12
  detectWorkingTreeMetrics,
@@ -13,11 +14,13 @@ import {
13
14
  deviceTokenResponseSchema,
14
15
  eventTypeForHook,
15
16
  hookEventKindSchema,
17
+ isObservedPush,
16
18
  mapClaudeCodeHook,
17
19
  mapCodexHook,
18
20
  mapCursorHook,
19
21
  matchesGitHubRepository,
20
22
  normalizeGitHubRepository,
23
+ observeRepositoryTransitions,
21
24
  pollDeviceToken,
22
25
  presenceStatePath,
23
26
  redact,
@@ -29,7 +32,7 @@ import {
29
32
  startPresenceSession,
30
33
  stopPresenceSession,
31
34
  updatePresenceSession
32
- } from "./chunk-RI4FCH2F.js";
35
+ } from "./chunk-O4MHMJZZ.js";
33
36
  export {
34
37
  JsonFileOfflineQueue,
35
38
  MemoryOfflineQueue,
@@ -38,6 +41,7 @@ export {
38
41
  SystemCredentialStore,
39
42
  agentSourceSchema,
40
43
  canonicalHookEventSchema,
44
+ classifyHeadTransition,
41
45
  createHookEvent,
42
46
  detectRepository,
43
47
  detectWorkingTreeMetrics,
@@ -45,11 +49,13 @@ export {
45
49
  deviceTokenResponseSchema,
46
50
  eventTypeForHook,
47
51
  hookEventKindSchema,
52
+ isObservedPush,
48
53
  mapClaudeCodeHook,
49
54
  mapCodexHook,
50
55
  mapCursorHook,
51
56
  matchesGitHubRepository,
52
57
  normalizeGitHubRepository,
58
+ observeRepositoryTransitions,
53
59
  pollDeviceToken,
54
60
  presenceStatePath,
55
61
  redact,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wibeco/bridge",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Privacy-first live activity bridge for Cursor, Claude Code, and Codex.",
5
5
  "repository": {
6
6
  "type": "git",