@afokapu/atdd-bun 0.10.109 → 0.10.110

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/integrity.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.10.109",
2
+ "version": "0.10.110",
3
3
  "files": {
4
4
  "HOOK_AUDIT.md": "5329d840db37671b1918f688ead26865473b87db73dbc75f7c8b2a8bbe8d6d43",
5
5
  "README.md": "8aefceaa24bee498f39081b2aaf4c0d5c54bc1ed9849c20e1d98d1427df56eab",
@@ -1269,7 +1269,7 @@
1269
1269
  "src/cli.ts": "395638ab87565bb10c2beb35988ff1acf686924d13515795156a2e8893b3da88",
1270
1270
  "src/docs-capability.ts": "bdeeb414649049b1cd4fa8008cada3a576318be51c43bec91bfa46a8e250c270",
1271
1271
  "src/enforce.ts": "c4811abe63efe4568f3cf26e21a152d1537e120cb3ce471daca32367cf6e2cda",
1272
- "src/hooks.ts": "7e3dd5518701d2f3399bee919a68e801588822b173a648ed738c05c4a57231f1",
1272
+ "src/hooks.ts": "fce67a102fe61c15b4100daddf842ce7264d9e0d0e79ec89c4ed4a0b188967b7",
1273
1273
  "src/index.ts": "4081f8b08d1d3ee9e15a9964143e53d9cfb94a5616ab3e58a180d24cc16dc381",
1274
1274
  "src/integrity.ts": "f1ede7cb04924ae88e11917047277e264af6596b9759e381bd9c497cd34a797d",
1275
1275
  "src/journey-docs.ts": "994d229376244c84212cff951032823068a76565dd2fd427b9d44e341bd14853",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afokapu/atdd-bun",
3
- "version": "0.10.109",
3
+ "version": "0.10.110",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/afokapu/atdd-bun.git"
package/src/hooks.ts CHANGED
@@ -345,7 +345,7 @@ type PrimaryPolicy = { policy: HookPolicy; source: { path: string; commit: strin
345
345
  async function primaryPolicy(root: string): Promise<PrimaryPolicy> { const found = await primaryRoot(await realpath(resolve(root))); if (!found) return { policy: defaultHookPolicy, source: null }; const primary = await realpath(found), revision = await git(primary, ["rev-parse", "--verify", "HEAD^{commit}"]); if (revision.code || !revision.out) return { policy: defaultHookPolicy, source: null }; const source = { path: primary, commit: revision.out }, committed = await git(primary, ["show", `${source.commit}:atdd-bun.yaml`]); return { policy: committed.code ? defaultHookPolicy : parsePolicy(committed.out), source }; }
346
346
  const disabledWorktreePolicy = (source: PrimaryPolicy["source"]) => bad(source ? `worktree policy is disabled in primary checkout ${source.path} at ${source.commit}; update the primary checkout and try again` : "worktree policy is disabled");
347
347
  export type WorktreeLayout = { primary: string; worktreeRoot: string; current: string; branch: string };
348
- async function layout(root: string, cfg: WorktreePolicy): Promise<WorktreeLayout | { error: string }> { const current = await realpath(resolve(root)), commonRoot = await primaryRoot(current); if (!commonRoot) return { error: "not inside a Git worktree" }; const primary = await realpath(commonRoot), expectedPrimary = cfg.primary_directory === "." ? primary : join(dirname(primary), cfg.primary_directory); if (primary !== expectedPrimary) return { error: `primary checkout must be ${expectedPrimary}, not ${primary}` }; const branch = (await git(primary, ["symbolic-ref", "--quiet", "--short", "HEAD"])).out; if (branch !== cfg.primary_branch) return { error: `primary checkout ${primary} must be on ${cfg.primary_branch}, not ${branch || "a detached HEAD"}` }; return { primary, worktreeRoot: resolve(primary, cfg.root.replaceAll("{repo}", basename(primary))), current, branch: (await git(current, ["symbolic-ref", "--quiet", "--short", "HEAD"])).out }; }
348
+ async function layout(root: string, cfg: WorktreePolicy): Promise<WorktreeLayout | { error: string }> { const current = await realpath(resolve(root)), commonRoot = await primaryRoot(current); if (!commonRoot) return { error: "not inside a Git worktree" }; const primary = await realpath(commonRoot), expectedPrimary = cfg.primary_directory === "." ? primary : join(dirname(primary), cfg.primary_directory); if (primary !== expectedPrimary) return { error: `primary checkout must be ${expectedPrimary}, not ${primary}` }; const branch = (await git(primary, ["symbolic-ref", "--quiet", "--short", "HEAD"])).out; if (branch !== cfg.primary_branch && !(await jjPrimaryOnBranch(primary, cfg.primary_branch))) return { error: `primary checkout ${primary} must be on ${cfg.primary_branch}, not ${branch || "a detached HEAD"}` }; return { primary, worktreeRoot: resolve(primary, cfg.root.replaceAll("{repo}", basename(primary))), current, branch: (await git(current, ["symbolic-ref", "--quiet", "--short", "HEAD"])).out }; }
349
349
  async function worktreeCommitPolicy(root: string, cfg: WorktreePolicy): Promise<string | null> { if (!cfg.enabled || !cfg.require_linked_worktree) return null; const state = await layout(root, cfg); if ("error" in state) return state.error; if (state.current === state.primary) return `commits must use a linked worktree beneath ${state.worktreeRoot}; run: atdd-bun worktree start <branch>`; if (!inside(state.worktreeRoot, state.current)) return `linked worktree ${state.current} is outside required root ${state.worktreeRoot}`; if (!state.branch) return "commits require a named branch, not a detached HEAD"; return null; }
350
350
 
351
351
  export async function runHook(event: HookEvent, root = process.cwd(), args: string[] = [], stdin = "") {
@@ -421,6 +421,17 @@ async function branchForCommit(root: string, commit: string, protectedBranches:
421
421
  : { error: `current Jujutsu revision must have exactly one non-protected exported bookmark; found ${candidates.length}` };
422
422
  }
423
423
 
424
+ // A JJ-colocated primary keeps Git HEAD detached at its working-copy parent, so JJ state decides:
425
+ // the parent must be exactly one commit and it must be the primary branch or its origin bookmark.
426
+ async function jjPrimaryOnBranch(primary: string, branch: string): Promise<boolean> {
427
+ if (!existsSync(join(primary, ".jj"))) return false;
428
+ const name = JSON.stringify(branch), lines = async (revset: string) => {
429
+ const result = await jj(primary, ["log", "--ignore-working-copy", "--no-graph", "-r", revset, "-T", 'commit_id ++ "\\n"']).catch(() => null);
430
+ return result && !result.code ? result.out.split("\n").filter(Boolean) : [];
431
+ };
432
+ return (await lines("@-")).length === 1 && (await lines(`@- & (bookmarks(exact:${name}) | remote_bookmarks(exact:${name}, exact:"origin"))`)).length === 1;
433
+ }
434
+
424
435
  type Layout = { primary: string; worktreeRoot: string; current: string };
425
436
  async function jjLayout(root: string, cfg: HookPolicy): Promise<Layout | { error: string }> {
426
437
  const current = await realpath(resolve(root));