altimate-receipts 0.5.0 → 0.5.2

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/cli.js CHANGED
@@ -13,13 +13,13 @@ import {
13
13
  renderShareMarkdown,
14
14
  sliceByBranch,
15
15
  toDsseEnvelope
16
- } from "./chunk-WISVSYA7.js";
16
+ } from "./chunk-GOLRNSZT.js";
17
17
  import {
18
18
  computeTrends,
19
19
  deriveTargets,
20
20
  renderTrends,
21
21
  upsertTrendsSection
22
- } from "./chunk-QGUQOQXO.js";
22
+ } from "./chunk-EKMFU3ES.js";
23
23
  import {
24
24
  agentIds,
25
25
  anyDetected,
@@ -43,10 +43,10 @@ import {
43
43
  selectSummary,
44
44
  upsertGuardrailsSection,
45
45
  verifyBundle
46
- } from "./chunk-2QTR3AF4.js";
46
+ } from "./chunk-EYM5WETZ.js";
47
47
 
48
48
  // src/cli.ts
49
- import { spawnSync as spawnSync3 } from "child_process";
49
+ import { spawnSync as spawnSync4 } from "child_process";
50
50
  import {
51
51
  existsSync as existsSync4,
52
52
  mkdirSync as mkdirSync3,
@@ -177,17 +177,13 @@ function wirePrepareScript(pkgPath) {
177
177
  import { spawnSync as spawnSync2 } from "child_process";
178
178
  import { existsSync as existsSync2 } from "fs";
179
179
  import { join as join2 } from "path";
180
- var REPUSH_EXIT = 42;
181
- var ATTACH_SUBJECT = (branch) => `chore(receipts): attach agent receipt for ${branch}`;
182
- function git2(args, cwd) {
183
- const r = spawnSync2("git", args, { encoding: "utf8", cwd });
184
- return r.status === 0 ? r.stdout.trim() : "";
185
- }
180
+
181
+ // src/trace/gitCommand.ts
186
182
  var WRAPPERS = /* @__PURE__ */ new Set(["command", "exec", "nohup", "time", "env"]);
187
183
  var GIT_VALUE_FLAGS = /* @__PURE__ */ new Set(["-C", "-c", "--git-dir", "--work-tree", "--exec-path"]);
188
- var TAG_REFSPEC = /^(refs\/tags\/|v\d+(\.\d+)*$)/;
189
- function isGitPush(command) {
184
+ function gitInvocations(command) {
190
185
  const blanked = command.replace(/\\["']/g, " ").replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
186
+ const out = [];
191
187
  for (const simple of blanked.split(/(?:&&|\|\||[;|\n])/)) {
192
188
  const tokens = simple.trim().split(/\s+/).filter(Boolean);
193
189
  let i = 0;
@@ -205,15 +201,31 @@ function isGitPush(command) {
205
201
  i++;
206
202
  }
207
203
  }
208
- if (tokens[i] !== "push") {
204
+ const sub = tokens[i];
205
+ if (sub) {
206
+ out.push({ sub, rest: tokens.slice(i + 1) });
207
+ }
208
+ }
209
+ return out;
210
+ }
211
+
212
+ // src/hook/prePush.ts
213
+ var REPUSH_EXIT = 42;
214
+ var ATTACH_SUBJECT = (branch) => `chore(receipts): attach agent receipt for ${branch}`;
215
+ function git2(args, cwd) {
216
+ const r = spawnSync2("git", args, { encoding: "utf8", cwd });
217
+ return r.status === 0 ? r.stdout.trim() : "";
218
+ }
219
+ var TAG_REFSPEC = /^(refs\/tags\/|v\d+(\.\d+)*$)/;
220
+ function isGitPush(command) {
221
+ for (const { sub, rest } of gitInvocations(command)) {
222
+ if (sub !== "push") {
209
223
  continue;
210
224
  }
211
- const rest = tokens.slice(i + 1);
212
225
  if (rest.includes("--dry-run") || rest.includes("-n") || rest.includes("--tags")) {
213
226
  continue;
214
227
  }
215
- const positionals = rest.filter((t) => !t.startsWith("-"));
216
- const refspecs = positionals.slice(1);
228
+ const refspecs = rest.filter((t) => !t.startsWith("-")).slice(1);
217
229
  if (refspecs.length > 0 && refspecs.every((r) => TAG_REFSPEC.test(r))) {
218
230
  continue;
219
231
  }
@@ -224,8 +236,19 @@ function isGitPush(command) {
224
236
  function gitStdinPushesBranch(stdin) {
225
237
  return stdin.split("\n").some((line) => line.trim().startsWith("refs/heads/"));
226
238
  }
239
+ var hasMarker = (root) => existsSync2(join2(root, ".github", "workflows", "receipts.yml")) || existsSync2(join2(root, ".receipts"));
227
240
  function repoOptedIn(repoRoot) {
228
- return existsSync2(join2(repoRoot, ".github", "workflows", "receipts.yml")) || existsSync2(join2(repoRoot, ".receipts"));
241
+ if (hasMarker(repoRoot)) {
242
+ return true;
243
+ }
244
+ const common = git2(["rev-parse", "--git-common-dir"], repoRoot);
245
+ if (common.endsWith("/.git")) {
246
+ const primary = common.slice(0, -"/.git".length);
247
+ if (primary && primary !== repoRoot) {
248
+ return hasMarker(primary);
249
+ }
250
+ }
251
+ return false;
229
252
  }
230
253
  function headIsAttachCommit(branch, cwd) {
231
254
  return git2(["log", "-1", "--format=%s"], cwd) === ATTACH_SUBJECT(branch);
@@ -245,7 +268,7 @@ async function runHookPrePush(dialect, stdin, generate) {
245
268
  if ((process.env.RECEIPTS_STORE || "commit").toLowerCase() === "none") {
246
269
  return { exit: 0 };
247
270
  }
248
- if (dialect === "claude") {
271
+ if (dialect === "claude" || dialect === "codex") {
249
272
  let payload;
250
273
  try {
251
274
  payload = JSON.parse(stdin);
@@ -301,7 +324,7 @@ async function runHookPrePush(dialect, stdin, generate) {
301
324
  if (dialect === "git") {
302
325
  return {
303
326
  exit: REPUSH_EXIT,
304
- message: `\u{1F4CE} Receipts: attached an agent receipt for '${branch}' \u2014 run \`git push\` again to include it.`
327
+ message: `\u{1F4CE} Receipts: attached an agent receipt for '${branch}' \u2014 rerun the same \`git push\` command to include it.`
305
328
  };
306
329
  }
307
330
  return {
@@ -317,7 +340,7 @@ async function runHookPrePush(dialect, stdin, generate) {
317
340
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
318
341
  import { dirname } from "path";
319
342
  var HOOK_COMMAND = "npx -y altimate-receipts@latest hook pre-push";
320
- function mergeHookIntoSettings(path) {
343
+ function mergeHookIntoSettings(path, command = HOOK_COMMAND) {
321
344
  let settings = {};
322
345
  if (existsSync3(path)) {
323
346
  let parsed;
@@ -345,11 +368,11 @@ function mergeHookIntoSettings(path) {
345
368
  if (!Array.isArray(pre)) {
346
369
  return { ok: false, reason: `${path} has a non-array hooks.PreToolUse \u2014 leaving it untouched` };
347
370
  }
348
- const present = pre.some((e) => e?.hooks?.some((h) => h?.command === HOOK_COMMAND));
371
+ const present = pre.some((e) => e?.hooks?.some((h) => h?.command === command));
349
372
  if (present) {
350
373
  return { ok: true, changed: false };
351
374
  }
352
- pre.push({ matcher: "Bash", hooks: [{ type: "command", command: HOOK_COMMAND }] });
375
+ pre.push({ matcher: "Bash", hooks: [{ type: "command", command }] });
353
376
  mkdirSync2(dirname(path), { recursive: true });
354
377
  writeFileSync2(path, `${JSON.stringify(settings, null, 2)}
355
378
  `);
@@ -1003,6 +1026,72 @@ function renderHandoffMarkdown(h) {
1003
1026
  `;
1004
1027
  }
1005
1028
 
1029
+ // src/trace/commitMatch.ts
1030
+ import { spawnSync as spawnSync3 } from "child_process";
1031
+ var SHA_CAP = 200;
1032
+ function branchShas(base, cwd) {
1033
+ if (!base) {
1034
+ return [];
1035
+ }
1036
+ const r = spawnSync3("git", ["log", `--max-count=${SHA_CAP}`, "--format=%H", `${base}..HEAD`], {
1037
+ encoding: "utf8",
1038
+ cwd
1039
+ });
1040
+ if (r.status !== 0) {
1041
+ return [];
1042
+ }
1043
+ return r.stdout.split("\n").filter((s) => /^[0-9a-f]{40}$/.test(s));
1044
+ }
1045
+ var HEX_RUN = /\b[0-9a-f]{7,40}\b/g;
1046
+ function commandOf(input) {
1047
+ if (typeof input === "string") {
1048
+ const t = input.trim();
1049
+ if (t.startsWith("{")) {
1050
+ try {
1051
+ return commandOf(JSON.parse(t));
1052
+ } catch {
1053
+ return input;
1054
+ }
1055
+ }
1056
+ return input;
1057
+ }
1058
+ if (input && typeof input === "object") {
1059
+ const o = input;
1060
+ for (const key of ["command", "cmd"]) {
1061
+ const v = o[key];
1062
+ if (typeof v === "string") {
1063
+ return v;
1064
+ }
1065
+ if (Array.isArray(v)) {
1066
+ return v.filter((t) => typeof t === "string").join(" ");
1067
+ }
1068
+ }
1069
+ }
1070
+ return "";
1071
+ }
1072
+ function isGitWrite(command) {
1073
+ return gitInvocations(command).some((g) => g.sub === "commit" || g.sub === "push");
1074
+ }
1075
+ function authoredBranch(spans, shas) {
1076
+ if (shas.length === 0) {
1077
+ return false;
1078
+ }
1079
+ for (const s of spans) {
1080
+ if (s.kind !== "tool" || typeof s.output !== "string") {
1081
+ continue;
1082
+ }
1083
+ if (!isGitWrite(commandOf(s.input))) {
1084
+ continue;
1085
+ }
1086
+ for (const token of s.output.match(HEX_RUN) ?? []) {
1087
+ if (shas.some((full) => full.startsWith(token))) {
1088
+ return true;
1089
+ }
1090
+ }
1091
+ }
1092
+ return false;
1093
+ }
1094
+
1006
1095
  // src/cli.ts
1007
1096
  var HELP = `
1008
1097
  \u{1F9FE} receipts \u2014 proof, not vibes
@@ -1032,9 +1121,10 @@ Usage
1032
1121
  receipts sarif [receipt] SARIF 2.1.0 for GitHub code-scanning (inline + Security tab; --out f)
1033
1122
  receipts prune [dir] Remove committed receipts for merged/deleted branches (--dry-run)
1034
1123
  receipts init One-command adopt: PR-check workflow + the repo-committed
1035
- agent hook (--prepare also wires the git-hook floor)
1124
+ agent hook; --pr also branches, commits, pushes + opens the PR
1125
+ (--prepare wires the git floor; --agents codex too)
1036
1126
  receipts hook pre-push (called by hooks, not humans) attach the receipt on push
1037
- (--agent claude | git selects the payload dialect)
1127
+ (--agent claude | codex | git selects the payload dialect)
1038
1128
  receipts install-hook Write the self-updating pre-push hook into .git/hooks
1039
1129
  receipts setup-local Add the hook to YOUR ~/.claude/settings.json (only fires
1040
1130
  in repos that have adopted the Receipts workflow)
@@ -1112,6 +1202,7 @@ function parseArgs(argv) {
1112
1202
  dryRun: false,
1113
1203
  wholeSession: false,
1114
1204
  prepare: false,
1205
+ pr: false,
1115
1206
  color: !process.env.NO_COLOR && process.stdout.isTTY === true
1116
1207
  };
1117
1208
  const positionals = [];
@@ -1156,12 +1247,16 @@ function parseArgs(argv) {
1156
1247
  parsed.wholeSession = true;
1157
1248
  } else if (a === "--prepare") {
1158
1249
  parsed.prepare = true;
1250
+ } else if (a === "--pr") {
1251
+ parsed.pr = true;
1252
+ } else if (a === "--agents") {
1253
+ parsed.agents = (args[++i] ?? "").split(",").filter(Boolean);
1159
1254
  } else if (a === "--agent") {
1160
1255
  const next = args[i + 1];
1161
1256
  if (next && agentIds().includes(next)) {
1162
1257
  parsed.agent = next;
1163
1258
  i++;
1164
- } else if (next === "claude" || next === "git") {
1259
+ } else if (next === "claude" || next === "codex" || next === "git") {
1165
1260
  parsed.hookDialect = next;
1166
1261
  i++;
1167
1262
  }
@@ -1222,7 +1317,7 @@ async function run(argv) {
1222
1317
  return runPrune(args.file, { dryRun: args.dryRun });
1223
1318
  }
1224
1319
  if (args.command === "init") {
1225
- return runInit({ prepare: args.prepare });
1320
+ return runInit({ prepare: args.prepare, agents: args.agents, pr: args.pr });
1226
1321
  }
1227
1322
  if (args.command === "hook") {
1228
1323
  return runHook(args.file, args.hookDialect ?? "claude");
@@ -1342,7 +1437,7 @@ Run a coding-agent session first, then try again.
1342
1437
  return 0;
1343
1438
  }
1344
1439
  function git3(args) {
1345
- const r = spawnSync3("git", args, { encoding: "utf8" });
1440
+ const r = spawnSync4("git", args, { encoding: "utf8" });
1346
1441
  return r.status === 0 ? r.stdout.trim() : "";
1347
1442
  }
1348
1443
  var PR_SELECT_SCAN = 150;
@@ -1360,7 +1455,7 @@ function branchBirthMs(base) {
1360
1455
  function staleForBranch(endedAt, birthMs) {
1361
1456
  return birthMs != null && endedAt != null && endedAt < birthMs;
1362
1457
  }
1363
- async function pickForDiff(all, branch, repoRoot, files, birthMs = null) {
1458
+ async function pickForDiff(all, branch, repoRoot, files, birthMs = null, shas = []) {
1364
1459
  const load = async (sum) => {
1365
1460
  const session = await loadSession(sum);
1366
1461
  if (!session) {
@@ -1368,6 +1463,14 @@ async function pickForDiff(all, branch, repoRoot, files, birthMs = null) {
1368
1463
  }
1369
1464
  return { summary: sum, session, derived: deriveSpans(session) };
1370
1465
  };
1466
+ if (shas.length > 0) {
1467
+ for (const sum of all.slice(0, PR_SELECT_SCAN)) {
1468
+ const cand = await load(sum);
1469
+ if (cand && authoredBranch(cand.derived.spans, shas)) {
1470
+ return cand;
1471
+ }
1472
+ }
1473
+ }
1371
1474
  const primarySum = selectForBranch(all, branch, repoRoot);
1372
1475
  const primary = primarySum ? await load(primarySum) : null;
1373
1476
  if (primary && diffOverlap(primary.derived, files) > 0) {
@@ -1398,7 +1501,14 @@ async function runPr(opts) {
1398
1501
  }
1399
1502
  const diff = opts.wholeSession ? null : changedFiles(opts.base);
1400
1503
  const all = await listSessions();
1401
- const picked = diff ? await pickForDiff(all, branch, repoRoot || void 0, diff.files, branchBirthMs(diff.base)) : await (async () => {
1504
+ const picked = diff ? await pickForDiff(
1505
+ all,
1506
+ branch,
1507
+ repoRoot || void 0,
1508
+ diff.files,
1509
+ branchBirthMs(diff.base),
1510
+ branchShas(diff.base)
1511
+ ) : await (async () => {
1402
1512
  const sum = selectForBranch(all, branch, repoRoot || void 0);
1403
1513
  return sum ? { summary: sum, session: await loadSession(sum), derived: null } : null;
1404
1514
  })();
@@ -1417,7 +1527,7 @@ Build the branch with a coding agent first, or run \`receipts --list\`.
1417
1527
  let scope = { kind: "session" };
1418
1528
  let scopeNote = "whole session";
1419
1529
  if (diff) {
1420
- const sd = applyDiffScope(derived, findings, diff.files);
1530
+ const sd = applyDiffScope(derived, findings, diff.files, session.projectPath);
1421
1531
  derived = sd.derived;
1422
1532
  findings = sd.findings;
1423
1533
  scope = { kind: "diff", base: diff.base, files: diff.files };
@@ -1465,6 +1575,11 @@ Build the branch with a coding agent first, or run \`receipts --list\`.
1465
1575
  const dir = join6(repoRoot || ".", ".receipts");
1466
1576
  const out = opts.out ?? join6(dir, `${safe}.json`);
1467
1577
  mkdirSync3(dir, { recursive: true });
1578
+ const attrs = join6(dir, ".gitattributes");
1579
+ if (!existsSync4(attrs)) {
1580
+ writeFileSync3(attrs, "* linguist-generated\n");
1581
+ git3(["add", attrs]);
1582
+ }
1468
1583
  writeFileSync3(out, json);
1469
1584
  git3(["add", out]);
1470
1585
  const rel = repoRoot ? relative(repoRoot, out) : out;
@@ -1844,6 +1959,7 @@ async function runEval(opts = {}) {
1844
1959
  }
1845
1960
  function runInit(opts = {}) {
1846
1961
  const lines = [];
1962
+ const written = [];
1847
1963
  const v = getVersion();
1848
1964
  const major = /^(\d+)\.\d+\.\d+/.exec(v)?.[1];
1849
1965
  const tag = major ? `v${major}` : "v0";
@@ -1854,6 +1970,7 @@ function runInit(opts = {}) {
1854
1970
  } else {
1855
1971
  mkdirSync3(dir, { recursive: true });
1856
1972
  writeFileSync3(path, workflowContent(tag, v));
1973
+ written.push(path);
1857
1974
  lines.push(`receipts init: wrote ${path} (tracking ${tag}, quiet + non-blocking).`);
1858
1975
  }
1859
1976
  const settingsPath = join6(".claude", "settings.json");
@@ -1861,6 +1978,7 @@ function runInit(opts = {}) {
1861
1978
  if (!merged.ok) {
1862
1979
  lines.push(`receipts init: ${merged.reason}.`);
1863
1980
  } else if (merged.changed) {
1981
+ written.push(settingsPath);
1864
1982
  lines.push(`receipts init: added the receipts pre-push hook to ${settingsPath}.`);
1865
1983
  } else {
1866
1984
  lines.push(`receipts init: ${settingsPath} already has the receipts hook.`);
@@ -1868,13 +1986,37 @@ function runInit(opts = {}) {
1868
1986
  const ignorePath = join6(".claude", ".gitignore");
1869
1987
  if (!existsSync4(ignorePath)) {
1870
1988
  writeFileSync3(ignorePath, "*\n!settings.json\n!.gitignore\n");
1989
+ written.push(ignorePath);
1871
1990
  lines.push(`receipts init: wrote ${ignorePath} (commit settings.json, ignore the rest).`);
1872
1991
  }
1992
+ if (opts.agents?.includes("codex") || existsSync4(".codex")) {
1993
+ const codexPath = join6(".codex", "hooks.json");
1994
+ const m = mergeHookIntoSettings(
1995
+ codexPath,
1996
+ "npx -y altimate-receipts@latest hook pre-push --agent codex"
1997
+ );
1998
+ if (!m.ok) {
1999
+ lines.push(`receipts init: ${m.reason}.`);
2000
+ } else if (m.changed) {
2001
+ written.push(codexPath);
2002
+ lines.push(`receipts init: added the receipts pre-push hook to ${codexPath}.`);
2003
+ } else {
2004
+ lines.push(`receipts init: ${codexPath} already has the receipts hook.`);
2005
+ }
2006
+ }
2007
+ const attrsPath = join6(".receipts", ".gitattributes");
2008
+ if (!existsSync4(attrsPath)) {
2009
+ mkdirSync3(".receipts", { recursive: true });
2010
+ writeFileSync3(attrsPath, "* linguist-generated\n");
2011
+ written.push(attrsPath);
2012
+ lines.push(`receipts init: wrote ${attrsPath} (receipts collapse in PR diffs).`);
2013
+ }
1873
2014
  if (opts.prepare) {
1874
2015
  const wired = wirePrepareScript("package.json");
1875
2016
  if (!wired.ok) {
1876
2017
  lines.push(`receipts init: ${wired.reason}.`);
1877
2018
  } else if (wired.changed) {
2019
+ written.push("package.json");
1878
2020
  lines.push(
1879
2021
  "receipts init: wired `prepare` in package.json \u2014 `npm install` now self-installs the git hook."
1880
2022
  );
@@ -1882,11 +2024,82 @@ function runInit(opts = {}) {
1882
2024
  lines.push("receipts init: package.json `prepare` already wired.");
1883
2025
  }
1884
2026
  }
1885
- lines.push(" Commit these, open a PR, and receipts attach themselves from then on.");
2027
+ if (opts.pr) {
2028
+ openAdoptionPr(written, lines);
2029
+ } else {
2030
+ lines.push(" Commit these, open a PR, and receipts attach themselves from then on.");
2031
+ lines.push(" (Or let init do that too: `npx altimate-receipts init --pr`.)");
2032
+ }
2033
+ if (written.includes(settingsPath)) {
2034
+ lines.push(" Note: agent sessions load hooks at startup \u2014 restart any running session once.");
2035
+ }
1886
2036
  process.stdout.write(`${lines.join("\n")}
1887
2037
  `);
1888
2038
  return 0;
1889
2039
  }
2040
+ var ADOPT_BRANCH = "chore/adopt-receipts";
2041
+ function openAdoptionPr(written, lines) {
2042
+ if (written.length === 0) {
2043
+ lines.push("receipts init: nothing new to commit \u2014 the repo is already integrated.");
2044
+ return;
2045
+ }
2046
+ if (!git3(["rev-parse", "--git-dir"])) {
2047
+ lines.push("receipts init: not a git repository \u2014 commit the files above manually.");
2048
+ return;
2049
+ }
2050
+ if (git3(["rev-parse", "--verify", "--quiet", ADOPT_BRANCH]) !== "") {
2051
+ lines.push(
2052
+ `receipts init: branch ${ADOPT_BRANCH} already exists \u2014 commit the files above to it manually.`
2053
+ );
2054
+ return;
2055
+ }
2056
+ if (spawnSync4("git", ["checkout", "-b", ADOPT_BRANCH], { encoding: "utf8" }).status !== 0) {
2057
+ lines.push(`receipts init: could not create branch ${ADOPT_BRANCH} \u2014 commit manually.`);
2058
+ return;
2059
+ }
2060
+ spawnSync4("git", ["add", "--", ...written], { encoding: "utf8" });
2061
+ const commit = spawnSync4(
2062
+ "git",
2063
+ ["commit", "-m", "chore: adopt receipts \u2014 zero-install agent-work verification"],
2064
+ { encoding: "utf8" }
2065
+ );
2066
+ if (commit.status !== 0) {
2067
+ lines.push(
2068
+ `receipts init: commit failed (pre-commit hooks?) \u2014 files are staged on ${ADOPT_BRANCH}; finish manually.`
2069
+ );
2070
+ return;
2071
+ }
2072
+ lines.push(`receipts init: committed ${written.length} file(s) on ${ADOPT_BRANCH}.`);
2073
+ const push = spawnSync4("git", ["push", "-u", "origin", ADOPT_BRANCH], { encoding: "utf8" });
2074
+ if (push.status !== 0) {
2075
+ lines.push(
2076
+ "receipts init: push failed (no remote / auth?) \u2014 push the branch and open a PR manually."
2077
+ );
2078
+ return;
2079
+ }
2080
+ lines.push(`receipts init: pushed ${ADOPT_BRANCH}.`);
2081
+ const gh = spawnSync4(
2082
+ "gh",
2083
+ [
2084
+ "pr",
2085
+ "create",
2086
+ "--title",
2087
+ "chore: adopt receipts \u2014 zero-install agent-work verification",
2088
+ "--body",
2089
+ "Adds the Verified-by-Receipts PR check and the repo-committed agent hook (generated by `npx altimate-receipts init --pr`). Contributors install nothing \u2014 see https://github.com/AltimateAI/altimate-receipts/blob/main/docs/onboarding.md"
2090
+ ],
2091
+ { encoding: "utf8" }
2092
+ );
2093
+ if (gh.status === 0) {
2094
+ lines.push(`receipts init: opened the PR \u2014 ${gh.stdout.trim().split("\n").pop()}`);
2095
+ return;
2096
+ }
2097
+ const remote = git3(["remote", "get-url", "origin"]);
2098
+ const slug = remote ? /github\.com[:/]([^/]+\/[^/.]+)/.exec(remote)?.[1] : void 0;
2099
+ lines.push(
2100
+ slug ? `receipts init: open the PR here \u2014 https://github.com/${slug}/pull/new/${ADOPT_BRANCH}` : "receipts init: branch pushed \u2014 open a PR from it in your forge."
2101
+ );
2102
+ }
1890
2103
  function workflowContent(tag, v) {
1891
2104
  return `name: Verified by Receipts
1892
2105