@bli-cockpit/cli 0.1.31 → 0.1.32

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.
@@ -1,5 +1,6 @@
1
1
  import crypto from "node:crypto";
2
2
  import path from "node:path";
3
+ import { normalizeGitOrigin, repoFingerprintFromOrigin, repoLabelFromOrigin, stableWorktreeFingerprint, } from "../repo-identity.js";
3
4
  /**
4
5
  * Shared deterministic attribution core for agent session transcripts (Codex
5
6
  * and Claude Code). Both adapters extract source-shaped signals, normalize them
@@ -73,6 +74,12 @@ export function scoreSignalsAgainstWorktrees(signals, worktrees, options = {}) {
73
74
  const secondBestScore = scored[1]?.score ?? 0;
74
75
  if (!best || best.score === 0) {
75
76
  if (repoPathAbsentFromDisk([...signals.cwds, ...signals.workspaceRoots], options.collectionRoots ?? [])) {
77
+ const transcriptFallback = fallbackToTranscriptOriginForDeletedRepo({
78
+ originLabel,
79
+ signals,
80
+ });
81
+ if (transcriptFallback)
82
+ return transcriptFallback;
76
83
  return skipped("repo_not_on_disk");
77
84
  }
78
85
  const reason = signals.cwds.length > 0 || signals.workspaceRoots.length > 0
@@ -173,6 +180,49 @@ function fallbackToKnownRepoPrimaryWorkContext(options) {
173
180
  worktree: primary,
174
181
  };
175
182
  }
183
+ /**
184
+ * Reconstructs a repo/worktree identity from transcript origin metadata only
185
+ * after the caller has already proven a recorded cwd/workspace root sits under
186
+ * an operator-approved collection root. The origin is never allowed to widen
187
+ * consent by itself: it only rescues sessions for repos that were once inside
188
+ * the approved scan boundary but have since been deleted from disk, where the
189
+ * transcript is still durable enough to carry the normalized remote identity.
190
+ */
191
+ function fallbackToTranscriptOriginForDeletedRepo(options) {
192
+ const origins = [
193
+ ...new Set(options.signals.originUrls
194
+ .map((origin) => normalizeGitOrigin(origin))
195
+ .filter(Boolean)),
196
+ ];
197
+ if (origins.length === 0)
198
+ return null;
199
+ if (origins.length > 1)
200
+ return skipped("multiple_transcript_origins");
201
+ const recordedPath = options.signals.workspaceRoots[0] ?? options.signals.cwds[0] ?? null;
202
+ if (!recordedPath)
203
+ return null;
204
+ const resolvedPath = path.resolve(recordedPath);
205
+ const origin = origins[0] ?? "";
206
+ return {
207
+ state: "attributed_fallback",
208
+ reason: "transcript_origin_fallback",
209
+ signals: ["transcript_origin_fallback", options.originLabel],
210
+ attribution_score: clampScore(SCORE_ORIGIN_MATCH),
211
+ path_score: 0,
212
+ worktree: {
213
+ requested_path: resolvedPath,
214
+ repo_root: resolvedPath,
215
+ repo_label: repoLabelFromOrigin(origin),
216
+ repo_fingerprint: repoFingerprintFromOrigin(origin),
217
+ repo_origin_url: origin,
218
+ branch: options.signals.branches[0] ?? "unknown",
219
+ head_sha: options.signals.headShas[0] ?? null,
220
+ worktree_label: path.basename(resolvedPath),
221
+ worktree_fingerprint: stableWorktreeFingerprint(resolvedPath),
222
+ worktree_is_primary: false,
223
+ },
224
+ };
225
+ }
176
226
  /**
177
227
  * For each path signal, the single worktree whose root most specifically
178
228
  * contains it (the longest containing root). Returns the set of worktree
@@ -32,10 +32,9 @@ export async function resolveRepoWorktreeIdentity(repoRoot) {
32
32
  const repoLabel = repoOriginUrl
33
33
  ? repoLabelFromOrigin(repoOriginUrl)
34
34
  : path.basename(resolvedRoot);
35
- const repoMaterial = repoOriginUrl
36
- ? `origin:${repoOriginUrl}`
37
- : `local:${sha256(commonGitDir ?? resolvedRoot)}`;
38
- const repoFingerprint = `repo-${sha256(repoMaterial).slice(0, 24)}`;
35
+ const repoFingerprint = repoOriginUrl
36
+ ? repoFingerprintFromOrigin(repoOriginUrl)
37
+ : `repo-${sha256(`local:${sha256(commonGitDir ?? resolvedRoot)}`).slice(0, 24)}`;
39
38
  const gitFilePath = path.join(resolvedRoot, ".git");
40
39
  const worktreeIsPrimary = await fs.stat(gitFilePath).then((stat) => stat.isDirectory(), () => false);
41
40
  return {
@@ -162,6 +161,10 @@ export async function stableWorktreeRoot(repoRoot) {
162
161
  export function stableWorktreeFingerprint(repoRoot) {
163
162
  return `wt-${sha256(`worktree:${normalizeFingerprintPath(repoRoot)}`).slice(0, 24)}`;
164
163
  }
164
+ export function repoFingerprintFromOrigin(origin) {
165
+ const normalizedOrigin = normalizeGitOrigin(origin);
166
+ return `repo-${sha256(`origin:${normalizedOrigin}`).slice(0, 24)}`;
167
+ }
165
168
  async function resolveBranchFromHead(repoRoot) {
166
169
  try {
167
170
  const gitPath = path.join(repoRoot, ".git");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/cli",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {