@attalabs/vinaya 0.23.0 → 0.24.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.
Files changed (117) hide show
  1. package/README.md +1 -0
  2. package/dist/checks/bin/check-body-bare-digits.js +7 -1
  3. package/dist/checks/bin/check-branch-topology.js +27 -1
  4. package/dist/checks/bin/check-brief-shape.js +7 -1
  5. package/dist/checks/bin/check-changeset-coverage.js +7 -1
  6. package/dist/checks/bin/check-closes-n.js +27 -1
  7. package/dist/checks/bin/check-coherence.js +27 -1
  8. package/dist/checks/bin/check-dead-branch-push.js +7 -1
  9. package/dist/checks/bin/check-dispatch-readiness.js +27 -1
  10. package/dist/checks/bin/check-doc-coverage-push.js +7 -1
  11. package/dist/checks/bin/check-doc-coverage.js +7 -1
  12. package/dist/checks/bin/check-doctrine-no-procedures.js +7 -1
  13. package/dist/checks/bin/check-doctrine-portability.js +7 -1
  14. package/dist/checks/bin/check-evidence-fresh.js +7 -1
  15. package/dist/checks/bin/check-exec-bits.js +7 -1
  16. package/dist/checks/bin/check-first-push-dispatch.js +27 -1
  17. package/dist/checks/bin/check-issue-assignment.js +27 -1
  18. package/dist/checks/bin/check-main-branch-refusal.js +13 -2
  19. package/dist/checks/bin/check-no-disk-state.js +7 -1
  20. package/dist/checks/bin/check-pr-report-density.js +7 -1
  21. package/dist/checks/bin/check-quoted-command.js +38 -12
  22. package/dist/checks/bin/check-reader-resolvable-prose.js +38 -12
  23. package/dist/checks/bin/check-registry-gates.js +7 -1
  24. package/dist/checks/bin/check-retired-vocabulary.js +38 -12
  25. package/dist/checks/bin/check-review-gate.js +7 -1
  26. package/dist/checks/bin/check-single-plan-pr.js +7 -1
  27. package/dist/checks/bin/check-test-plan.js +7 -1
  28. package/dist/checks/bin/check-token-collection-wired.js +7 -1
  29. package/dist/checks/bin/check-token-report.js +7 -1
  30. package/dist/checks/bin/check-workspace-escape.js +7 -1
  31. package/dist/index.js +362 -87
  32. package/package.json +1 -1
  33. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/anchored-region.ts +9 -4
  34. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/blast-radius-domains.ts +7 -1
  35. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-render.ts +462 -0
  36. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-validation.ts +405 -4
  37. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/coherence-checks.ts +19 -0
  38. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/consumer-enumeration.ts +75 -0
  39. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dispatch-gate.ts +44 -0
  40. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-no-procedures.ts +94 -0
  41. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-portability.ts +128 -5
  42. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/gate-audience.ts +3 -0
  43. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/index.ts +25 -3
  44. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-token-report.ts +17 -5
  45. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/pr-report-density.ts +116 -0
  46. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/premise-check.ts +35 -0
  47. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/quoted-command.ts +3 -3
  48. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-gate.ts +116 -7
  49. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-status.ts +200 -0
  50. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/test-plan-gate.ts +25 -12
  51. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/verdict-extraction.ts +70 -11
  52. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  53. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  54. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  55. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  56. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  57. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  58. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  59. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  60. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  61. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  63. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  66. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  72. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  81. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  82. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  83. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  84. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  85. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  86. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  87. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  88. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  89. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  90. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__02a-3g8._.js +1 -1
  91. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/1q96_modules_@clerk_nextjs_dist_esm_app-router_client_keyless-creator-reader_0lom2js.js +1 -1
  92. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
  93. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
  94. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
  95. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
  96. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
  97. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1mcitst._.js → [root-of-the-server]__1pndh3_._.js} +1 -1
  98. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
  99. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
  100. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
  101. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
  102. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +2 -2
  103. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
  104. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/apps_vinaya-studio_web_src_app_studio_projects_[name]_tranches_[slug]_05ufo8i._.js +3 -3
  105. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  106. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  107. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  108. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  109. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  110. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0jer_drkl1e4r.js → 0zebsmmk1bvnb.js} +4 -4
  111. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{20x-t0a0v129z.js → 1uogpj5w2n5ju.js} +1 -1
  112. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2ou_81ahqw31e.js → 3m1kgax7j2vgs.js} +1 -1
  113. package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
  114. /package/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/{aeg-core → vinaya/studio-standalone/_node_modules/@attalabs/aeg-core}/bin/verify-coherence.ts +0 -0
  115. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_buildManifest.js +0 -0
  116. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_clientMiddlewareManifest.js +0 -0
  117. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { dirname as dirname9, join as join29 } from "node:path";
6
6
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7
7
 
8
8
  // src/commands/archive.ts
9
- import { execFileSync as execFileSync5 } from "node:child_process";
9
+ import { execFileSync as execFileSync6 } from "node:child_process";
10
10
  // ../../packages/aeg-forge-state/src/strip-code.ts
