@wibeco/bridge 0.2.4 → 0.2.6

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.
@@ -17,7 +17,7 @@ import {
17
17
  startPresenceSession,
18
18
  stopPresenceSession,
19
19
  updatePresenceSession
20
- } from "./chunk-RI4FCH2F.js";
20
+ } from "./chunk-WVBSXZG3.js";
21
21
 
22
22
  // src/cli/commands.ts
23
23
  import { access, cp, mkdir, readFile, writeFile } from "fs/promises";
@@ -193,7 +193,7 @@ async function emitCommand(adapter, eventName, input) {
193
193
  const mapper = adapter === "cursor" ? mapCursorHook : adapter === "claude-code" ? mapClaudeCodeHook : mapCodexHook;
194
194
  let mappedEvent = mapper(eventName, input);
195
195
  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")) {
196
+ if (repo && (mappedEvent.kind === "session.started" || mappedEvent.kind === "session.ended" || mappedEvent.kind === "lifecycle.before" || mappedEvent.kind === "presence.heartbeat" || mappedEvent.kind === "file.changed")) {
197
197
  const workingTree = await detectWorkingTreeMetrics(repo.root);
198
198
  if (workingTree) {
199
199
  mappedEvent = {
@@ -232,18 +232,29 @@ async function emitCommand(adapter, eventName, input) {
232
232
  };
233
233
  }
234
234
  } else {
235
- const metrics = await updatePresenceSession(
235
+ let metrics = await updatePresenceSession(
236
236
  adapter,
237
237
  mappedEvent.sessionId,
238
238
  mappedEvent
239
239
  );
240
+ if (!metrics && mappedEvent.kind === "lifecycle.before") {
241
+ await startPresenceSession(adapter, mappedEvent.sessionId);
242
+ metrics = await updatePresenceSession(
243
+ adapter,
244
+ mappedEvent.sessionId,
245
+ mappedEvent
246
+ );
247
+ }
240
248
  if (mappedEvent.kind === "file.changed" && metrics) {
241
249
  mappedEvent = {
242
250
  ...mappedEvent,
243
251
  metadata: {
244
252
  ...mappedEvent.metadata,
245
253
  lines_added: metrics.lines_added,
246
- lines_deleted: metrics.lines_deleted
254
+ lines_deleted: metrics.lines_deleted,
255
+ task_lines_added: metrics.task_lines_added,
256
+ task_lines_deleted: metrics.task_lines_deleted,
257
+ task_paths: metrics.task_paths
247
258
  }
248
259
  };
249
260
  } else if (mappedEvent.kind === "presence.heartbeat" && metrics) {
@@ -259,6 +270,7 @@ async function emitCommand(adapter, eventName, input) {
259
270
  if (mappedEvent.kind === "session.started" || mappedEvent.kind === "session.ended") {
260
271
  const publicMetadata = { ...mappedEvent.metadata };
261
272
  delete publicMetadata.working_tree_paths;
273
+ delete publicMetadata.task_paths;
262
274
  mappedEvent = { ...mappedEvent, metadata: publicMetadata };
263
275
  }
264
276
  const event = repo ? { ...mappedEvent, repo } : mappedEvent;
@@ -33,7 +33,10 @@ var canonicalHookEventSchema = z.object({
33
33
  root: z.string().min(1),
34
34
  remote: z.string().min(1).optional(),
35
35
  branch: z.string().min(1).optional(),
36
- commit: z.string().min(1).optional()
36
+ commit: z.string().min(1).optional(),
37
+ defaultBranch: z.string().min(1).optional(),
38
+ baseCommit: z.string().min(1).optional(),
39
+ behindBy: z.number().int().nonnegative().optional()
37
40
  }).optional(),
38
41
  outcome: z.enum(["success", "failure", "cancelled", "unknown"]).optional(),
39
42
  durationMs: z.number().nonnegative().optional(),
@@ -366,7 +369,10 @@ function toEnvelope(event, options) {
366
369
  agent_name: event.source,
367
370
  hook_kind: event.kind,
368
371
  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
372
+ lines_deleted: typeof event.metadata.lines_deleted === "number" ? event.metadata.lines_deleted : void 0,
373
+ task_lines_added: typeof event.metadata.task_lines_added === "number" ? event.metadata.task_lines_added : void 0,
374
+ task_lines_deleted: typeof event.metadata.task_lines_deleted === "number" ? event.metadata.task_lines_deleted : void 0,
375
+ task_paths: Array.isArray(event.metadata.task_paths) ? event.metadata.task_paths : void 0
370
376
  } : event.kind === "presence.heartbeat" ? {
371
377
  verification: event.metadata.verification,
372
378
  sequence: event.metadata.sequence,
@@ -414,7 +420,10 @@ function toEnvelope(event, options) {
414
420
  correlation: {
415
421
  session_id: event.sessionId,
416
422
  branch: event.repo?.branch,
417
- commit_sha: event.repo?.commit
423
+ commit_sha: event.repo?.commit,
424
+ default_branch: event.repo?.defaultBranch,
425
+ base_commit_sha: event.repo?.baseCommit,
426
+ behind_by: event.repo?.behindBy
418
427
  },
419
428
  payload
420
429
  };
@@ -584,6 +593,9 @@ async function startPresenceSession(source, sessionId, cwd = process.cwd()) {
584
593
  sequence: 0,
585
594
  linesAdded: 0,
586
595
  linesDeleted: 0,
596
+ taskLinesAdded: 0,
597
+ taskLinesDeleted: 0,
598
+ taskPaths: [],
587
599
  paths: [],
588
600
  modelSeconds: {},
589
601
  toolCounts: {},
@@ -636,6 +648,20 @@ async function updatePresenceSession(source, sessionId, event, cwd = process.cwd
636
648
  const workingLinesDeleted = optionalCount(
637
649
  event.metadata.working_tree_lines_deleted
638
650
  );
651
+ if (event.kind === "lifecycle.before") {
652
+ state.taskBaselineLinesAdded = workingLinesAdded;
653
+ state.taskBaselineLinesDeleted = workingLinesDeleted;
654
+ state.taskLinesAdded = 0;
655
+ state.taskLinesDeleted = 0;
656
+ state.taskPaths = [];
657
+ }
658
+ if (event.kind === "file.changed") {
659
+ state.taskLinesAdded += safeCount(event.metadata.lines_added);
660
+ state.taskLinesDeleted += safeCount(event.metadata.lines_deleted);
661
+ if (typeof event.metadata.path === "string") {
662
+ state.taskPaths = [.../* @__PURE__ */ new Set([...state.taskPaths, event.metadata.path])];
663
+ }
664
+ }
639
665
  if (workingLinesAdded !== void 0) {
640
666
  state.baselineLinesAdded ??= workingLinesAdded;
641
667
  const workingDelta = Math.max(
@@ -652,6 +678,18 @@ async function updatePresenceSession(source, sessionId, event, cwd = process.cwd
652
678
  );
653
679
  state.linesDeleted = isHeartbeat && state.linesDeleted > MAX_SESSION_LINE_COUNT ? workingDelta : Math.max(state.linesDeleted, workingDelta);
654
680
  }
681
+ if (event.kind === "file.changed" && workingLinesAdded !== void 0 && state.taskBaselineLinesAdded !== void 0) {
682
+ state.taskLinesAdded = Math.max(
683
+ state.taskLinesAdded,
684
+ Math.max(0, workingLinesAdded - state.taskBaselineLinesAdded)
685
+ );
686
+ }
687
+ if (event.kind === "file.changed" && workingLinesDeleted !== void 0 && state.taskBaselineLinesDeleted !== void 0) {
688
+ state.taskLinesDeleted = Math.max(
689
+ state.taskLinesDeleted,
690
+ Math.max(0, workingLinesDeleted - state.taskBaselineLinesDeleted)
691
+ );
692
+ }
655
693
  const paths = [
656
694
  ...Array.isArray(event.metadata.paths) ? event.metadata.paths.filter(
657
695
  (path) => typeof path === "string"
@@ -730,6 +768,13 @@ async function readPresenceState(path) {
730
768
  linesDeleted: safeCount(value.linesDeleted),
731
769
  baselineLinesAdded: optionalCount(value.baselineLinesAdded),
732
770
  baselineLinesDeleted: optionalCount(value.baselineLinesDeleted),
771
+ taskBaselineLinesAdded: optionalCount(value.taskBaselineLinesAdded),
772
+ taskBaselineLinesDeleted: optionalCount(value.taskBaselineLinesDeleted),
773
+ taskLinesAdded: safeCount(value.taskLinesAdded),
774
+ taskLinesDeleted: safeCount(value.taskLinesDeleted),
775
+ taskPaths: Array.isArray(value.taskPaths) ? value.taskPaths.filter(
776
+ (path2) => typeof path2 === "string"
777
+ ) : [],
733
778
  paths: Array.isArray(value.paths) ? value.paths.filter((path2) => typeof path2 === "string") : [],
734
779
  model: typeof value.model === "string" ? canonicalModelName(value.model) : void 0,
735
780
  modelSeconds: canonicalModelSeconds(value.modelSeconds),
@@ -791,6 +836,9 @@ function metricsFromState(state, measuredAt) {
791
836
  active_seconds: Math.floor(sessionElapsedMs / 1e3),
792
837
  lines_added: state.linesAdded,
793
838
  lines_deleted: state.linesDeleted,
839
+ task_lines_added: state.taskLinesAdded,
840
+ task_lines_deleted: state.taskLinesDeleted,
841
+ task_paths: state.taskPaths,
794
842
  changed_files: state.paths.length,
795
843
  model_seconds: state.modelSeconds,
796
844
  tool_counts: state.toolCounts,
@@ -857,6 +905,27 @@ async function git(cwd, args) {
857
905
  return void 0;
858
906
  }
859
907
  }
908
+ async function detectDefaultBranch(root) {
909
+ const symbolic = await git(root, [
910
+ "symbolic-ref",
911
+ "--short",
912
+ "refs/remotes/origin/HEAD"
913
+ ]);
914
+ const candidates = [
915
+ symbolic?.replace(/^origin\//, ""),
916
+ "main",
917
+ "master"
918
+ ].filter((value) => Boolean(value));
919
+ for (const branch of [...new Set(candidates)]) {
920
+ const commit = await git(root, [
921
+ "rev-parse",
922
+ "--verify",
923
+ `refs/remotes/origin/${branch}`
924
+ ]);
925
+ if (commit) return { branch, commit };
926
+ }
927
+ return void 0;
928
+ }
860
929
  async function detectRepository(cwd = process.cwd()) {
861
930
  const root = await git(cwd, ["rev-parse", "--show-toplevel"]);
862
931
  if (!root) return void 0;
@@ -865,11 +934,26 @@ async function detectRepository(cwd = process.cwd()) {
865
934
  git(root, ["branch", "--show-current"]),
866
935
  git(root, ["rev-parse", "HEAD"])
867
936
  ]);
937
+ const defaultBranch = await detectDefaultBranch(root);
938
+ const baseCommit = branch && commit && defaultBranch ? branch === defaultBranch.branch ? commit : await git(root, [
939
+ "merge-base",
940
+ "HEAD",
941
+ `refs/remotes/origin/${defaultBranch.branch}`
942
+ ]) : void 0;
943
+ const behindValue = commit && defaultBranch ? await git(root, [
944
+ "rev-list",
945
+ "--count",
946
+ `HEAD..refs/remotes/origin/${defaultBranch.branch}`
947
+ ]) : void 0;
948
+ const behindBy = behindValue !== void 0 && /^\d+$/.test(behindValue) ? Number(behindValue) : void 0;
868
949
  return {
869
950
  root,
870
951
  ...remote ? { remote: sanitizeRemote(remote) } : {},
871
952
  ...branch ? { branch } : {},
872
- ...commit ? { commit } : {}
953
+ ...commit ? { commit } : {},
954
+ ...defaultBranch ? { defaultBranch: defaultBranch.branch } : {},
955
+ ...baseCommit ? { baseCommit } : {},
956
+ ...behindBy !== void 0 ? { behindBy } : {}
873
957
  };
874
958
  }
875
959
  async function detectWorkingTreeMetrics(root) {
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-7UJHKOF6.js";
10
10
  import {
11
11
  runPresenceHeartbeat
12
- } from "./chunk-RI4FCH2F.js";
12
+ } from "./chunk-WVBSXZG3.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-7UJHKOF6.js";
5
+ import "./chunk-WVBSXZG3.js";
6
6
 
7
7
  // src/codex-hook.ts
8
8
  async function main() {
package/dist/index.d.ts CHANGED
@@ -59,6 +59,9 @@ declare const canonicalHookEventSchema: z.ZodObject<{
59
59
  remote: z.ZodOptional<z.ZodString>;
60
60
  branch: z.ZodOptional<z.ZodString>;
61
61
  commit: z.ZodOptional<z.ZodString>;
62
+ defaultBranch: z.ZodOptional<z.ZodString>;
63
+ baseCommit: z.ZodOptional<z.ZodString>;
64
+ behindBy: z.ZodOptional<z.ZodNumber>;
62
65
  }, z.core.$strip>>;
63
66
  outcome: z.ZodOptional<z.ZodEnum<{
64
67
  unknown: "unknown";
@@ -85,6 +88,9 @@ declare function mapClaudeCodeHook(eventName: string, input: unknown): {
85
88
  remote?: string | undefined;
86
89
  branch?: string | undefined;
87
90
  commit?: string | undefined;
91
+ defaultBranch?: string | undefined;
92
+ baseCommit?: string | undefined;
93
+ behindBy?: number | undefined;
88
94
  } | undefined;
89
95
  outcome?: "unknown" | "success" | "failure" | "cancelled" | undefined;
90
96
  durationMs?: number | undefined;
@@ -103,6 +109,9 @@ declare function mapCodexHook(eventName: string, input: unknown): {
103
109
  remote?: string | undefined;
104
110
  branch?: string | undefined;
105
111
  commit?: string | undefined;
112
+ defaultBranch?: string | undefined;
113
+ baseCommit?: string | undefined;
114
+ behindBy?: number | undefined;
106
115
  } | undefined;
107
116
  outcome?: "unknown" | "success" | "failure" | "cancelled" | undefined;
108
117
  durationMs?: number | undefined;
@@ -121,6 +130,9 @@ declare function mapCursorHook(eventName: string, input: unknown): {
121
130
  remote?: string | undefined;
122
131
  branch?: string | undefined;
123
132
  commit?: string | undefined;
133
+ defaultBranch?: string | undefined;
134
+ baseCommit?: string | undefined;
135
+ behindBy?: number | undefined;
124
136
  } | undefined;
125
137
  outcome?: "unknown" | "success" | "failure" | "cancelled" | undefined;
126
138
  durationMs?: number | undefined;
@@ -263,6 +275,9 @@ interface PresenceMetrics {
263
275
  active_seconds: number;
264
276
  lines_added: number;
265
277
  lines_deleted: number;
278
+ task_lines_added: number;
279
+ task_lines_deleted: number;
280
+ task_paths: string[];
266
281
  changed_files: number;
267
282
  model_seconds: Record<string, number>;
268
283
  tool_counts: Record<string, number>;
@@ -289,6 +304,9 @@ interface RepositoryInfo {
289
304
  remote?: string;
290
305
  branch?: string;
291
306
  commit?: string;
307
+ defaultBranch?: string;
308
+ baseCommit?: string;
309
+ behindBy?: number;
292
310
  }
293
311
  interface WorkingTreeMetrics {
294
312
  linesAdded: number;
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  startPresenceSession,
30
30
  stopPresenceSession,
31
31
  updatePresenceSession
32
- } from "./chunk-RI4FCH2F.js";
32
+ } from "./chunk-WVBSXZG3.js";
33
33
  export {
34
34
  JsonFileOfflineQueue,
35
35
  MemoryOfflineQueue,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wibeco/bridge",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Privacy-first live activity bridge for Cursor, Claude Code, and Codex.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -6,9 +6,8 @@ alwaysApply: true
6
6
  # Wibe activity
7
7
 
8
8
  - This repository is connected to Wibe project `${WIBE_PROJECT_ID}`.
9
- - For most tasks, publish only two updates: the settled plan and the completed outcome.
10
- - Share the plan with `wibe_share_plan` when available. Use `wibe_share_progress` at completion, or earlier only for a material blocker or a major direction change on work lasting over ten minutes.
11
- - Write one or two concrete sentences totaling 20–55 words. Explain the user-facing goal, what changed, and validation or blockers when material.
12
- - Use present progressive for active work and past tense for completed work. Never include prompts, source code, secrets, raw commands, routine tool calls, praise, hype, or speculation.
13
- - Do not repeat information already shared or publish separate updates for editing, testing, committing, or pushing.
14
- - If the Wibe MCP tools are unavailable, run `npx --yes --package @wibeco/bridge@latest wibe share-progress --summary "<summary>" --phase <planning|implementing|validating|blocked|shipped>`.
9
+ - For most tasks, publish one update only after the requested outcome is complete. Do not publish plans, routine progress, or separate updates for editing, testing, committing, or pushing.
10
+ - Use `wibe_share_progress` with phase `shipped` at completion. Publish earlier only with phase `blocked` when a material blocker leaves the task unfinished.
11
+ - Write one self-contained paragraph of 20–55 words in past tense. Lead with what now works, then explain the user or team impact and meaningful validation. Mention important limitations when present.
12
+ - Describe the outcome, not the agent’s process. Never include first-person narration, prompts, source code, secrets, raw commands, routine tool calls, filenames, line counts, praise, hype, or speculation; Wibe displays code and file changes separately.
13
+ - If the Wibe MCP tool is unavailable, run `npx --yes --package @wibeco/bridge@latest wibe share-progress --summary "<summary>" --phase <shipped|blocked>`.