codiedev 0.9.6 → 0.9.7-beta.0

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/hook.js CHANGED
@@ -252,6 +252,10 @@ async function main() {
252
252
  ? { messageCount: 0, toolCallCount: 0 }
253
253
  : (0, utils_1.parseClaudeCodeStats)(transcriptContent);
254
254
  const transcriptBase64 = Buffer.from(transcriptContent, "utf8").toString("base64");
255
+ // Session→merge join keys: the branch + HEAD sha the work happened on. Matched
256
+ // server-side against a merged MR/PR's source_branch / commits for AI attribution.
257
+ const branch = (0, utils_1.getGitBranch)(workspaceRoot);
258
+ const headSha = (0, utils_1.getGitHeadSha)(workspaceRoot);
255
259
  const payload = (0, utils_1.buildCapturePayload)({
256
260
  sessionId: session_id,
257
261
  transcriptBase64,
@@ -262,6 +266,8 @@ async function main() {
262
266
  toolCallCount: stats.toolCallCount,
263
267
  capturedAt: Date.now(),
264
268
  source: mode,
269
+ branch,
270
+ headSha,
265
271
  });
266
272
  // Fire the upload from a fully detached child and exit immediately. Awaiting
267
273
  // it inline let Claude Code's SessionEnd shutdown window elapse and kill the
package/dist/utils.d.ts CHANGED
@@ -27,6 +27,8 @@ export declare function matchRepo(remoteUrl: string, repos: Array<{
27
27
  url: string;
28
28
  fullName: string;
29
29
  } | null;
30
+ export declare function getGitBranch(cwd: string): string | null;
31
+ export declare function getGitHeadSha(cwd: string): string | null;
30
32
  export interface CapturePayloadInput {
31
33
  sessionId?: string;
32
34
  transcriptBase64: string;
@@ -37,6 +39,8 @@ export interface CapturePayloadInput {
37
39
  toolCallCount: number;
38
40
  capturedAt: number;
39
41
  source: string;
42
+ branch?: string | null;
43
+ headSha?: string | null;
40
44
  }
41
45
  export declare function buildCapturePayload(input: CapturePayloadInput): Record<string, unknown>;
42
46
  export declare function hashToken(token: string): string;
package/dist/utils.js CHANGED
@@ -40,6 +40,8 @@ exports.readGitRemoteFromConfig = readGitRemoteFromConfig;
40
40
  exports.getGitRemoteUrl = getGitRemoteUrl;
41
41
  exports.normalizeRepoUrl = normalizeRepoUrl;
42
42
  exports.matchRepo = matchRepo;
43
+ exports.getGitBranch = getGitBranch;
44
+ exports.getGitHeadSha = getGitHeadSha;
43
45
  exports.buildCapturePayload = buildCapturePayload;
44
46
  exports.hashToken = hashToken;
45
47
  exports.claudeCodeInstalled = claudeCodeInstalled;
@@ -247,12 +249,101 @@ function matchRepo(remoteUrl, repos) {
247
249
  }
248
250
  return null;
249
251
  }
252
+ // Current branch via direct .git/HEAD read (no git binary required, same
253
+ // robustness as getGitRemoteUrl). Returns null on detached HEAD or when
254
+ // unresolvable. This is the session→merge join key: a captured session's
255
+ // branch is matched against a merged MR/PR's source_branch.
256
+ function getGitBranch(cwd) {
257
+ const gitDir = resolveGitDir(cwd);
258
+ if (gitDir) {
259
+ try {
260
+ const head = fs.readFileSync(path.join(gitDir, "HEAD"), "utf8").trim();
261
+ const m = head.match(/^ref:\s*refs\/heads\/(.+)$/);
262
+ if (m)
263
+ return m[1].trim();
264
+ // raw sha => detached HEAD, no branch
265
+ }
266
+ catch {
267
+ // fall through to git
268
+ }
269
+ }
270
+ try {
271
+ const b = (0, child_process_1.execSync)("git rev-parse --abbrev-ref HEAD", {
272
+ cwd,
273
+ stdio: ["pipe", "pipe", "pipe"],
274
+ })
275
+ .toString()
276
+ .trim();
277
+ return b && b !== "HEAD" ? b : null;
278
+ }
279
+ catch {
280
+ return null;
281
+ }
282
+ }
283
+ // Current commit SHA (HEAD), direct-read first then git fallback. Exact join
284
+ // key — survives branch renames that branch-matching would miss.
285
+ function getGitHeadSha(cwd) {
286
+ const gitDir = resolveGitDir(cwd);
287
+ if (gitDir) {
288
+ try {
289
+ const head = fs.readFileSync(path.join(gitDir, "HEAD"), "utf8").trim();
290
+ if (/^[0-9a-f]{40}$/i.test(head))
291
+ return head.toLowerCase();
292
+ const m = head.match(/^ref:\s*(.+)$/);
293
+ if (m) {
294
+ const ref = m[1].trim();
295
+ for (const cfg of gitConfigCandidates(gitDir)) {
296
+ const baseDir = path.dirname(cfg);
297
+ try {
298
+ const sha = fs.readFileSync(path.join(baseDir, ref), "utf8").trim();
299
+ if (/^[0-9a-f]{40}$/i.test(sha))
300
+ return sha.toLowerCase();
301
+ }
302
+ catch {
303
+ /* try packed-refs */
304
+ }
305
+ try {
306
+ const packed = fs.readFileSync(path.join(baseDir, "packed-refs"), "utf8");
307
+ const line = packed
308
+ .split("\n")
309
+ .find((l) => l.endsWith(" " + ref));
310
+ if (line) {
311
+ const sha = line.split(" ")[0].trim();
312
+ if (/^[0-9a-f]{40}$/i.test(sha))
313
+ return sha.toLowerCase();
314
+ }
315
+ }
316
+ catch {
317
+ /* ignore */
318
+ }
319
+ }
320
+ }
321
+ }
322
+ catch {
323
+ // fall through to git
324
+ }
325
+ }
326
+ try {
327
+ const sha = (0, child_process_1.execSync)("git rev-parse HEAD", {
328
+ cwd,
329
+ stdio: ["pipe", "pipe", "pipe"],
330
+ })
331
+ .toString()
332
+ .trim();
333
+ return /^[0-9a-f]{40}$/i.test(sha) ? sha.toLowerCase() : null;
334
+ }
335
+ catch {
336
+ return null;
337
+ }
338
+ }
250
339
  function buildCapturePayload(input) {
251
340
  return {
252
341
  sessionId: input.sessionId,
253
342
  repoUrl: input.remoteUrl ?? "",
254
343
  repoId: input.matchedRepoId,
255
344
  companyId: input.companyId,
345
+ branch: input.branch ?? "",
346
+ headSha: input.headSha ?? "",
256
347
  transcript: input.transcriptBase64,
257
348
  messageCount: input.messageCount,
258
349
  toolCallCount: input.toolCallCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codiedev",
3
- "version": "0.9.6",
3
+ "version": "0.9.7-beta.0",
4
4
  "description": "Connect Claude Code, Codex, Cursor, or VS Code Copilot to Codiedev for org-wide session capture and artifact collaboration",
5
5
  "bin": {
6
6
  "codiedev": "dist/cli.js",