@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.
- package/dist/{chunk-RI4FCH2F.js → chunk-O4MHMJZZ.js} +336 -15
- package/dist/{chunk-ZFVW6RVP.js → chunk-VBQSVC34.js} +60 -10
- package/dist/cli.js +2 -2
- package/dist/codex-hook.js +2 -2
- package/dist/index.d.ts +49 -5
- package/dist/index.js +7 -1
- package/package.json +1 -1
|
@@ -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: "
|
|
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": "
|
|
221
|
-
"subagent-stop": "
|
|
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
|
|
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:
|
|
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 [
|
|
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-
|
|
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
|
-
|
|
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 (
|
|
309
|
+
if (publishableEvents.length) await queue.enqueue(publishableEvents);
|
|
269
310
|
return {
|
|
270
311
|
exitCode: 0,
|
|
271
|
-
message:
|
|
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
|
|
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
|
-
})
|
|
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:
|
|
285
|
-
message:
|
|
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-
|
|
9
|
+
} from "./chunk-VBQSVC34.js";
|
|
10
10
|
import {
|
|
11
11
|
runPresenceHeartbeat
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-O4MHMJZZ.js";
|
|
13
13
|
|
|
14
14
|
// src/cli.ts
|
|
15
15
|
var HELP = `wibe-bridge <command>
|
package/dist/codex-hook.js
CHANGED
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-
|
|
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,
|