11
11
  function maskCode(body) {
12
12
  const fill = (line) => " ".repeat(line.length);
@@ -3444,6 +3444,7 @@ import { readFileSync as readFileSync4 } from "node:fs";
3444
3444
  import { join as join4 } from "node:path";
3445
3445
 
3446
3446
  // src/commands/doctrine.ts
3447
+ import { execFileSync as execFileSync2 } from "node:child_process";
3447
3448
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
3448
3449
  import { dirname as dirname2, join as join2, sep } from "node:path";
3449
3450
  import matter2 from "gray-matter";
@@ -3477,10 +3478,29 @@ function packageRoot(moduleUrl) {
3477
3478
 
3478
3479
  // src/commands/doctrine.ts
3479
3480
  var ENTRY_SEGMENTS = ["skills", "aeg", "SKILL.md"];
3480
- function resolveDoctrineRoot(pkg = packageRoot(import.meta.url)) {
3481
+ function resolveDoctrineRoot(pkg = packageRoot(import.meta.url), cwd = process.cwd()) {
3482
+ return resolveDoctrineRootInfo(pkg, cwd)?.root ?? null;
3483
+ }
3484
+ function gitToplevel(cwd) {
3485
+ try {
3486
+ return execFileSync2("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
3487
+ encoding: "utf8",
3488
+ stdio: ["ignore", "pipe", "pipe"]
3489
+ }).trim();
3490
+ } catch {
3491
+ return null;
3492
+ }
3493
+ }
3494
+ function resolveDoctrineRootInfo(pkg = packageRoot(import.meta.url), cwd = process.cwd()) {
3495
+ const toplevel = gitToplevel(cwd);
3496
+ if (toplevel !== null) {
3497
+ const treeRoot = join2(toplevel, "aeg-root");
3498
+ if (existsSync2(join2(treeRoot, "roles")))
3499
+ return { root: treeRoot, source: "tree" };
3500
+ }
3481
3501
  const fromSource = !pkg.split(sep).includes("node_modules");
3482
- const root = fromSource ? join2(dirname2(dirname2(pkg)), "aeg-root") : join2(pkg, "aeg-root");
3483
- return hasDoctrineEntry(root) ? root : null;
3502
+ const bundleRoot = fromSource ? join2(dirname2(dirname2(pkg)), "aeg-root") : join2(pkg, "aeg-root");
3503
+ return hasDoctrineEntry(bundleRoot) ? { root: bundleRoot, source: "bundle" } : null;
3484
3504
  }
3485
3505
  var ROLE_ALIASES = { "code-reviewer": "reviewer" };
3486
3506
  function hasDoctrineEntry(root) {
@@ -3550,21 +3570,24 @@ function discoverRoleNames(doctrineRoot) {
3550
3570
  function agentSkillPath(roleName) {
3551
3571
  return `.agents/skills/vinaya-${roleName}/SKILL.md`;
3552
3572
  }
3553
- function renderAgentSkill(roleName) {
3573
+ function doctrineInvocation(selfHost) {
3574
+ return selfHost ? `bun ${selfHost.dir}/src/index.ts doctrine` : "vinaya doctrine";
3575
+ }
3576
+ function renderAgentSkill(roleName, selfHost = null) {
3554
3577
  const roleTitle = formatRoleTitle(roleName);
3555
3578
  return `---
3556
3579
  name: vinaya-${roleName}
3557
3580
  description: Act as the AEG ${roleTitle} for this repo.
3558
3581
  ---
3559
- Run \`vinaya doctrine --role ${roleName}\` and follow its output as your operating instructions for this session.
3582
+ Run \`${doctrineInvocation(selfHost)} --role ${roleName}\` and follow its output as your operating instructions for this session.
3560
3583
  `;
3561
3584
  }
3562
- function buildAgentsSkillsOps(doctrineRoot) {
3585
+ function buildAgentsSkillsOps(doctrineRoot, selfHost = null) {
3563
3586
  const roles = discoverRoleNames(doctrineRoot);
3564
3587
  return roles.map((role) => ({
3565
3588
  kind: "create-file",
3566
3589
  path: agentSkillPath(role),
3567
- content: renderAgentSkill(role),
3590
+ content: renderAgentSkill(role, selfHost),
3568
3591
  group: AGENTS_SKILLS_GROUP
3569
3592
  }));
3570
3593
  }
@@ -3572,20 +3595,24 @@ function buildAgentsSkillsOps(doctrineRoot) {
3572
3595
  // src/lib/claude-command-emitter.ts
3573
3596
  var CLAUDE_COMMAND_GROUP = "Claude Code command (.claude/commands/)";
3574
3597
  var CLAUDE_COMMAND_PATH = ".claude/commands/vinaya.md";
3575
- function renderClaudeCommand() {
3598
+ function doctrineInvocation2(selfHost) {
3599
+ return selfHost ? `bun ${selfHost.dir}/src/index.ts doctrine` : "vinaya doctrine";
3600
+ }
3601
+ function renderClaudeCommand(selfHost = null) {
3602
+ const invocation = doctrineInvocation2(selfHost);
3576
3603
  return `---
3577
3604
  description: Act as an AEG role for this repo.
3578
- allowed-tools: Bash(vinaya doctrine *)
3605
+ allowed-tools: Bash(${invocation} *)
3579
3606
  ---
3580
- !\`vinaya doctrine --role "$ARGUMENTS"\`
3607
+ !\`${invocation} --role "$ARGUMENTS"\`
3581
3608
  `;
3582
3609
  }
3583
- function buildClaudeCommandOps() {
3610
+ function buildClaudeCommandOps(selfHost = null) {
3584
3611
  return [
3585
3612
  {
3586
3613
  kind: "create-file",
3587
3614
  path: CLAUDE_COMMAND_PATH,
3588
- content: renderClaudeCommand(),
3615
+ content: renderClaudeCommand(selfHost),
3589
3616
  group: CLAUDE_COMMAND_GROUP
3590
3617
  }
3591
3618
  ];
@@ -3681,16 +3708,19 @@ function buildClaudeStopHookOps() {
3681
3708
  // src/lib/gemini-command-emitter.ts
3682
3709
  var GEMINI_COMMAND_GROUP = "Gemini CLI command (.gemini/commands/)";
3683
3710
  var GEMINI_COMMAND_PATH = ".gemini/commands/vinaya.toml";
3684
- function renderGeminiCommand() {
3711
+ function doctrineInvocation3(selfHost) {
3712
+ return selfHost ? `bun ${selfHost.dir}/src/index.ts doctrine` : "vinaya doctrine";
3713
+ }
3714
+ function renderGeminiCommand(selfHost = null) {
3685
3715
  return `description = "Act as an AEG role for this repo."
3686
- prompt = "!{vinaya doctrine --role {{args}}}"
3716
+ prompt = "!{${doctrineInvocation3(selfHost)} --role {{args}}}"
3687
3717
  `;
3688
3718
  }
3689
- function buildGeminiCommandOp() {
3719
+ function buildGeminiCommandOp(selfHost = null) {
3690
3720
  return {
3691
3721
  kind: "create-file",
3692
3722
  path: GEMINI_COMMAND_PATH,
3693
- content: renderGeminiCommand(),
3723
+ content: renderGeminiCommand(selfHost),
3694
3724
  group: GEMINI_COMMAND_GROUP
3695
3725
  };
3696
3726
  }
@@ -4388,8 +4418,19 @@ function preCommitBody(selfHost) {
4388
4418
  ${hookRun(selfHost, "check --all --diff-only --local --skip-full")}`;
4389
4419
  }
4390
4420
  function prePushBody(selfHost) {
4391
- return `# Vinaya pre-push gate. Runs branch/dispatch checks before the push leaves.
4421
+ const base = `# Vinaya pre-push gate. Runs branch/dispatch checks before the push leaves.
4422
+ # Forward git's own pre-push stdin (one "<local ref> <local sha> <remote
4423
+ # ref> <remote sha>" line per ref being pushed) so main-branch-refusal can
4424
+ # tell a tag-only push apart from one that also carries a branch ref.
4425
+ VINAYA_PUSH_REFS="$(cat)"
4426
+ export VINAYA_PUSH_REFS
4392
4427
  ${hookRun(selfHost, "check --all --local")}`;
4428
+ if (!selfHost)
4429
+ return base;
4430
+ return `${base}
4431
+ # Ring 0: the affected test suite. A failing test refuses the push with
4432
+ # its own output (#407, O4).
4433
+ bunx turbo test --affected || exit 1`;
4393
4434
  }
4394
4435
  function commitMsgBody(selfHost) {
4395
4436
  return `# Vinaya commit-message gate. Validates the message's first line against
@@ -4751,14 +4792,14 @@ function buildInitOps(ctx) {
4751
4792
  if (!doctrineRoot) {
4752
4793
  throw new Error('vinaya init: --agents includes "skills" but no bundled doctrine was found next to this CLI install — ' + "cannot discover agent-skill roles. Reinstall @attalabs/vinaya, or run bundle-doctrine first in a repo " + "that vendors the CLI.");
4753
4794
  }
4754
- ops.push(...buildAgentsSkillsOps(doctrineRoot));
4795
+ ops.push(...buildAgentsSkillsOps(doctrineRoot, ctx.selfHost));
4755
4796
  }
4756
4797
  if (ctx.agents.has("claude")) {
4757
- ops.push(...buildClaudeCommandOps());
4798
+ ops.push(...buildClaudeCommandOps(ctx.selfHost));
4758
4799
  ops.push(...buildClaudeStopHookOps());
4759
4800
  }
4760
4801
  if (ctx.agents.has("gemini")) {
4761
- ops.push(buildGeminiCommandOp());
4802
+ ops.push(buildGeminiCommandOp(ctx.selfHost));
4762
4803
  }
4763
4804
  if (ctx.agents.size > 0) {
4764
4805
  const affected = [];
@@ -4784,12 +4825,12 @@ function buildInitOps(ctx) {
4784
4825
  }
4785
4826
 
4786
4827
  // src/lib/ops.ts
4787
- import { execFileSync as execFileSync3 } from "node:child_process";
4828
+ import { execFileSync as execFileSync4 } from "node:child_process";
4788
4829
  import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
4789
4830
  import { dirname as dirname4, isAbsolute, join as join6, resolve as resolve2, sep as sep2 } from "node:path";
4790
4831
 
4791
4832
  // src/lib/config.ts
4792
- import { execFileSync as execFileSync2 } from "node:child_process";
4833
+ import { execFileSync as execFileSync3 } from "node:child_process";
4793
4834
  import { createHash as createHash2 } from "node:crypto";
4794
4835
  import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync5, realpathSync, writeFileSync } from "node:fs";
4795
4836
  import { homedir } from "node:os";
@@ -5062,7 +5103,7 @@ function trustAnchorRepo() {
5062
5103
  return validated;
5063
5104
  }
5064
5105
  try {
5065
- const url = execFileSync2("git", ["remote", "get-url", "origin"], {
5106
+ const url = execFileSync3("git", ["remote", "get-url", "origin"], {
5066
5107
  encoding: "utf-8",
5067
5108
  stdio: ["ignore", "pipe", "pipe"]
5068
5109
  }).trim();
@@ -5076,7 +5117,7 @@ function ghFetchTrustAnchorConfig() {
5076
5117
  const repo = trustAnchorRepo();
5077
5118
  if (!repo)
5078
5119
  throw new Error("could not resolve repo identity for the trust-anchor read");
5079
- return execFileSync2("gh", ["api", `repos/${repo}/contents/${LOCAL_CONFIG_FILENAME}`, "--jq", ".content"], {
5120
+ return execFileSync3("gh", ["api", `repos/${repo}/contents/${LOCAL_CONFIG_FILENAME}`, "--jq", ".content"], {
5080
5121
  encoding: "utf-8",
5081
5122
  stdio: ["ignore", "pipe", "pipe"]
5082
5123
  });
@@ -5177,7 +5218,7 @@ function writeTokensCollectTrustStore(storePath, store) {
5177
5218
  var GIT_IDENTITY_TIMEOUT_MS = 5000;
5178
5219
  function gitCommonDir(cwd = process.cwd()) {
5179
5220
  try {
5180
- const raw = execFileSync2("git", ["rev-parse", "--git-common-dir"], {
5221
+ const raw = execFileSync3("git", ["rev-parse", "--git-common-dir"], {
5181
5222
  cwd,
5182
5223
  encoding: "utf-8",
5183
5224
  stdio: ["ignore", "pipe", "pipe"],
@@ -5196,7 +5237,7 @@ function repoLocalConfigDir() {
5196
5237
  }
5197
5238
  function gitBlobHash(absolutePath, cwd) {
5198
5239
  try {
5199
- const raw = execFileSync2("git", ["hash-object", absolutePath], {
5240
+ const raw = execFileSync3("git", ["hash-object", absolutePath], {
5200
5241
  cwd,
5201
5242
  encoding: "utf-8",
5202
5243
  stdio: ["ignore", "pipe", "pipe"],
@@ -5247,7 +5288,7 @@ function resolveManagedBlockPath(repoRoot, opPath) {
5247
5288
  }
5248
5289
  function gitCommonDir2(repoRoot) {
5249
5290
  try {
5250
- const commonDir = execFileSync3("git", ["-C", repoRoot, "rev-parse", "--git-common-dir"], {
5291
+ const commonDir = execFileSync4("git", ["-C", repoRoot, "rev-parse", "--git-common-dir"], {
5251
5292
  encoding: "utf8"
5252
5293
  }).trim();
5253
5294
  if (commonDir === "")
@@ -5826,7 +5867,7 @@ function closeStdin() {
5826
5867
  }
5827
5868
 
5828
5869
  // src/commands/tokens.ts
5829
- import { execFileSync as execFileSync4 } from "node:child_process";
5870
+ import { execFileSync as execFileSync5 } from "node:child_process";
5830
5871
  import { resolve as resolvePath } from "node:path";
5831
5872
  var USAGE = [
5832
5873
  'Usage: vinaya tokens --phase "<task-id>: develop" --role Developer',
@@ -5981,7 +6022,7 @@ function realDeps() {
5981
6022
  ...hardenedMeteringDeps(),
5982
6023
  loadConfig,
5983
6024
  repoConfigDir: repoLocalConfigDir,
5984
- runScript: (interpreter, scriptAbsolutePath, cwd) => execFileSync4(interpreter, [scriptAbsolutePath], { cwd, encoding: "utf-8", timeout: COLLECT_COMMAND_TIMEOUT_MS }),
6025
+ runScript: (interpreter, scriptAbsolutePath, cwd) => execFileSync5(interpreter, [scriptAbsolutePath], { cwd, encoding: "utf-8", timeout: COLLECT_COMMAND_TIMEOUT_MS }),
5985
6026
  warn: (message) => console.error(message),
5986
6027
  gitCommonDir,
5987
6028
  scriptContentHash: gitBlobHash,
@@ -6107,7 +6148,7 @@ function renderArchiveTokensLine(capability, phase, role) {
6107
6148
  return { line: formatTokensLine({ phase, role, summary: capability.summary }), dangling: null };
6108
6149
  }
6109
6150
  function sh(args, input) {
6110
- return execFileSync5(args[0], args.slice(1), { encoding: "utf8", input, env: process.env }).trim();
6151
+ return execFileSync6(args[0], args.slice(1), { encoding: "utf8", input, env: process.env }).trim();
6111
6152
  }
6112
6153
  function shJson(args) {
6113
6154
  return JSON.parse(sh(args));
@@ -6321,7 +6362,7 @@ async function archiveTrancheCommand(args) {
6321
6362
  }
6322
6363
 
6323
6364
  // src/commands/audit.ts
6324
- import { execFileSync as execFileSync6 } from "node:child_process";
6365
+ import { execFileSync as execFileSync7 } from "node:child_process";
6325
6366
  function ring2Accelerated2() {
6326
6367
  return loadConfig()?.rings?.ring2_asyncAudits === true;
6327
6368
  }
@@ -6340,7 +6381,7 @@ var DIRECT_PUSH_MARKER = `<!-- ${DIRECT_PUSH_LABEL} -->`;
6340
6381
  var DIRECT_PUSH_LABEL_DESCRIPTION = "A commit landed on main with no associated merged PR (ring-2 detection)";
6341
6382
  function shSoft(args, input) {
6342
6383
  try {
6343
- return execFileSync6(args[0], args.slice(1), {
6384
+ return execFileSync7(args[0], args.slice(1), {
6344
6385
  encoding: "utf8",
6345
6386
  input,
6346
6387
  timeout: COMMAND_TIMEOUT_MS
@@ -6364,7 +6405,7 @@ function shSoftJson(args) {
6364
6405
  }
6365
6406
  }
6366
6407
  function sh2(args, input) {
6367
- return execFileSync6(args[0], args.slice(1), { encoding: "utf8", input }).trim();
6408
+ return execFileSync7(args[0], args.slice(1), { encoding: "utf8", input }).trim();
6368
6409
  }
6369
6410
  function listTaskBranches() {
6370
6411
  const out = shSoft(["git", "ls-remote", "--heads", "origin", "task/*"]);
@@ -6625,7 +6666,7 @@ async function auditCommand(args) {
6625
6666
 
6626
6667
  // src/commands/brief.ts
6627
6668
  import { createHash as createHash3 } from "node:crypto";
6628
- import { execFileSync as execFileSync8 } from "node:child_process";
6669
+ import { execFileSync as execFileSync9 } from "node:child_process";
6629
6670
  import { existsSync as existsSync7, readFileSync as readFileSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync3 } from "node:fs";
6630
6671
  import { join as join9 } from "node:path";
6631
6672
 
@@ -7112,6 +7153,26 @@ var COMMANDS = [
7112
7153
  "Never reimplements or edits `init`/`init product`/`demo break`/`doctor` — it only calls their existing, unmodified entry points in sequence."
7113
7154
  ],
7114
7155
  status: "shipped"
7156
+ },
7157
+ {
7158
+ name: "release",
7159
+ description: "Run this repo's own publish sequence in one command, refusing to start unless every precondition holds",
7160
+ flags: [
7161
+ {
7162
+ flag: "--dry-run",
7163
+ description: "Run the preconditions only and print the plan; publishes nothing"
7164
+ },
7165
+ {
7166
+ flag: "--allow-any-commit",
7167
+ description: "Skip the check that HEAD's commit is a Version Packages commit"
7168
+ }
7169
+ ],
7170
+ details: [
7171
+ "Refuses unless HEAD is the default branch, the working tree is clean, HEAD equals `origin/<default>` (after `git fetch origin`), HEAD's commit subject starts with `Chore(release): Version packages` (unless `--allow-any-commit`), and `npm whoami` exits `0` — each its own refusal naming the fix.",
7172
+ "Then streams `bun install --frozen-lockfile`, `bun run build`, `bun run changeset:publish`, and `git push origin --tags` — a real push, so the repo's own generated pre-push hook sees it exactly as any other push would. After it, prints `npm view <pkg> version` for every tag now on HEAD, noting registry lag on `@attalabs/vinaya` (observed ~20 minutes) when it still shows the previous version.",
7173
+ 'This is the one procedure named in `apps/cli/specs/self-hosting.md`, "How the published version is produced" — publishing itself stays manual and human-triggered; this command only removes the hand-typed four-step recipe.'
7174
+ ],
7175
+ status: "shipped"
7115
7176
  }
7116
7177
  ];
7117
7178
  // ../../packages/sources/src/doctrine-file-adapter.ts
@@ -7155,7 +7216,7 @@ var StateSourceConfigSchema = z2.discriminatedUnion("kind", [
7155
7216
  z2.object({ kind: z2.literal("file"), root: z2.string().optional() })
7156
7217
  ]);
7157
7218
  // src/checks/edge-resolve.ts
7158
- import { execFileSync as execFileSync7 } from "node:child_process";
7219
+ import { execFileSync as execFileSync8 } from "node:child_process";
7159
7220
  var DIRECT_ISSUE_REF = /#(\d+)/;
7160
7221
  var issueCache = new Map;
7161
7222
  function fetchIssueState(num, repo) {
@@ -7164,7 +7225,7 @@ function fetchIssueState(num, repo) {
7164
7225
  return issueCache.get(key) ?? null;
7165
7226
  let out = null;
7166
7227
  try {
7167
- const raw = execFileSync7("gh", [
7228
+ const raw = execFileSync8("gh", [
7168
7229
  "issue",
7169
7230
  "view",
7170
7231
  String(num),
@@ -7277,7 +7338,7 @@ var DOC_OWNERS_PATH2 = ".vinaya/doc-owners";
7277
7338
  var TEMPLATE_PATH = "aeg-root/templates/brief-template.md";
7278
7339
  function git(args) {
7279
7340
  try {
7280
- return execFileSync8("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
7341
+ return execFileSync9("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
7281
7342
  } catch {
7282
7343
  return "";
7283
7344
  }
@@ -7304,7 +7365,7 @@ async function resolveToken() {
7304
7365
  if (process.env.GH_TOKEN)
7305
7366
  return process.env.GH_TOKEN;
7306
7367
  try {
7307
- return execFileSync8("gh", ["auth", "token"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
7368
+ return execFileSync9("gh", ["auth", "token"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
7308
7369
  } catch {
7309
7370
  return null;
7310
7371
  }
@@ -7465,7 +7526,7 @@ ${result.missing.map((m) => ` - ${m}`).join(`
7465
7526
  }
7466
7527
 
7467
7528
  // src/commands/check.ts
7468
- import { execFileSync as execFileSync9 } from "node:child_process";
7529
+ import { execFileSync as execFileSync10 } from "node:child_process";
7469
7530
  import { dirname as dirname5 } from "node:path";
7470
7531
 
7471
7532
  // src/checks/contract.ts
@@ -8418,7 +8479,7 @@ async function buildRolePlan(configDir, configRoles) {
8418
8479
  // src/commands/check.ts
8419
8480
  function git2(args) {
8420
8481
  try {
8421
- return execFileSync9("git", args, { encoding: "utf8" }).trim();
8482
+ return execFileSync10("git", args, { encoding: "utf8" }).trim();
8422
8483
  } catch {
8423
8484
  return "";
8424
8485
  }
@@ -8682,14 +8743,14 @@ function commitMsgCommand(args) {
8682
8743
  }
8683
8744
 
8684
8745
  // src/commands/demo.ts
8685
- import { execFileSync as execFileSync10, spawnSync } from "node:child_process";
8746
+ import { execFileSync as execFileSync11, spawnSync } from "node:child_process";
8686
8747
  import { existsSync as existsSync10, readFileSync as readFileSync12, rmSync as rmSync2, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
8687
8748
  import { isAbsolute as isAbsolute2, join as join12 } from "node:path";
8688
8749
  var DEMO_BRANCH_PREFIX = "vinaya/demo-break-";
8689
8750
  var FIXTURE_PATH = ".vinaya-demo-brief.md";
8690
8751
  var STATE_FILENAME = "vinaya-demo-state.json";
8691
8752
  function gitCapture(repoRoot, args) {
8692
- return execFileSync10("git", args, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
8753
+ return execFileSync11("git", args, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
8693
8754
  }
8694
8755
  function currentBranchName(repoRoot) {
8695
8756
  try {
@@ -8700,7 +8761,7 @@ function currentBranchName(repoRoot) {
8700
8761
  }
8701
8762
  function branchExists(repoRoot, name) {
8702
8763
  try {
8703
- execFileSync10("git", ["rev-parse", "--verify", "--quiet", `refs/heads/${name}`], {
8764
+ execFileSync11("git", ["rev-parse", "--verify", "--quiet", `refs/heads/${name}`], {
8704
8765
  cwd: repoRoot,
8705
8766
  stdio: ["ignore", "pipe", "pipe"]
8706
8767
  });
@@ -8902,7 +8963,7 @@ async function demoBreakCommand(args) {
8902
8963
  }
8903
8964
 
8904
8965
  // src/commands/doctor.ts
8905
- import { execFileSync as execFileSync11 } from "node:child_process";
8966
+ import { execFileSync as execFileSync12 } from "node:child_process";
8906
8967
  import { existsSync as existsSync13, readdirSync as readdirSync7, readFileSync as readFileSync16, statSync as statSync2 } from "node:fs";
8907
8968
  import { delimiter, join as join15 } from "node:path";
8908
8969
 
@@ -9328,7 +9389,7 @@ function diagnoseCustomChecks(repoRoot, config) {
9328
9389
  }
9329
9390
  function listTrackedFiles(repoRoot) {
9330
9391
  try {
9331
- return execFileSync11("git", ["ls-files"], { cwd: repoRoot, encoding: "utf-8" }).split(`
9392
+ return execFileSync12("git", ["ls-files"], { cwd: repoRoot, encoding: "utf-8" }).split(`
9332
9393
  `).map((s) => s.trim()).filter(Boolean);
9333
9394
  } catch {
9334
9395
  return [];
@@ -9593,10 +9654,15 @@ function symbolFor(severity) {
9593
9654
  return "✗";
9594
9655
  }
9595
9656
  }
9596
- function printReport(findings, healthy) {
9657
+ function printReport(findings, healthy, doctrineInfo) {
9597
9658
  process.stdout.write(`vinaya doctor
9598
9659
 
9599
9660
  `);
9661
+ if (doctrineInfo) {
9662
+ process.stdout.write(`doctrine: ${doctrineInfo.root} (${doctrineInfo.source})
9663
+
9664
+ `);
9665
+ }
9600
9666
  for (const f of findings) {
9601
9667
  process.stdout.write(`${symbolFor(f.severity)} [${f.check}] ${f.message}
9602
9668
  `);
@@ -9612,6 +9678,7 @@ async function runDoctor(args, deps) {
9612
9678
  console.error("Error: not a git repository. Run `vinaya doctor` from inside your repo.");
9613
9679
  return 1;
9614
9680
  }
9681
+ const doctrineInfo = resolveDoctrineRootInfo(undefined, repo.repoRoot);
9615
9682
  const configRead = readConfig(repo.repoRoot);
9616
9683
  const findings = [];
9617
9684
  let hasDrift = false;
@@ -9654,7 +9721,7 @@ async function runDoctor(args, deps) {
9654
9721
  if (jsonOutput) {
9655
9722
  printJson({ healthy, findings });
9656
9723
  } else {
9657
- printReport(findings, healthy);
9724
+ printReport(findings, healthy, doctrineInfo);
9658
9725
  }
9659
9726
  return healthy ? 0 : 1;
9660
9727
  }
@@ -9978,10 +10045,10 @@ async function initProductCommand(args) {
9978
10045
  }
9979
10046
 
9980
10047
  // src/commands/issue.ts
9981
- import { execFileSync as execFileSync13 } from "node:child_process";
10048
+ import { execFileSync as execFileSync14 } from "node:child_process";
9982
10049
 
9983
10050
  // src/lib/forge-write.ts
9984
- import { execFileSync as execFileSync12 } from "node:child_process";
10051
+ import { execFileSync as execFileSync13 } from "node:child_process";
9985
10052
  import { existsSync as existsSync16, mkdtempSync, readdirSync as readdirSync8, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
9986
10053
  import { readFileSync as readFileSync19 } from "node:fs";
9987
10054
  import { tmpdir } from "node:os";
@@ -10075,7 +10142,7 @@ function ensureTrancheLabelExists(slug) {
10075
10142
  const name = trancheLabel(slug);
10076
10143
  let existing;
10077
10144
  try {
10078
- const out = execFileSync12("gh", ["label", "list", "--json", "name", "--limit", "200"], {
10145
+ const out = execFileSync13("gh", ["label", "list", "--json", "name", "--limit", "200"], {
10079
10146
  encoding: "utf8",
10080
10147
  stdio: ["ignore", "pipe", "pipe"]
10081
10148
  });
@@ -10088,7 +10155,7 @@ function ensureTrancheLabelExists(slug) {
10088
10155
  if (existing.some((l) => l.name === name))
10089
10156
  return;
10090
10157
  try {
10091
- execFileSync12("gh", ["label", "create", name, "--color", TRANCHE_LABEL_COLOR, "--description", `Tranche: ${slug}`], {
10158
+ execFileSync13("gh", ["label", "create", name, "--color", TRANCHE_LABEL_COLOR, "--description", `Tranche: ${slug}`], {
10092
10159
  encoding: "utf8",
10093
10160
  stdio: ["ignore", "pipe", "pipe"]
10094
10161
  });
@@ -10240,7 +10307,7 @@ function validateForgeWrite(input) {
10240
10307
  }
10241
10308
  function git3(args) {
10242
10309
  try {
10243
- return execFileSync12("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
10310
+ return execFileSync13("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
10244
10311
  } catch {
10245
10312
  return "";
10246
10313
  }
@@ -10335,7 +10402,7 @@ function locateBodyOrRefuse(ghArgs, retryCommand) {
10335
10402
  function fetchForgeLabels(issueRef) {
10336
10403
  let out;
10337
10404
  try {
10338
- out = execFileSync13("gh", ["issue", "view", issueRef, "--json", "labels"], {
10405
+ out = execFileSync14("gh", ["issue", "view", issueRef, "--json", "labels"], {
10339
10406
  encoding: "utf8",
10340
10407
  stdio: ["ignore", "pipe", "pipe"]
10341
10408
  });
@@ -10363,7 +10430,7 @@ function reportPass(json, command) {
10363
10430
  function runGhWrite(ghCmd, ghArgs, bodyResult, json) {
10364
10431
  const { finalArgs, cleanup: cleanup2 } = resolveShippableArgs(ghArgs, bodyResult);
10365
10432
  try {
10366
- const out = execFileSync13("gh", [...ghCmd, ...finalArgs], { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
10433
+ const out = execFileSync14("gh", [...ghCmd, ...finalArgs], { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
10367
10434
  const url = out.trim();
10368
10435
  if (json)
10369
10436
  printJson({ validated: true, written: true, url });
@@ -10449,13 +10516,13 @@ function issueEditCommand(args) {
10449
10516
  }
10450
10517
 
10451
10518
  // src/commands/milestone.ts
10452
- import { execFileSync as execFileSync14 } from "node:child_process";
10519
+ import { execFileSync as execFileSync15 } from "node:child_process";
10453
10520
  var RETRY_CREATE2 = "vinaya milestone create --title <title> --body-file <path>";
10454
10521
  var RETRY_ADOPT = "vinaya milestone adopt --target <title> --slug <slug> [--slug <slug> ...]";
10455
10522
  var RETRY_EDIT2 = "vinaya milestone edit <n> --body-file <path>";
10456
10523
  var RETRY_CLOSE = "vinaya milestone close --slug <slug>";
10457
10524
  function sh3(args, input) {
10458
- return execFileSync14(args[0], args.slice(1), {
10525
+ return execFileSync15(args[0], args.slice(1), {
10459
10526
  encoding: "utf8",
10460
10527
  input,
10461
10528
  env: process.env,
@@ -10955,7 +11022,7 @@ ${registration}
10955
11022
  }
10956
11023
 
10957
11024
  // src/commands/pr.ts
10958
- import { execFileSync as execFileSync15 } from "node:child_process";
11025
+ import { execFileSync as execFileSync16 } from "node:child_process";
10959
11026
  import { rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
10960
11027
  import { tmpdir as tmpdir2 } from "node:os";
10961
11028
  import { join as join22 } from "node:path";
@@ -11250,7 +11317,7 @@ var RETRY_CREATE3 = "vinaya pr create --validate-only …";
11250
11317
  var RETRY_EDIT3 = "vinaya pr edit <n> --validate-only …";
11251
11318
  function git4(args) {
11252
11319
  try {
11253
- return execFileSync15("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
11320
+ return execFileSync16("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
11254
11321
  } catch {
11255
11322
  return "";
11256
11323
  }
@@ -11284,7 +11351,7 @@ function reportPass2(json, command) {
11284
11351
  function runGhWrite2(ghCmd, ghArgs, bodyResult, json) {
11285
11352
  const { finalArgs, cleanup: cleanup2 } = resolveShippableArgs(ghArgs, bodyResult);
11286
11353
  try {
11287
- const out = execFileSync15("gh", [...ghCmd, ...finalArgs], { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
11354
+ const out = execFileSync16("gh", [...ghCmd, ...finalArgs], { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
11288
11355
  const url = out.trim();
11289
11356
  if (json)
11290
11357
  printJson({ validated: true, written: true, url });
@@ -11299,7 +11366,7 @@ function runGhWrite2(ghCmd, ghArgs, bodyResult, json) {
11299
11366
  function fetchPrForgeContext(prRef) {
11300
11367
  let viewOut;
11301
11368
  try {
11302
- viewOut = execFileSync15("gh", ["pr", "view", prRef, "--json", "headRefName,files"], {
11369
+ viewOut = execFileSync16("gh", ["pr", "view", prRef, "--json", "headRefName,files"], {
11303
11370
  encoding: "utf8",
11304
11371
  stdio: ["ignore", "pipe", "pipe"]
11305
11372
  });
@@ -11353,7 +11420,7 @@ ${brief}
11353
11420
  const tmp = join22(tmpdir2(), `vinaya-pr-create-brief-${process.pid}-${Date.now()}.md`);
11354
11421
  writeFileSync11(tmp, commentBody);
11355
11422
  try {
11356
- execFileSync15("gh", ["pr", "comment", prNumber, "--body-file", tmp], {
11423
+ execFileSync16("gh", ["pr", "comment", prNumber, "--body-file", tmp], {
11357
11424
  encoding: "utf8",
11358
11425
  stdio: ["ignore", "pipe", "pipe"]
11359
11426
  });
@@ -11476,7 +11543,7 @@ function prEditCommand(args) {
11476
11543
  }
11477
11544
 
11478
11545
  // src/commands/pr-report.ts
11479
- import { execFileSync as execFileSync16, spawnSync as spawnSync2 } from "node:child_process";
11546
+ import { execFileSync as execFileSync17, spawnSync as spawnSync2 } from "node:child_process";
11480
11547
  import { existsSync as existsSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync23, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
11481
11548
  import { tmpdir as tmpdir3 } from "node:os";
11482
11549
  import { join as join23 } from "node:path";
@@ -11484,14 +11551,14 @@ var EVIDENCE_START = "<!-- AEG:EVIDENCE:START -->";
11484
11551
  var EVIDENCE_END = "<!-- AEG:EVIDENCE:END -->";
11485
11552
  function git5(args) {
11486
11553
  try {
11487
- return execFileSync16("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
11554
+ return execFileSync17("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
11488
11555
  } catch {
11489
11556
  return "";
11490
11557
  }
11491
11558
  }
11492
11559
  function gh(args) {
11493
11560
  try {
11494
- return execFileSync16("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
11561
+ return execFileSync17("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
11495
11562
  } catch (err) {
11496
11563
  const stderr = err.stderr;
11497
11564
  throw new Error(String(stderr ?? err.message).trim() || "gh command failed");
@@ -11510,7 +11577,7 @@ class GitCommandError extends Error {
11510
11577
  }
11511
11578
  function gitStrict(args) {
11512
11579
  try {
11513
- return execFileSync16("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
11580
+ return execFileSync17("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
11514
11581
  } catch (err) {
11515
11582
  const stderr = err.stderr;
11516
11583
  throw new GitCommandError(args, String(stderr ?? err.message).trim() || "non-zero exit");
@@ -12028,7 +12095,7 @@ The AEG:EVIDENCE block was still written to ${writePath}.`);
12028
12095
  }
12029
12096
 
12030
12097
  // src/commands/pr-verify-evidence.ts
12031
- import { execFileSync as execFileSync17 } from "node:child_process";
12098
+ import { execFileSync as execFileSync18 } from "node:child_process";
12032
12099
 
12033
12100
  // src/commands/pr-verify-evidence-logic.ts
12034
12101
  function publishedMergeBase(region) {
@@ -12115,10 +12182,10 @@ function renderVerdict(verdict) {
12115
12182
 
12116
12183
  // src/commands/pr-verify-evidence.ts
12117
12184
  function gh2(args) {
12118
- return execFileSync17("gh", args, { encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
12185
+ return execFileSync18("gh", args, { encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
12119
12186
  }
12120
12187
  function assertCleanWorktree() {
12121
- const dirty = execFileSync17("git", ["status", "--porcelain"], {
12188
+ const dirty = execFileSync18("git", ["status", "--porcelain"], {
12122
12189
  encoding: "utf8",
12123
12190
  cwd: repoRoot2()
12124
12191
  }).trim();
@@ -12155,7 +12222,7 @@ function assertRepoRootCwd() {
12155
12222
  process.exit(2);
12156
12223
  }
12157
12224
  function repoRoot2() {
12158
- return execFileSync17("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
12225
+ return execFileSync18("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
12159
12226
  }
12160
12227
  async function prVerifyEvidenceCommand(args) {
12161
12228
  const prRef = args[0] && !args[0].startsWith("-") ? args[0] : undefined;
@@ -12179,7 +12246,7 @@ async function prVerifyEvidenceCommand(args) {
12179
12246
  process.exit(1);
12180
12247
  return;
12181
12248
  }
12182
- const localHead = execFileSync17("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).trim();
12249
+ const localHead = execFileSync18("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).trim();
12183
12250
  if (!forgeHead) {
12184
12251
  process.stderr.write(`pr verify-evidence: REFUSED — could not read #${prRef}'s head from the forge, so the verdict cannot be bound to a commit.
12185
12252
  `);
@@ -12202,7 +12269,7 @@ async function prVerifyEvidenceCommand(args) {
12202
12269
  }
12203
12270
 
12204
12271
  // src/commands/quickstart.ts
12205
- import { execFileSync as execFileSync18 } from "node:child_process";
12272
+ import { execFileSync as execFileSync19 } from "node:child_process";
12206
12273
  import { existsSync as existsSync22, readFileSync as readFileSync25 } from "node:fs";
12207
12274
  import { join as join25 } from "node:path";
12208
12275
 
@@ -12402,7 +12469,7 @@ function realDeps7() {
12402
12469
  };
12403
12470
  }
12404
12471
  function execGit(repoRoot3, args) {
12405
- return execFileSync18("git", args, { cwd: repoRoot3, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
12472
+ return execFileSync19("git", args, { cwd: repoRoot3, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
12406
12473
  }
12407
12474
  function errorDetail(err) {
12408
12475
  const stderr = err.stderr;
@@ -12535,8 +12602,212 @@ async function quickstartCommand(args) {
12535
12602
  process.exit(await runQuickstart(args, realDeps7()));
12536
12603
  }
12537
12604
 
12605
+ // src/commands/release.ts
12606
+ import { execFileSync as execFileSync20 } from "node:child_process";
12607
+ function realDeps8() {
12608
+ const git6 = (args) => {
12609
+ try {
12610
+ return execFileSync20("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
12611
+ } catch {
12612
+ return null;
12613
+ }
12614
+ };
12615
+ return {
12616
+ currentBranch() {
12617
+ const branch = git6(["symbolic-ref", "--quiet", "--short", "HEAD"]);
12618
+ return branch === null || branch === "" ? null : branch;
12619
+ },
12620
+ defaultBranch() {
12621
+ const ref = git6(["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"]);
12622
+ if (ref === null || !ref.startsWith("origin/"))
12623
+ return null;
12624
+ const branch = ref.slice("origin/".length);
12625
+ return branch === "" ? null : branch;
12626
+ },
12627
+ fetchOrigin() {
12628
+ try {
12629
+ execFileSync20("git", ["fetch", "origin"], { stdio: "inherit" });
12630
+ return true;
12631
+ } catch {
12632
+ return false;
12633
+ }
12634
+ },
12635
+ treeIsClean() {
12636
+ const status = git6(["status", "--porcelain"]);
12637
+ return status === "";
12638
+ },
12639
+ headSha() {
12640
+ return git6(["rev-parse", "HEAD"]);
12641
+ },
12642
+ defaultBranchSha(defaultBranch) {
12643
+ return git6(["rev-parse", `origin/${defaultBranch}`]);
12644
+ },
12645
+ headSubject() {
12646
+ return git6(["log", "-1", "--format=%s"]) ?? "";
12647
+ },
12648
+ npmWhoami() {
12649
+ try {
12650
+ execFileSync20("npm", ["whoami"], { stdio: "ignore" });
12651
+ return true;
12652
+ } catch {
12653
+ return false;
12654
+ }
12655
+ },
12656
+ runStreamed(cmd, args) {
12657
+ execFileSync20(cmd, args, { stdio: "inherit" });
12658
+ },
12659
+ tagsAtHead() {
12660
+ const tags = git6(["tag", "--points-at", "HEAD"]);
12661
+ if (tags === null || tags === "")
12662
+ return [];
12663
+ return tags.split(`
12664
+ `).filter((t) => t.length > 0);
12665
+ },
12666
+ npmViewVersion(pkg) {
12667
+ try {
12668
+ const out = execFileSync20("npm", ["view", pkg, "version"], {
12669
+ encoding: "utf8",
12670
+ stdio: ["ignore", "pipe", "pipe"]
12671
+ }).trim();
12672
+ return out === "" ? null : out;
12673
+ } catch {
12674
+ return null;
12675
+ }
12676
+ },
12677
+ log(message) {
12678
+ process.stdout.write(`${message}
12679
+ `);
12680
+ }
12681
+ };
12682
+ }
12683
+ var RELEASE_PLAN = [
12684
+ ["bun", "install", "--frozen-lockfile"],
12685
+ ["bun", "run", "build"],
12686
+ ["bun", "run", "changeset:publish"],
12687
+ ["git", "push", "origin", "--tags"]
12688
+ ];
12689
+ function parseTag(tag) {
12690
+ const at = tag.lastIndexOf("@");
12691
+ if (at <= 0)
12692
+ return null;
12693
+ return { pkg: tag.slice(0, at), version: tag.slice(at + 1) };
12694
+ }
12695
+ function runRelease(opts, deps) {
12696
+ const current = deps.currentBranch();
12697
+ const defaultBranch = deps.defaultBranch();
12698
+ if (defaultBranch === null) {
12699
+ return {
12700
+ ok: false,
12701
+ message: "vinaya release: could not determine this repo's default branch (no resolvable `origin/HEAD`) — run `git remote set-head origin -a` first."
12702
+ };
12703
+ }
12704
+ if (current !== defaultBranch) {
12705
+ return {
12706
+ ok: false,
12707
+ message: `vinaya release: HEAD is on \`${current ?? "(detached)"}\`, not the default branch \`${defaultBranch}\` — check out \`${defaultBranch}\` first.`
12708
+ };
12709
+ }
12710
+ if (!deps.treeIsClean()) {
12711
+ return {
12712
+ ok: false,
12713
+ message: "vinaya release: working tree is dirty — commit or stash your changes first."
12714
+ };
12715
+ }
12716
+ if (!deps.fetchOrigin()) {
12717
+ return {
12718
+ ok: false,
12719
+ message: "vinaya release: `git fetch origin` failed — check your network/auth and try again."
12720
+ };
12721
+ }
12722
+ const head = deps.headSha();
12723
+ const originSha = deps.defaultBranchSha(defaultBranch);
12724
+ if (head === null || originSha === null || head !== originSha) {
12725
+ return {
12726
+ ok: false,
12727
+ message: `vinaya release: HEAD does not equal \`origin/${defaultBranch}\` — run \`git pull --ff-only\` first.`
12728
+ };
12729
+ }
12730
+ const subject = deps.headSubject();
12731
+ if (!opts.allowAnyCommit && !subject.startsWith("Chore(release): Version packages")) {
12732
+ return {
12733
+ ok: false,
12734
+ message: `vinaya release: HEAD's commit "${subject}" is not a Version Packages commit — merge the Version Packages PR first, or pass \`--allow-any-commit\`.`
12735
+ };
12736
+ }
12737
+ if (!deps.npmWhoami()) {
12738
+ return {
12739
+ ok: false,
12740
+ message: "vinaya release: `npm whoami` failed — run `npm login` first."
12741
+ };
12742
+ }
12743
+ if (opts.dryRun) {
12744
+ return { ok: true, dryRun: true, plan: RELEASE_PLAN };
12745
+ }
12746
+ const ranSteps = [];
12747
+ let publishedAlready = false;
12748
+ for (const step of RELEASE_PLAN) {
12749
+ const [cmd, ...args] = step;
12750
+ deps.log(`vinaya release: running \`${step.join(" ")}\``);
12751
+ try {
12752
+ deps.runStreamed(cmd, args);
12753
+ } catch (error2) {
12754
+ const reason = error2 instanceof Error ? error2.message : String(error2);
12755
+ if (!publishedAlready) {
12756
+ return { ok: false, message: `vinaya release: \`${step.join(" ")}\` failed — ${reason}` };
12757
+ }
12758
+ const recoveryCommand = RELEASE_PLAN.slice(ranSteps.length).map((s) => s.join(" ")).join(" && ");
12759
+ return {
12760
+ ok: false,
12761
+ message: `vinaya release: \`${step.join(" ")}\` failed after publish already succeeded — packages are on the registry, the release is not finished. Ran: ${ranSteps.map((s) => s.join(" ")).join(", ") || "(nothing)"}. Failed: \`${step.join(" ")}\` — ${reason}. Finish by hand: \`${recoveryCommand}\``,
12762
+ ranSteps: [...ranSteps],
12763
+ failedStep: step,
12764
+ recoveryCommand
12765
+ };
12766
+ }
12767
+ deps.log(`vinaya release: \`${step.join(" ")}\` done`);
12768
+ ranSteps.push(step);
12769
+ if (cmd === "bun" && args.join(" ") === "run changeset:publish")
12770
+ publishedAlready = true;
12771
+ }
12772
+ const published = [];
12773
+ for (const tag of deps.tagsAtHead()) {
12774
+ const parsed = parseTag(tag);
12775
+ if (parsed === null)
12776
+ continue;
12777
+ const registryVersion = deps.npmViewVersion(parsed.pkg);
12778
+ const lagExpected = parsed.pkg === "@attalabs/vinaya" && registryVersion !== parsed.version;
12779
+ published.push({ tag, pkg: parsed.pkg, version: parsed.version, registryVersion, lagExpected });
12780
+ }
12781
+ return { ok: true, dryRun: false, published };
12782
+ }
12783
+ async function releaseCommand(args) {
12784
+ const dryRun = args.includes("--dry-run");
12785
+ const allowAnyCommit = args.includes("--allow-any-commit");
12786
+ const deps = realDeps8();
12787
+ const outcome = runRelease({ dryRun, allowAnyCommit }, deps);
12788
+ if (!outcome.ok) {
12789
+ console.error(outcome.message);
12790
+ process.exit(1);
12791
+ }
12792
+ if (outcome.dryRun) {
12793
+ deps.log("vinaya release --dry-run: preconditions pass. Plan:");
12794
+ for (const step of outcome.plan)
12795
+ deps.log(` ${step.join(" ")}`);
12796
+ process.exit(0);
12797
+ }
12798
+ for (const p of outcome.published) {
12799
+ const registry = p.registryVersion ?? "(not found)";
12800
+ if (p.lagExpected) {
12801
+ deps.log(`${p.tag}: npm view reports \`${registry}\` — registry lag expected (~20 min)`);
12802
+ } else {
12803
+ deps.log(`${p.tag}: npm view reports \`${registry}\``);
12804
+ }
12805
+ }
12806
+ process.exit(0);
12807
+ }
12808
+
12538
12809
  // src/commands/review-post.ts
12539
- import { execFileSync as execFileSync19 } from "node:child_process";
12810
+ import { execFileSync as execFileSync21 } from "node:child_process";
12540
12811
  import { readFileSync as readFileSync26, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
12541
12812
  import { tmpdir as tmpdir4 } from "node:os";
12542
12813
  import { join as join26 } from "node:path";
@@ -12925,7 +13196,7 @@ function normalizeSecurityVerdict(raw) {
12925
13196
  }
12926
13197
  function gh3(args) {
12927
13198
  try {
12928
- return execFileSync19("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
13199
+ return execFileSync21("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
12929
13200
  } catch (err) {
12930
13201
  const stderr = err.stderr;
12931
13202
  throw new Error(String(stderr ?? err.message).trim() || "gh command failed");
@@ -12945,7 +13216,7 @@ function resolveHeadRefName(pr) {
12945
13216
  }
12946
13217
  function shaFromLsRemote(branch) {
12947
13218
  try {
12948
- const out = execFileSync19("git", ["ls-remote", "origin", `refs/heads/${branch}`], {
13219
+ const out = execFileSync21("git", ["ls-remote", "origin", `refs/heads/${branch}`], {
12949
13220
  encoding: "utf8",
12950
13221
  stdio: ["ignore", "pipe", "pipe"]
12951
13222
  }).trim();
@@ -13007,13 +13278,13 @@ function fetchComments(pr) {
13007
13278
  }
13008
13279
  function computeChangedRanges(pr, judgedHead, resolvedHead) {
13009
13280
  try {
13010
- execFileSync19("git", ["fetch", "origin", resolvedHead], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
13281
+ execFileSync21("git", ["fetch", "origin", resolvedHead], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
13011
13282
  } catch (err) {
13012
13283
  refuseCmd(`Could not compute the round-two diff for PR ${pr}: \`git fetch origin ${resolvedHead}\` failed (${err instanceof Error ? err.message : String(err)}) — the PR's current head is not reachable from this worktree's origin remote.`, "Confirm the PR head is pushed to origin and reachable, then re-run.");
13013
13284
  }
13014
13285
  let out;
13015
13286
  try {
13016
- out = execFileSync19("git", ["diff", `${judgedHead}...${resolvedHead}`, "-U0"], {
13287
+ out = execFileSync21("git", ["diff", `${judgedHead}...${resolvedHead}`, "-U0"], {
13017
13288
  encoding: "utf8",
13018
13289
  stdio: ["ignore", "pipe", "pipe"]
13019
13290
  });
@@ -13312,11 +13583,11 @@ Self-verification: clean — re-parsed VERDICT is bound to head ${headSha}.
13312
13583
  }
13313
13584
 
13314
13585
  // src/commands/review-status.ts
13315
- import { execFileSync as execFileSync20 } from "node:child_process";
13586
+ import { execFileSync as execFileSync22 } from "node:child_process";
13316
13587
  var MAX_ROUNDS = 3;
13317
13588
  function fetchPr(prNumber) {
13318
13589
  try {
13319
- const out = execFileSync20("gh", ["pr", "view", prNumber, "--json", "comments,headRefOid,baseRefName"], {
13590
+ const out = execFileSync22("gh", ["pr", "view", prNumber, "--json", "comments,headRefOid,baseRefName"], {
13320
13591
  encoding: "utf8",
13321
13592
  stdio: ["ignore", "pipe", "pipe"]
13322
13593
  });
@@ -13327,7 +13598,7 @@ function fetchPr(prNumber) {
13327
13598
  }
13328
13599
  function behindBy(base) {
13329
13600
  try {
13330
- const out = execFileSync20("git", ["rev-list", "--count", `HEAD..origin/${base}`], {
13601
+ const out = execFileSync22("git", ["rev-list", "--count", `HEAD..origin/${base}`], {
13331
13602
  encoding: "utf8",
13332
13603
  stdio: ["ignore", "pipe", "pipe"]
13333
13604
  }).trim();
@@ -13526,7 +13797,7 @@ async function runStudio(cwd, args, moduleUrl = import.meta.url) {
13526
13797
  // src/commands/upgrade.ts
13527
13798
  import { existsSync as existsSync24, readFileSync as readFileSync28, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "node:fs";
13528
13799
  import { join as join28 } from "node:path";
13529
- function realDeps8() {
13800
+ function realDeps9() {
13530
13801
  return {
13531
13802
  detectRepo: detectGitRepo,
13532
13803
  hookDirFor: resolveHookDir,
@@ -13921,16 +14192,16 @@ Vinaya upgraded.
13921
14192
  return 0;
13922
14193
  }
13923
14194
  async function upgradeCommand(args) {
13924
- process.exit(await runUpgrade(args, realDeps8()));
14195
+ process.exit(await runUpgrade(args, realDeps9()));
13925
14196
  }
13926
14197
 
13927
14198
  // src/commands/waiver.ts
13928
- import { execFileSync as execFileSync21 } from "node:child_process";
14199
+ import { execFileSync as execFileSync23 } from "node:child_process";
13929
14200
  function labelFor(kind) {
13930
14201
  return kind === "docs" ? WAIVER_LABEL : WAIVER_LABEL_REVIEW;
13931
14202
  }
13932
14203
  function gh4(args) {
13933
- return execFileSync21("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], env: process.env }).trim();
14204
+ return execFileSync23("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], env: process.env }).trim();
13934
14205
  }
13935
14206
  function detectPrNumber() {
13936
14207
  try {
@@ -14030,8 +14301,8 @@ Reason: ${reason}`];
14030
14301
  }
14031
14302
  process.stdout.write(`Applying \`${label2}\` to PR #${prNumber} under your authenticated \`gh\` identity — vinaya never fabricates an actor.
14032
14303
  `);
14033
- execFileSync21("gh", editArgs, { stdio: "inherit", env: process.env });
14034
- execFileSync21("gh", commentArgs, { stdio: "inherit", env: process.env });
14304
+ execFileSync23("gh", editArgs, { stdio: "inherit", env: process.env });
14305
+ execFileSync23("gh", commentArgs, { stdio: "inherit", env: process.env });
14035
14306
  process.stdout.write(`
14036
14307
  ✓ Label applied and reason posted as a PR comment.
14037
14308
  `);
@@ -14227,6 +14498,10 @@ try {
14227
14498
  await waiverCommand(args);
14228
14499
  break;
14229
14500
  }
14501
+ case "release": {
14502
+ await releaseCommand(args);
14503
+ break;
14504
+ }
14230
14505
  case "quickstart": {
14231
14506
  await quickstartCommand(args);
14232
14507
  break;