@attalabs/vinaya 0.23.0 → 0.24.1
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/README.md +1 -0
- package/aeg-root/enforcement.md +1 -1
- package/aeg-root/roles/developer.md +1 -1
- package/dist/checks/bin/check-body-bare-digits.js +7 -1
- package/dist/checks/bin/check-branch-topology.js +27 -1
- package/dist/checks/bin/check-brief-shape.js +7 -1
- package/dist/checks/bin/check-changeset-coverage.js +19 -1
- package/dist/checks/bin/check-closes-n.js +27 -1
- package/dist/checks/bin/check-coherence.js +27 -1
- package/dist/checks/bin/check-dead-branch-push.js +7 -1
- package/dist/checks/bin/check-dispatch-readiness.js +27 -1
- package/dist/checks/bin/check-doc-coverage-push.js +7 -1
- package/dist/checks/bin/check-doc-coverage.js +7 -1
- package/dist/checks/bin/check-doctrine-no-procedures.js +7 -1
- package/dist/checks/bin/check-doctrine-portability.js +7 -1
- package/dist/checks/bin/check-evidence-fresh.js +7 -1
- package/dist/checks/bin/check-exec-bits.js +7 -1
- package/dist/checks/bin/check-first-push-dispatch.js +27 -1
- package/dist/checks/bin/check-issue-assignment.js +27 -1
- package/dist/checks/bin/check-main-branch-refusal.js +13 -2
- package/dist/checks/bin/check-no-disk-state.js +7 -1
- package/dist/checks/bin/check-pr-report-density.js +7 -1
- package/dist/checks/bin/check-quoted-command.js +38 -12
- package/dist/checks/bin/check-reader-resolvable-prose.js +38 -12
- package/dist/checks/bin/check-registry-gates.js +8 -2
- package/dist/checks/bin/check-retired-vocabulary.js +38 -12
- package/dist/checks/bin/check-review-gate.js +7 -1
- package/dist/checks/bin/check-single-plan-pr.js +7 -1
- package/dist/checks/bin/check-test-plan.js +7 -1
- package/dist/checks/bin/check-token-collection-wired.js +7 -1
- package/dist/checks/bin/check-token-report.js +7 -1
- package/dist/checks/bin/check-workspace-escape.js +17 -3
- package/dist/index.js +405 -98
- package/package.json +2 -2
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/anchored-region.ts +9 -4
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/blast-radius-domains.ts +7 -1
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-render.ts +462 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-validation.ts +405 -4
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/coherence-checks.ts +19 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/consumer-enumeration.ts +75 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dispatch-gate.ts +44 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-no-procedures.ts +94 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-portability.ts +128 -5
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/gate-audience.ts +3 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/index.ts +25 -3
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-token-report.ts +17 -5
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/pr-report-density.ts +116 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/premise-check.ts +35 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/quoted-command.ts +3 -3
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-gate.ts +116 -7
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-status.ts +200 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/test-plan-gate.ts +25 -12
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/verdict-extraction.ts +70 -11
- package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__02a-3g8._.js +1 -1
- 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
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
- 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
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
- 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
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0jer_drkl1e4r.js → 0zebsmmk1bvnb.js} +4 -4
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{20x-t0a0v129z.js → 1uogpj5w2n5ju.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2ou_81ahqw31e.js → 3m1kgax7j2vgs.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
- /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
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_buildManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_clientMiddlewareManifest.js +0 -0
- /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
|
|
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);
|
|
@@ -3309,7 +3309,7 @@ function renderSection6(facts) {
|
|
|
3309
3309
|
" Files:",
|
|
3310
3310
|
...files.map((f) => ` - ${f.path}`),
|
|
3311
3311
|
"",
|
|
3312
|
-
`
|
|
3312
|
+
` Touches ${pkg}. The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.`
|
|
3313
3313
|
].join(`
|
|
3314
3314
|
`));
|
|
3315
3315
|
n++;
|
|
@@ -3335,7 +3335,7 @@ function renderSection8() {
|
|
|
3335
3335
|
return [
|
|
3336
3336
|
"## 8. Verification before claiming done",
|
|
3337
3337
|
"",
|
|
3338
|
-
"- `
|
|
3338
|
+
"- The pre-push hook already ran the affected suite on your one push and refused it on failure — do not additionally run it yourself; `vinaya pr report --write`/`--push` separately re-runs it with `--force` to attest the command and its output in the Evidence block.",
|
|
3339
3339
|
"- The full `bun run test` suite is CI's to run, on the one push — never run it locally.",
|
|
3340
3340
|
"- Every blast-radius consumer named in §4, re-verified by name.",
|
|
3341
3341
|
'- `roles/developer.md`\'s tier checklist genuinely satisfied, and `PR_BODY="$(cat <body-file>)" bun packages/aeg-core/bin/verify-docs.ts --pr` green.'
|
|
@@ -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
|
|
3483
|
-
return hasDoctrineEntry(
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
3605
|
+
allowed-tools: Bash(${invocation} *)
|
|
3579
3606
|
---
|
|
3580
|
-
|
|
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
|
|
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 = "!{
|
|
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
|
}
|
|
@@ -3881,7 +3911,7 @@ ${vinayaSetupSteps(selfHost, "pull-request")}${adopterSetupStep(ciSetup)} #
|
|
|
3881
3911
|
GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
3882
3912
|
PR_NUMBER: \${{ github.event.pull_request.number }}
|
|
3883
3913
|
run: |
|
|
3884
|
-
DELIM="PR_BODY_$(
|
|
3914
|
+
DELIM="PR_BODY_$(openssl rand -hex 16)"
|
|
3885
3915
|
echo "PR_BODY<<$DELIM" >> "$GITHUB_ENV"
|
|
3886
3916
|
gh pr view "$PR_NUMBER" --json body --jq .body >> "$GITHUB_ENV"
|
|
3887
3917
|
echo "$DELIM" >> "$GITHUB_ENV"
|
|
@@ -4051,13 +4081,26 @@ jobs:
|
|
|
4051
4081
|
# lookup vinaya-review-verdict.yml's own retrigger step runs.
|
|
4052
4082
|
set -o pipefail
|
|
4053
4083
|
RUN_TITLE="Vinaya Review Gate PR #$PR_NUMBER @ $HEAD_SHA"
|
|
4084
|
+
# O7 (found live 2026-09-04): dropped \`select(.status == "completed")\`
|
|
4085
|
+
# — two retriggers for the same head race the SAME required run
|
|
4086
|
+
# (PR #401, PR #409: runs 33841069791/33841065139), and the loser's
|
|
4087
|
+
# query landed while the winner's \`gh run rerun\` had already
|
|
4088
|
+
# flipped the run to \`in_progress\`. Requiring \`completed\` made
|
|
4089
|
+
# that run invisible to the loser entirely — not "already handled,
|
|
4090
|
+
# skip", but "nothing matched, give up" — so when that in-flight
|
|
4091
|
+
# attempt itself later failed, nothing retried it again and the
|
|
4092
|
+
# gate stayed red until a hand rerun. Matching on title+event alone
|
|
4093
|
+
# (any status) lets the loser find the SAME run mid-run and try
|
|
4094
|
+
# \`gh run rerun\` on it too; the existing "declined (already
|
|
4095
|
+
# queued)" fallback below already handles that outcome harmlessly
|
|
4096
|
+
# — it is not a new failure mode, only a query that used to see
|
|
4097
|
+
# nothing at all now sees the run and takes the same safe no-op.
|
|
4054
4098
|
RUN_ID=$(gh api --paginate \\
|
|
4055
4099
|
"repos/\${{ github.repository }}/actions/workflows/vinaya-review.yml/runs?event=pull_request_target&per_page=100" \\
|
|
4056
4100
|
| jq -sr --arg title "$RUN_TITLE" '
|
|
4057
4101
|
[.[].workflow_runs[]
|
|
4058
4102
|
| select(.display_title == $title)
|
|
4059
4103
|
| select(.event == "pull_request_target")
|
|
4060
|
-
| select(.status == "completed")
|
|
4061
4104
|
| select(.conclusion != "cancelled")
|
|
4062
4105
|
| .id][0] // empty')
|
|
4063
4106
|
if [ -z "$RUN_ID" ]; then
|
|
@@ -4134,7 +4177,7 @@ ${vinayaSetupSteps(selfHost, "trusted")} # PR_BODY is what makes body-bare-
|
|
|
4134
4177
|
GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
4135
4178
|
PR_NUMBER: \${{ github.event.pull_request.number }}
|
|
4136
4179
|
run: |
|
|
4137
|
-
DELIM="PR_BODY_$(
|
|
4180
|
+
DELIM="PR_BODY_$(openssl rand -hex 16)"
|
|
4138
4181
|
echo "PR_BODY<<$DELIM" >> "$GITHUB_ENV"
|
|
4139
4182
|
gh pr view "$PR_NUMBER" --json body --jq .body >> "$GITHUB_ENV"
|
|
4140
4183
|
echo "$DELIM" >> "$GITHUB_ENV"
|
|
@@ -4255,13 +4298,26 @@ ${vinayaSetupSteps(selfHost, "trusted")} - name: Review gate (verdict evalu
|
|
|
4255
4298
|
# query matches that immutable display_title exactly.
|
|
4256
4299
|
set -o pipefail
|
|
4257
4300
|
RUN_TITLE="Vinaya Review Gate PR #$PR_NUMBER @ $HEAD_SHA"
|
|
4301
|
+
# O7 (found live 2026-09-04): dropped \`select(.status == "completed")\`
|
|
4302
|
+
# — two retriggers for the same head race the SAME required run
|
|
4303
|
+
# (PR #401, PR #409: runs 33841069791/33841065139), and the loser's
|
|
4304
|
+
# query landed while the winner's \`gh run rerun\` had already
|
|
4305
|
+
# flipped the run to \`in_progress\`. Requiring \`completed\` made
|
|
4306
|
+
# that run invisible to the loser entirely — not "already handled,
|
|
4307
|
+
# skip", but "nothing matched, give up" — so when that in-flight
|
|
4308
|
+
# attempt itself later failed, nothing retried it again and the
|
|
4309
|
+
# gate stayed red until a hand rerun. Matching on title+event alone
|
|
4310
|
+
# (any status) lets the loser find the SAME run mid-run and try
|
|
4311
|
+
# \`gh run rerun\` on it too; the existing "declined (already
|
|
4312
|
+
# queued)" fallback below already handles that outcome harmlessly
|
|
4313
|
+
# — it is not a new failure mode, only a query that used to see
|
|
4314
|
+
# nothing at all now sees the run and takes the same safe no-op.
|
|
4258
4315
|
RUN_ID=$(gh api --paginate \\
|
|
4259
4316
|
"repos/\${{ github.repository }}/actions/workflows/vinaya-review.yml/runs?event=pull_request_target&per_page=100" \\
|
|
4260
4317
|
| jq -sr --arg title "$RUN_TITLE" '
|
|
4261
4318
|
[.[].workflow_runs[]
|
|
4262
4319
|
| select(.display_title == $title)
|
|
4263
4320
|
| select(.event == "pull_request_target")
|
|
4264
|
-
| select(.status == "completed")
|
|
4265
4321
|
| select(.conclusion != "cancelled")
|
|
4266
4322
|
| .id][0] // empty')
|
|
4267
4323
|
if [ -z "$RUN_ID" ]; then
|
|
@@ -4388,8 +4444,24 @@ function preCommitBody(selfHost) {
|
|
|
4388
4444
|
${hookRun(selfHost, "check --all --diff-only --local --skip-full")}`;
|
|
4389
4445
|
}
|
|
4390
4446
|
function prePushBody(selfHost) {
|
|
4391
|
-
|
|
4447
|
+
const base = `# Vinaya pre-push gate. Runs branch/dispatch checks before the push leaves.
|
|
4448
|
+
# Forward git's own pre-push stdin (one "<local ref> <local sha> <remote
|
|
4449
|
+
# ref> <remote sha>" line per ref being pushed) so main-branch-refusal can
|
|
4450
|
+
# tell a tag-only push apart from one that also carries a branch ref.
|
|
4451
|
+
VINAYA_PUSH_REFS="$(cat)"
|
|
4452
|
+
export VINAYA_PUSH_REFS
|
|
4392
4453
|
${hookRun(selfHost, "check --all --local")}`;
|
|
4454
|
+
if (!selfHost)
|
|
4455
|
+
return base;
|
|
4456
|
+
return `${base}
|
|
4457
|
+
# Ring 0: the affected test suite. A failing test refuses the push with
|
|
4458
|
+
# its own output (#407, O4). --concurrency=1 (O9, found live 2026-09-04):
|
|
4459
|
+
# a local machine already running other work (another worktree's own
|
|
4460
|
+
# build/test, an IDE indexer) alongside this hook's parallel package
|
|
4461
|
+
# suites was measured pushing a git-clone-heavy fixture test past its
|
|
4462
|
+
# timeout under real contention — CI's own runner is dedicated and keeps
|
|
4463
|
+
# turbo.json's default concurrency; only this hook invocation is serialized.
|
|
4464
|
+
bunx turbo test --affected --concurrency=1 || exit 1`;
|
|
4393
4465
|
}
|
|
4394
4466
|
function commitMsgBody(selfHost) {
|
|
4395
4467
|
return `# Vinaya commit-message gate. Validates the message's first line against
|
|
@@ -4751,14 +4823,14 @@ function buildInitOps(ctx) {
|
|
|
4751
4823
|
if (!doctrineRoot) {
|
|
4752
4824
|
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
4825
|
}
|
|
4754
|
-
ops.push(...buildAgentsSkillsOps(doctrineRoot));
|
|
4826
|
+
ops.push(...buildAgentsSkillsOps(doctrineRoot, ctx.selfHost));
|
|
4755
4827
|
}
|
|
4756
4828
|
if (ctx.agents.has("claude")) {
|
|
4757
|
-
ops.push(...buildClaudeCommandOps());
|
|
4829
|
+
ops.push(...buildClaudeCommandOps(ctx.selfHost));
|
|
4758
4830
|
ops.push(...buildClaudeStopHookOps());
|
|
4759
4831
|
}
|
|
4760
4832
|
if (ctx.agents.has("gemini")) {
|
|
4761
|
-
ops.push(buildGeminiCommandOp());
|
|
4833
|
+
ops.push(buildGeminiCommandOp(ctx.selfHost));
|
|
4762
4834
|
}
|
|
4763
4835
|
if (ctx.agents.size > 0) {
|
|
4764
4836
|
const affected = [];
|
|
@@ -4784,12 +4856,12 @@ function buildInitOps(ctx) {
|
|
|
4784
4856
|
}
|
|
4785
4857
|
|
|
4786
4858
|
// src/lib/ops.ts
|
|
4787
|
-
import { execFileSync as
|
|
4859
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
4788
4860
|
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4789
4861
|
import { dirname as dirname4, isAbsolute, join as join6, resolve as resolve2, sep as sep2 } from "node:path";
|
|
4790
4862
|
|
|
4791
4863
|
// src/lib/config.ts
|
|
4792
|
-
import { execFileSync as
|
|
4864
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
4793
4865
|
import { createHash as createHash2 } from "node:crypto";
|
|
4794
4866
|
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync5, realpathSync, writeFileSync } from "node:fs";
|
|
4795
4867
|
import { homedir } from "node:os";
|
|
@@ -5062,7 +5134,7 @@ function trustAnchorRepo() {
|
|
|
5062
5134
|
return validated;
|
|
5063
5135
|
}
|
|
5064
5136
|
try {
|
|
5065
|
-
const url =
|
|
5137
|
+
const url = execFileSync3("git", ["remote", "get-url", "origin"], {
|
|
5066
5138
|
encoding: "utf-8",
|
|
5067
5139
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5068
5140
|
}).trim();
|
|
@@ -5076,7 +5148,7 @@ function ghFetchTrustAnchorConfig() {
|
|
|
5076
5148
|
const repo = trustAnchorRepo();
|
|
5077
5149
|
if (!repo)
|
|
5078
5150
|
throw new Error("could not resolve repo identity for the trust-anchor read");
|
|
5079
|
-
return
|
|
5151
|
+
return execFileSync3("gh", ["api", `repos/${repo}/contents/${LOCAL_CONFIG_FILENAME}`, "--jq", ".content"], {
|
|
5080
5152
|
encoding: "utf-8",
|
|
5081
5153
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5082
5154
|
});
|
|
@@ -5177,7 +5249,7 @@ function writeTokensCollectTrustStore(storePath, store) {
|
|
|
5177
5249
|
var GIT_IDENTITY_TIMEOUT_MS = 5000;
|
|
5178
5250
|
function gitCommonDir(cwd = process.cwd()) {
|
|
5179
5251
|
try {
|
|
5180
|
-
const raw =
|
|
5252
|
+
const raw = execFileSync3("git", ["rev-parse", "--git-common-dir"], {
|
|
5181
5253
|
cwd,
|
|
5182
5254
|
encoding: "utf-8",
|
|
5183
5255
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5196,7 +5268,7 @@ function repoLocalConfigDir() {
|
|
|
5196
5268
|
}
|
|
5197
5269
|
function gitBlobHash(absolutePath, cwd) {
|
|
5198
5270
|
try {
|
|
5199
|
-
const raw =
|
|
5271
|
+
const raw = execFileSync3("git", ["hash-object", absolutePath], {
|
|
5200
5272
|
cwd,
|
|
5201
5273
|
encoding: "utf-8",
|
|
5202
5274
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5247,7 +5319,7 @@ function resolveManagedBlockPath(repoRoot, opPath) {
|
|
|
5247
5319
|
}
|
|
5248
5320
|
function gitCommonDir2(repoRoot) {
|
|
5249
5321
|
try {
|
|
5250
|
-
const commonDir =
|
|
5322
|
+
const commonDir = execFileSync4("git", ["-C", repoRoot, "rev-parse", "--git-common-dir"], {
|
|
5251
5323
|
encoding: "utf8"
|
|
5252
5324
|
}).trim();
|
|
5253
5325
|
if (commonDir === "")
|
|
@@ -5826,7 +5898,7 @@ function closeStdin() {
|
|
|
5826
5898
|
}
|
|
5827
5899
|
|
|
5828
5900
|
// src/commands/tokens.ts
|
|
5829
|
-
import { execFileSync as
|
|
5901
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
5830
5902
|
import { resolve as resolvePath } from "node:path";
|
|
5831
5903
|
var USAGE = [
|
|
5832
5904
|
'Usage: vinaya tokens --phase "<task-id>: develop" --role Developer',
|
|
@@ -5981,7 +6053,7 @@ function realDeps() {
|
|
|
5981
6053
|
...hardenedMeteringDeps(),
|
|
5982
6054
|
loadConfig,
|
|
5983
6055
|
repoConfigDir: repoLocalConfigDir,
|
|
5984
|
-
runScript: (interpreter, scriptAbsolutePath, cwd) =>
|
|
6056
|
+
runScript: (interpreter, scriptAbsolutePath, cwd) => execFileSync5(interpreter, [scriptAbsolutePath], { cwd, encoding: "utf-8", timeout: COLLECT_COMMAND_TIMEOUT_MS }),
|
|
5985
6057
|
warn: (message) => console.error(message),
|
|
5986
6058
|
gitCommonDir,
|
|
5987
6059
|
scriptContentHash: gitBlobHash,
|
|
@@ -6107,7 +6179,7 @@ function renderArchiveTokensLine(capability, phase, role) {
|
|
|
6107
6179
|
return { line: formatTokensLine({ phase, role, summary: capability.summary }), dangling: null };
|
|
6108
6180
|
}
|
|
6109
6181
|
function sh(args, input) {
|
|
6110
|
-
return
|
|
6182
|
+
return execFileSync6(args[0], args.slice(1), { encoding: "utf8", input, env: process.env }).trim();
|
|
6111
6183
|
}
|
|
6112
6184
|
function shJson(args) {
|
|
6113
6185
|
return JSON.parse(sh(args));
|
|
@@ -6321,7 +6393,7 @@ async function archiveTrancheCommand(args) {
|
|
|
6321
6393
|
}
|
|
6322
6394
|
|
|
6323
6395
|
// src/commands/audit.ts
|
|
6324
|
-
import { execFileSync as
|
|
6396
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
6325
6397
|
function ring2Accelerated2() {
|
|
6326
6398
|
return loadConfig()?.rings?.ring2_asyncAudits === true;
|
|
6327
6399
|
}
|
|
@@ -6340,7 +6412,7 @@ var DIRECT_PUSH_MARKER = `<!-- ${DIRECT_PUSH_LABEL} -->`;
|
|
|
6340
6412
|
var DIRECT_PUSH_LABEL_DESCRIPTION = "A commit landed on main with no associated merged PR (ring-2 detection)";
|
|
6341
6413
|
function shSoft(args, input) {
|
|
6342
6414
|
try {
|
|
6343
|
-
return
|
|
6415
|
+
return execFileSync7(args[0], args.slice(1), {
|
|
6344
6416
|
encoding: "utf8",
|
|
6345
6417
|
input,
|
|
6346
6418
|
timeout: COMMAND_TIMEOUT_MS
|
|
@@ -6364,7 +6436,7 @@ function shSoftJson(args) {
|
|
|
6364
6436
|
}
|
|
6365
6437
|
}
|
|
6366
6438
|
function sh2(args, input) {
|
|
6367
|
-
return
|
|
6439
|
+
return execFileSync7(args[0], args.slice(1), { encoding: "utf8", input }).trim();
|
|
6368
6440
|
}
|
|
6369
6441
|
function listTaskBranches() {
|
|
6370
6442
|
const out = shSoft(["git", "ls-remote", "--heads", "origin", "task/*"]);
|
|
@@ -6625,7 +6697,7 @@ async function auditCommand(args) {
|
|
|
6625
6697
|
|
|
6626
6698
|
// src/commands/brief.ts
|
|
6627
6699
|
import { createHash as createHash3 } from "node:crypto";
|
|
6628
|
-
import { execFileSync as
|
|
6700
|
+
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
6629
6701
|
import { existsSync as existsSync7, readFileSync as readFileSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6630
6702
|
import { join as join9 } from "node:path";
|
|
6631
6703
|
|
|
@@ -7112,6 +7184,26 @@ var COMMANDS = [
|
|
|
7112
7184
|
"Never reimplements or edits `init`/`init product`/`demo break`/`doctor` — it only calls their existing, unmodified entry points in sequence."
|
|
7113
7185
|
],
|
|
7114
7186
|
status: "shipped"
|
|
7187
|
+
},
|
|
7188
|
+
{
|
|
7189
|
+
name: "release",
|
|
7190
|
+
description: "Run this repo's own publish sequence in one command, refusing to start unless every precondition holds",
|
|
7191
|
+
flags: [
|
|
7192
|
+
{
|
|
7193
|
+
flag: "--dry-run",
|
|
7194
|
+
description: "Run the preconditions only and print the plan; publishes nothing"
|
|
7195
|
+
},
|
|
7196
|
+
{
|
|
7197
|
+
flag: "--allow-any-commit",
|
|
7198
|
+
description: "Skip the check that HEAD's commit is a Version Packages commit"
|
|
7199
|
+
}
|
|
7200
|
+
],
|
|
7201
|
+
details: [
|
|
7202
|
+
"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.",
|
|
7203
|
+
"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.",
|
|
7204
|
+
'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.'
|
|
7205
|
+
],
|
|
7206
|
+
status: "shipped"
|
|
7115
7207
|
}
|
|
7116
7208
|
];
|
|
7117
7209
|
// ../../packages/sources/src/doctrine-file-adapter.ts
|
|
@@ -7155,7 +7247,7 @@ var StateSourceConfigSchema = z2.discriminatedUnion("kind", [
|
|
|
7155
7247
|
z2.object({ kind: z2.literal("file"), root: z2.string().optional() })
|
|
7156
7248
|
]);
|
|
7157
7249
|
// src/checks/edge-resolve.ts
|
|
7158
|
-
import { execFileSync as
|
|
7250
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
7159
7251
|
var DIRECT_ISSUE_REF = /#(\d+)/;
|
|
7160
7252
|
var issueCache = new Map;
|
|
7161
7253
|
function fetchIssueState(num, repo) {
|
|
@@ -7164,7 +7256,7 @@ function fetchIssueState(num, repo) {
|
|
|
7164
7256
|
return issueCache.get(key) ?? null;
|
|
7165
7257
|
let out = null;
|
|
7166
7258
|
try {
|
|
7167
|
-
const raw =
|
|
7259
|
+
const raw = execFileSync8("gh", [
|
|
7168
7260
|
"issue",
|
|
7169
7261
|
"view",
|
|
7170
7262
|
String(num),
|
|
@@ -7277,7 +7369,7 @@ var DOC_OWNERS_PATH2 = ".vinaya/doc-owners";
|
|
|
7277
7369
|
var TEMPLATE_PATH = "aeg-root/templates/brief-template.md";
|
|
7278
7370
|
function git(args) {
|
|
7279
7371
|
try {
|
|
7280
|
-
return
|
|
7372
|
+
return execFileSync9("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
7281
7373
|
} catch {
|
|
7282
7374
|
return "";
|
|
7283
7375
|
}
|
|
@@ -7304,7 +7396,7 @@ async function resolveToken() {
|
|
|
7304
7396
|
if (process.env.GH_TOKEN)
|
|
7305
7397
|
return process.env.GH_TOKEN;
|
|
7306
7398
|
try {
|
|
7307
|
-
return
|
|
7399
|
+
return execFileSync9("gh", ["auth", "token"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
|
|
7308
7400
|
} catch {
|
|
7309
7401
|
return null;
|
|
7310
7402
|
}
|
|
@@ -7465,7 +7557,7 @@ ${result.missing.map((m) => ` - ${m}`).join(`
|
|
|
7465
7557
|
}
|
|
7466
7558
|
|
|
7467
7559
|
// src/commands/check.ts
|
|
7468
|
-
import { execFileSync as
|
|
7560
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
7469
7561
|
import { dirname as dirname5 } from "node:path";
|
|
7470
7562
|
|
|
7471
7563
|
// src/checks/contract.ts
|
|
@@ -7870,7 +7962,7 @@ var REGISTRY = [
|
|
|
7870
7962
|
run: bin("check-main-branch-refusal"),
|
|
7871
7963
|
scope: "full",
|
|
7872
7964
|
timeoutMs: 15000,
|
|
7873
|
-
env: {}
|
|
7965
|
+
env: { VINAYA_PUSH_REFS: { optional: true } }
|
|
7874
7966
|
},
|
|
7875
7967
|
0
|
|
7876
7968
|
],
|
|
@@ -8418,7 +8510,7 @@ async function buildRolePlan(configDir, configRoles) {
|
|
|
8418
8510
|
// src/commands/check.ts
|
|
8419
8511
|
function git2(args) {
|
|
8420
8512
|
try {
|
|
8421
|
-
return
|
|
8513
|
+
return execFileSync10("git", args, { encoding: "utf8" }).trim();
|
|
8422
8514
|
} catch {
|
|
8423
8515
|
return "";
|
|
8424
8516
|
}
|
|
@@ -8682,14 +8774,14 @@ function commitMsgCommand(args) {
|
|
|
8682
8774
|
}
|
|
8683
8775
|
|
|
8684
8776
|
// src/commands/demo.ts
|
|
8685
|
-
import { execFileSync as
|
|
8777
|
+
import { execFileSync as execFileSync11, spawnSync } from "node:child_process";
|
|
8686
8778
|
import { existsSync as existsSync10, readFileSync as readFileSync12, rmSync as rmSync2, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8687
8779
|
import { isAbsolute as isAbsolute2, join as join12 } from "node:path";
|
|
8688
8780
|
var DEMO_BRANCH_PREFIX = "vinaya/demo-break-";
|
|
8689
8781
|
var FIXTURE_PATH = ".vinaya-demo-brief.md";
|
|
8690
8782
|
var STATE_FILENAME = "vinaya-demo-state.json";
|
|
8691
8783
|
function gitCapture(repoRoot, args) {
|
|
8692
|
-
return
|
|
8784
|
+
return execFileSync11("git", args, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
8693
8785
|
}
|
|
8694
8786
|
function currentBranchName(repoRoot) {
|
|
8695
8787
|
try {
|
|
@@ -8700,7 +8792,7 @@ function currentBranchName(repoRoot) {
|
|
|
8700
8792
|
}
|
|
8701
8793
|
function branchExists(repoRoot, name) {
|
|
8702
8794
|
try {
|
|
8703
|
-
|
|
8795
|
+
execFileSync11("git", ["rev-parse", "--verify", "--quiet", `refs/heads/${name}`], {
|
|
8704
8796
|
cwd: repoRoot,
|
|
8705
8797
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8706
8798
|
});
|
|
@@ -8902,7 +8994,7 @@ async function demoBreakCommand(args) {
|
|
|
8902
8994
|
}
|
|
8903
8995
|
|
|
8904
8996
|
// src/commands/doctor.ts
|
|
8905
|
-
import { execFileSync as
|
|
8997
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
8906
8998
|
import { existsSync as existsSync13, readdirSync as readdirSync7, readFileSync as readFileSync16, statSync as statSync2 } from "node:fs";
|
|
8907
8999
|
import { delimiter, join as join15 } from "node:path";
|
|
8908
9000
|
|
|
@@ -9328,7 +9420,7 @@ function diagnoseCustomChecks(repoRoot, config) {
|
|
|
9328
9420
|
}
|
|
9329
9421
|
function listTrackedFiles(repoRoot) {
|
|
9330
9422
|
try {
|
|
9331
|
-
return
|
|
9423
|
+
return execFileSync12("git", ["ls-files"], { cwd: repoRoot, encoding: "utf-8" }).split(`
|
|
9332
9424
|
`).map((s) => s.trim()).filter(Boolean);
|
|
9333
9425
|
} catch {
|
|
9334
9426
|
return [];
|
|
@@ -9593,10 +9685,15 @@ function symbolFor(severity) {
|
|
|
9593
9685
|
return "✗";
|
|
9594
9686
|
}
|
|
9595
9687
|
}
|
|
9596
|
-
function printReport(findings, healthy) {
|
|
9688
|
+
function printReport(findings, healthy, doctrineInfo) {
|
|
9597
9689
|
process.stdout.write(`vinaya doctor
|
|
9598
9690
|
|
|
9599
9691
|
`);
|
|
9692
|
+
if (doctrineInfo) {
|
|
9693
|
+
process.stdout.write(`doctrine: ${doctrineInfo.root} (${doctrineInfo.source})
|
|
9694
|
+
|
|
9695
|
+
`);
|
|
9696
|
+
}
|
|
9600
9697
|
for (const f of findings) {
|
|
9601
9698
|
process.stdout.write(`${symbolFor(f.severity)} [${f.check}] ${f.message}
|
|
9602
9699
|
`);
|
|
@@ -9612,6 +9709,7 @@ async function runDoctor(args, deps) {
|
|
|
9612
9709
|
console.error("Error: not a git repository. Run `vinaya doctor` from inside your repo.");
|
|
9613
9710
|
return 1;
|
|
9614
9711
|
}
|
|
9712
|
+
const doctrineInfo = resolveDoctrineRootInfo(undefined, repo.repoRoot);
|
|
9615
9713
|
const configRead = readConfig(repo.repoRoot);
|
|
9616
9714
|
const findings = [];
|
|
9617
9715
|
let hasDrift = false;
|
|
@@ -9652,9 +9750,9 @@ async function runDoctor(args, deps) {
|
|
|
9652
9750
|
findings.push(...diagnoseTestCi(repo.repoRoot));
|
|
9653
9751
|
const healthy = findings.every((f) => f.severity === "ok" || f.severity === "info");
|
|
9654
9752
|
if (jsonOutput) {
|
|
9655
|
-
printJson({ healthy, findings });
|
|
9753
|
+
printJson({ healthy, findings, doctrineInfo });
|
|
9656
9754
|
} else {
|
|
9657
|
-
printReport(findings, healthy);
|
|
9755
|
+
printReport(findings, healthy, doctrineInfo);
|
|
9658
9756
|
}
|
|
9659
9757
|
return healthy ? 0 : 1;
|
|
9660
9758
|
}
|
|
@@ -9978,10 +10076,10 @@ async function initProductCommand(args) {
|
|
|
9978
10076
|
}
|
|
9979
10077
|
|
|
9980
10078
|
// src/commands/issue.ts
|
|
9981
|
-
import { execFileSync as
|
|
10079
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
9982
10080
|
|
|
9983
10081
|
// src/lib/forge-write.ts
|
|
9984
|
-
import { execFileSync as
|
|
10082
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
9985
10083
|
import { existsSync as existsSync16, mkdtempSync, readdirSync as readdirSync8, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
|
|
9986
10084
|
import { readFileSync as readFileSync19 } from "node:fs";
|
|
9987
10085
|
import { tmpdir } from "node:os";
|
|
@@ -10075,7 +10173,7 @@ function ensureTrancheLabelExists(slug) {
|
|
|
10075
10173
|
const name = trancheLabel(slug);
|
|
10076
10174
|
let existing;
|
|
10077
10175
|
try {
|
|
10078
|
-
const out =
|
|
10176
|
+
const out = execFileSync13("gh", ["label", "list", "--json", "name", "--limit", "200"], {
|
|
10079
10177
|
encoding: "utf8",
|
|
10080
10178
|
stdio: ["ignore", "pipe", "pipe"]
|
|
10081
10179
|
});
|
|
@@ -10088,7 +10186,7 @@ function ensureTrancheLabelExists(slug) {
|
|
|
10088
10186
|
if (existing.some((l) => l.name === name))
|
|
10089
10187
|
return;
|
|
10090
10188
|
try {
|
|
10091
|
-
|
|
10189
|
+
execFileSync13("gh", ["label", "create", name, "--color", TRANCHE_LABEL_COLOR, "--description", `Tranche: ${slug}`], {
|
|
10092
10190
|
encoding: "utf8",
|
|
10093
10191
|
stdio: ["ignore", "pipe", "pipe"]
|
|
10094
10192
|
});
|
|
@@ -10240,7 +10338,7 @@ function validateForgeWrite(input) {
|
|
|
10240
10338
|
}
|
|
10241
10339
|
function git3(args) {
|
|
10242
10340
|
try {
|
|
10243
|
-
return
|
|
10341
|
+
return execFileSync13("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
10244
10342
|
} catch {
|
|
10245
10343
|
return "";
|
|
10246
10344
|
}
|
|
@@ -10335,7 +10433,7 @@ function locateBodyOrRefuse(ghArgs, retryCommand) {
|
|
|
10335
10433
|
function fetchForgeLabels(issueRef) {
|
|
10336
10434
|
let out;
|
|
10337
10435
|
try {
|
|
10338
|
-
out =
|
|
10436
|
+
out = execFileSync14("gh", ["issue", "view", issueRef, "--json", "labels"], {
|
|
10339
10437
|
encoding: "utf8",
|
|
10340
10438
|
stdio: ["ignore", "pipe", "pipe"]
|
|
10341
10439
|
});
|
|
@@ -10363,7 +10461,7 @@ function reportPass(json, command) {
|
|
|
10363
10461
|
function runGhWrite(ghCmd, ghArgs, bodyResult, json) {
|
|
10364
10462
|
const { finalArgs, cleanup: cleanup2 } = resolveShippableArgs(ghArgs, bodyResult);
|
|
10365
10463
|
try {
|
|
10366
|
-
const out =
|
|
10464
|
+
const out = execFileSync14("gh", [...ghCmd, ...finalArgs], { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
|
|
10367
10465
|
const url = out.trim();
|
|
10368
10466
|
if (json)
|
|
10369
10467
|
printJson({ validated: true, written: true, url });
|
|
@@ -10449,13 +10547,13 @@ function issueEditCommand(args) {
|
|
|
10449
10547
|
}
|
|
10450
10548
|
|
|
10451
10549
|
// src/commands/milestone.ts
|
|
10452
|
-
import { execFileSync as
|
|
10550
|
+
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
10453
10551
|
var RETRY_CREATE2 = "vinaya milestone create --title <title> --body-file <path>";
|
|
10454
10552
|
var RETRY_ADOPT = "vinaya milestone adopt --target <title> --slug <slug> [--slug <slug> ...]";
|
|
10455
10553
|
var RETRY_EDIT2 = "vinaya milestone edit <n> --body-file <path>";
|
|
10456
10554
|
var RETRY_CLOSE = "vinaya milestone close --slug <slug>";
|
|
10457
10555
|
function sh3(args, input) {
|
|
10458
|
-
return
|
|
10556
|
+
return execFileSync15(args[0], args.slice(1), {
|
|
10459
10557
|
encoding: "utf8",
|
|
10460
10558
|
input,
|
|
10461
10559
|
env: process.env,
|
|
@@ -10955,7 +11053,7 @@ ${registration}
|
|
|
10955
11053
|
}
|
|
10956
11054
|
|
|
10957
11055
|
// src/commands/pr.ts
|
|
10958
|
-
import { execFileSync as
|
|
11056
|
+
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
10959
11057
|
import { rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
|
|
10960
11058
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
10961
11059
|
import { join as join22 } from "node:path";
|
|
@@ -11250,7 +11348,7 @@ var RETRY_CREATE3 = "vinaya pr create --validate-only …";
|
|
|
11250
11348
|
var RETRY_EDIT3 = "vinaya pr edit <n> --validate-only …";
|
|
11251
11349
|
function git4(args) {
|
|
11252
11350
|
try {
|
|
11253
|
-
return
|
|
11351
|
+
return execFileSync16("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
11254
11352
|
} catch {
|
|
11255
11353
|
return "";
|
|
11256
11354
|
}
|
|
@@ -11284,7 +11382,7 @@ function reportPass2(json, command) {
|
|
|
11284
11382
|
function runGhWrite2(ghCmd, ghArgs, bodyResult, json) {
|
|
11285
11383
|
const { finalArgs, cleanup: cleanup2 } = resolveShippableArgs(ghArgs, bodyResult);
|
|
11286
11384
|
try {
|
|
11287
|
-
const out =
|
|
11385
|
+
const out = execFileSync16("gh", [...ghCmd, ...finalArgs], { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
|
|
11288
11386
|
const url = out.trim();
|
|
11289
11387
|
if (json)
|
|
11290
11388
|
printJson({ validated: true, written: true, url });
|
|
@@ -11299,7 +11397,7 @@ function runGhWrite2(ghCmd, ghArgs, bodyResult, json) {
|
|
|
11299
11397
|
function fetchPrForgeContext(prRef) {
|
|
11300
11398
|
let viewOut;
|
|
11301
11399
|
try {
|
|
11302
|
-
viewOut =
|
|
11400
|
+
viewOut = execFileSync16("gh", ["pr", "view", prRef, "--json", "headRefName,files"], {
|
|
11303
11401
|
encoding: "utf8",
|
|
11304
11402
|
stdio: ["ignore", "pipe", "pipe"]
|
|
11305
11403
|
});
|
|
@@ -11353,7 +11451,7 @@ ${brief}
|
|
|
11353
11451
|
const tmp = join22(tmpdir2(), `vinaya-pr-create-brief-${process.pid}-${Date.now()}.md`);
|
|
11354
11452
|
writeFileSync11(tmp, commentBody);
|
|
11355
11453
|
try {
|
|
11356
|
-
|
|
11454
|
+
execFileSync16("gh", ["pr", "comment", prNumber, "--body-file", tmp], {
|
|
11357
11455
|
encoding: "utf8",
|
|
11358
11456
|
stdio: ["ignore", "pipe", "pipe"]
|
|
11359
11457
|
});
|
|
@@ -11476,7 +11574,7 @@ function prEditCommand(args) {
|
|
|
11476
11574
|
}
|
|
11477
11575
|
|
|
11478
11576
|
// src/commands/pr-report.ts
|
|
11479
|
-
import { execFileSync as
|
|
11577
|
+
import { execFileSync as execFileSync17, spawnSync as spawnSync2 } from "node:child_process";
|
|
11480
11578
|
import { existsSync as existsSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync23, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
|
|
11481
11579
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
11482
11580
|
import { join as join23 } from "node:path";
|
|
@@ -11484,14 +11582,14 @@ var EVIDENCE_START = "<!-- AEG:EVIDENCE:START -->";
|
|
|
11484
11582
|
var EVIDENCE_END = "<!-- AEG:EVIDENCE:END -->";
|
|
11485
11583
|
function git5(args) {
|
|
11486
11584
|
try {
|
|
11487
|
-
return
|
|
11585
|
+
return execFileSync17("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
11488
11586
|
} catch {
|
|
11489
11587
|
return "";
|
|
11490
11588
|
}
|
|
11491
11589
|
}
|
|
11492
11590
|
function gh(args) {
|
|
11493
11591
|
try {
|
|
11494
|
-
return
|
|
11592
|
+
return execFileSync17("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
11495
11593
|
} catch (err) {
|
|
11496
11594
|
const stderr = err.stderr;
|
|
11497
11595
|
throw new Error(String(stderr ?? err.message).trim() || "gh command failed");
|
|
@@ -11510,7 +11608,7 @@ class GitCommandError extends Error {
|
|
|
11510
11608
|
}
|
|
11511
11609
|
function gitStrict(args) {
|
|
11512
11610
|
try {
|
|
11513
|
-
return
|
|
11611
|
+
return execFileSync17("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
11514
11612
|
} catch (err) {
|
|
11515
11613
|
const stderr = err.stderr;
|
|
11516
11614
|
throw new GitCommandError(args, String(stderr ?? err.message).trim() || "non-zero exit");
|
|
@@ -11862,16 +11960,17 @@ async function buildReport(opts = {}) {
|
|
|
11862
11960
|
const groupA = opts.groupA ?? computeGroupA();
|
|
11863
11961
|
const gateRunner = opts.gateRunner ?? runRealGates;
|
|
11864
11962
|
const gateResult = await gateRunner();
|
|
11963
|
+
const outcomes = gateResult.outcomes.filter((o) => o.name !== "evidence-fresh");
|
|
11865
11964
|
const groupC = opts.groupC ?? computeGroupC(opts.body ?? process.env.PR_BODY ?? "");
|
|
11866
|
-
const blockInner = buildBlockInner(groupA,
|
|
11965
|
+
const blockInner = buildBlockInner(groupA, outcomes, groupC);
|
|
11867
11966
|
const block = `${EVIDENCE_START}
|
|
11868
11967
|
${blockInner}
|
|
11869
11968
|
${EVIDENCE_END}`;
|
|
11870
11969
|
return {
|
|
11871
11970
|
block,
|
|
11872
11971
|
blockInner,
|
|
11873
|
-
gatesFailed:
|
|
11874
|
-
gateOutcomes:
|
|
11972
|
+
gatesFailed: anyGateFailed(outcomes) || groupCFailed(groupC),
|
|
11973
|
+
gateOutcomes: outcomes,
|
|
11875
11974
|
groupC
|
|
11876
11975
|
};
|
|
11877
11976
|
}
|
|
@@ -12028,7 +12127,7 @@ The AEG:EVIDENCE block was still written to ${writePath}.`);
|
|
|
12028
12127
|
}
|
|
12029
12128
|
|
|
12030
12129
|
// src/commands/pr-verify-evidence.ts
|
|
12031
|
-
import { execFileSync as
|
|
12130
|
+
import { execFileSync as execFileSync18 } from "node:child_process";
|
|
12032
12131
|
|
|
12033
12132
|
// src/commands/pr-verify-evidence-logic.ts
|
|
12034
12133
|
function publishedMergeBase(region) {
|
|
@@ -12115,10 +12214,10 @@ function renderVerdict(verdict) {
|
|
|
12115
12214
|
|
|
12116
12215
|
// src/commands/pr-verify-evidence.ts
|
|
12117
12216
|
function gh2(args) {
|
|
12118
|
-
return
|
|
12217
|
+
return execFileSync18("gh", args, { encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
12119
12218
|
}
|
|
12120
12219
|
function assertCleanWorktree() {
|
|
12121
|
-
const dirty =
|
|
12220
|
+
const dirty = execFileSync18("git", ["status", "--porcelain"], {
|
|
12122
12221
|
encoding: "utf8",
|
|
12123
12222
|
cwd: repoRoot2()
|
|
12124
12223
|
}).trim();
|
|
@@ -12155,7 +12254,7 @@ function assertRepoRootCwd() {
|
|
|
12155
12254
|
process.exit(2);
|
|
12156
12255
|
}
|
|
12157
12256
|
function repoRoot2() {
|
|
12158
|
-
return
|
|
12257
|
+
return execFileSync18("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
12159
12258
|
}
|
|
12160
12259
|
async function prVerifyEvidenceCommand(args) {
|
|
12161
12260
|
const prRef = args[0] && !args[0].startsWith("-") ? args[0] : undefined;
|
|
@@ -12179,7 +12278,7 @@ async function prVerifyEvidenceCommand(args) {
|
|
|
12179
12278
|
process.exit(1);
|
|
12180
12279
|
return;
|
|
12181
12280
|
}
|
|
12182
|
-
const localHead =
|
|
12281
|
+
const localHead = execFileSync18("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
12183
12282
|
if (!forgeHead) {
|
|
12184
12283
|
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
12284
|
`);
|
|
@@ -12202,7 +12301,7 @@ async function prVerifyEvidenceCommand(args) {
|
|
|
12202
12301
|
}
|
|
12203
12302
|
|
|
12204
12303
|
// src/commands/quickstart.ts
|
|
12205
|
-
import { execFileSync as
|
|
12304
|
+
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
12206
12305
|
import { existsSync as existsSync22, readFileSync as readFileSync25 } from "node:fs";
|
|
12207
12306
|
import { join as join25 } from "node:path";
|
|
12208
12307
|
|
|
@@ -12402,7 +12501,7 @@ function realDeps7() {
|
|
|
12402
12501
|
};
|
|
12403
12502
|
}
|
|
12404
12503
|
function execGit(repoRoot3, args) {
|
|
12405
|
-
return
|
|
12504
|
+
return execFileSync19("git", args, { cwd: repoRoot3, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
12406
12505
|
}
|
|
12407
12506
|
function errorDetail(err) {
|
|
12408
12507
|
const stderr = err.stderr;
|
|
@@ -12535,8 +12634,212 @@ async function quickstartCommand(args) {
|
|
|
12535
12634
|
process.exit(await runQuickstart(args, realDeps7()));
|
|
12536
12635
|
}
|
|
12537
12636
|
|
|
12637
|
+
// src/commands/release.ts
|
|
12638
|
+
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
12639
|
+
function realDeps8() {
|
|
12640
|
+
const git6 = (args) => {
|
|
12641
|
+
try {
|
|
12642
|
+
return execFileSync20("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
12643
|
+
} catch {
|
|
12644
|
+
return null;
|
|
12645
|
+
}
|
|
12646
|
+
};
|
|
12647
|
+
return {
|
|
12648
|
+
currentBranch() {
|
|
12649
|
+
const branch = git6(["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
12650
|
+
return branch === null || branch === "" ? null : branch;
|
|
12651
|
+
},
|
|
12652
|
+
defaultBranch() {
|
|
12653
|
+
const ref = git6(["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"]);
|
|
12654
|
+
if (ref === null || !ref.startsWith("origin/"))
|
|
12655
|
+
return null;
|
|
12656
|
+
const branch = ref.slice("origin/".length);
|
|
12657
|
+
return branch === "" ? null : branch;
|
|
12658
|
+
},
|
|
12659
|
+
fetchOrigin() {
|
|
12660
|
+
try {
|
|
12661
|
+
execFileSync20("git", ["fetch", "origin"], { stdio: "inherit" });
|
|
12662
|
+
return true;
|
|
12663
|
+
} catch {
|
|
12664
|
+
return false;
|
|
12665
|
+
}
|
|
12666
|
+
},
|
|
12667
|
+
treeIsClean() {
|
|
12668
|
+
const status = git6(["status", "--porcelain"]);
|
|
12669
|
+
return status === "";
|
|
12670
|
+
},
|
|
12671
|
+
headSha() {
|
|
12672
|
+
return git6(["rev-parse", "HEAD"]);
|
|
12673
|
+
},
|
|
12674
|
+
defaultBranchSha(defaultBranch) {
|
|
12675
|
+
return git6(["rev-parse", `origin/${defaultBranch}`]);
|
|
12676
|
+
},
|
|
12677
|
+
headSubject() {
|
|
12678
|
+
return git6(["log", "-1", "--format=%s"]) ?? "";
|
|
12679
|
+
},
|
|
12680
|
+
npmWhoami() {
|
|
12681
|
+
try {
|
|
12682
|
+
execFileSync20("npm", ["whoami"], { stdio: "ignore" });
|
|
12683
|
+
return true;
|
|
12684
|
+
} catch {
|
|
12685
|
+
return false;
|
|
12686
|
+
}
|
|
12687
|
+
},
|
|
12688
|
+
runStreamed(cmd, args) {
|
|
12689
|
+
execFileSync20(cmd, args, { stdio: "inherit" });
|
|
12690
|
+
},
|
|
12691
|
+
tagsAtHead() {
|
|
12692
|
+
const tags = git6(["tag", "--points-at", "HEAD"]);
|
|
12693
|
+
if (tags === null || tags === "")
|
|
12694
|
+
return [];
|
|
12695
|
+
return tags.split(`
|
|
12696
|
+
`).filter((t) => t.length > 0);
|
|
12697
|
+
},
|
|
12698
|
+
npmViewVersion(pkg) {
|
|
12699
|
+
try {
|
|
12700
|
+
const out = execFileSync20("npm", ["view", pkg, "version"], {
|
|
12701
|
+
encoding: "utf8",
|
|
12702
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
12703
|
+
}).trim();
|
|
12704
|
+
return out === "" ? null : out;
|
|
12705
|
+
} catch {
|
|
12706
|
+
return null;
|
|
12707
|
+
}
|
|
12708
|
+
},
|
|
12709
|
+
log(message) {
|
|
12710
|
+
process.stdout.write(`${message}
|
|
12711
|
+
`);
|
|
12712
|
+
}
|
|
12713
|
+
};
|
|
12714
|
+
}
|
|
12715
|
+
var RELEASE_PLAN = [
|
|
12716
|
+
["bun", "install", "--frozen-lockfile"],
|
|
12717
|
+
["bun", "run", "build"],
|
|
12718
|
+
["bun", "run", "changeset:publish"],
|
|
12719
|
+
["git", "push", "origin", "--tags"]
|
|
12720
|
+
];
|
|
12721
|
+
function parseTag(tag) {
|
|
12722
|
+
const at = tag.lastIndexOf("@");
|
|
12723
|
+
if (at <= 0)
|
|
12724
|
+
return null;
|
|
12725
|
+
return { pkg: tag.slice(0, at), version: tag.slice(at + 1) };
|
|
12726
|
+
}
|
|
12727
|
+
function runRelease(opts, deps) {
|
|
12728
|
+
const current = deps.currentBranch();
|
|
12729
|
+
const defaultBranch = deps.defaultBranch();
|
|
12730
|
+
if (defaultBranch === null) {
|
|
12731
|
+
return {
|
|
12732
|
+
ok: false,
|
|
12733
|
+
message: "vinaya release: could not determine this repo's default branch (no resolvable `origin/HEAD`) — run `git remote set-head origin -a` first."
|
|
12734
|
+
};
|
|
12735
|
+
}
|
|
12736
|
+
if (current !== defaultBranch) {
|
|
12737
|
+
return {
|
|
12738
|
+
ok: false,
|
|
12739
|
+
message: `vinaya release: HEAD is on \`${current ?? "(detached)"}\`, not the default branch \`${defaultBranch}\` — check out \`${defaultBranch}\` first.`
|
|
12740
|
+
};
|
|
12741
|
+
}
|
|
12742
|
+
if (!deps.treeIsClean()) {
|
|
12743
|
+
return {
|
|
12744
|
+
ok: false,
|
|
12745
|
+
message: "vinaya release: working tree is dirty — commit or stash your changes first."
|
|
12746
|
+
};
|
|
12747
|
+
}
|
|
12748
|
+
if (!deps.fetchOrigin()) {
|
|
12749
|
+
return {
|
|
12750
|
+
ok: false,
|
|
12751
|
+
message: "vinaya release: `git fetch origin` failed — check your network/auth and try again."
|
|
12752
|
+
};
|
|
12753
|
+
}
|
|
12754
|
+
const head = deps.headSha();
|
|
12755
|
+
const originSha = deps.defaultBranchSha(defaultBranch);
|
|
12756
|
+
if (head === null || originSha === null || head !== originSha) {
|
|
12757
|
+
return {
|
|
12758
|
+
ok: false,
|
|
12759
|
+
message: `vinaya release: HEAD does not equal \`origin/${defaultBranch}\` — run \`git pull --ff-only\` first.`
|
|
12760
|
+
};
|
|
12761
|
+
}
|
|
12762
|
+
const subject = deps.headSubject();
|
|
12763
|
+
if (!opts.allowAnyCommit && !subject.startsWith("Chore(release): Version packages")) {
|
|
12764
|
+
return {
|
|
12765
|
+
ok: false,
|
|
12766
|
+
message: `vinaya release: HEAD's commit "${subject}" is not a Version Packages commit — merge the Version Packages PR first, or pass \`--allow-any-commit\`.`
|
|
12767
|
+
};
|
|
12768
|
+
}
|
|
12769
|
+
if (!deps.npmWhoami()) {
|
|
12770
|
+
return {
|
|
12771
|
+
ok: false,
|
|
12772
|
+
message: "vinaya release: `npm whoami` failed — run `npm login` first."
|
|
12773
|
+
};
|
|
12774
|
+
}
|
|
12775
|
+
if (opts.dryRun) {
|
|
12776
|
+
return { ok: true, dryRun: true, plan: RELEASE_PLAN };
|
|
12777
|
+
}
|
|
12778
|
+
const ranSteps = [];
|
|
12779
|
+
let publishedAlready = false;
|
|
12780
|
+
for (const step of RELEASE_PLAN) {
|
|
12781
|
+
const [cmd, ...args] = step;
|
|
12782
|
+
deps.log(`vinaya release: running \`${step.join(" ")}\``);
|
|
12783
|
+
try {
|
|
12784
|
+
deps.runStreamed(cmd, args);
|
|
12785
|
+
} catch (error2) {
|
|
12786
|
+
const reason = error2 instanceof Error ? error2.message : String(error2);
|
|
12787
|
+
if (!publishedAlready) {
|
|
12788
|
+
return { ok: false, message: `vinaya release: \`${step.join(" ")}\` failed — ${reason}` };
|
|
12789
|
+
}
|
|
12790
|
+
const recoveryCommand = RELEASE_PLAN.slice(ranSteps.length).map((s) => s.join(" ")).join(" && ");
|
|
12791
|
+
return {
|
|
12792
|
+
ok: false,
|
|
12793
|
+
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}\``,
|
|
12794
|
+
ranSteps: [...ranSteps],
|
|
12795
|
+
failedStep: step,
|
|
12796
|
+
recoveryCommand
|
|
12797
|
+
};
|
|
12798
|
+
}
|
|
12799
|
+
deps.log(`vinaya release: \`${step.join(" ")}\` done`);
|
|
12800
|
+
ranSteps.push(step);
|
|
12801
|
+
if (cmd === "bun" && args.join(" ") === "run changeset:publish")
|
|
12802
|
+
publishedAlready = true;
|
|
12803
|
+
}
|
|
12804
|
+
const published = [];
|
|
12805
|
+
for (const tag of deps.tagsAtHead()) {
|
|
12806
|
+
const parsed = parseTag(tag);
|
|
12807
|
+
if (parsed === null)
|
|
12808
|
+
continue;
|
|
12809
|
+
const registryVersion = deps.npmViewVersion(parsed.pkg);
|
|
12810
|
+
const lagExpected = parsed.pkg === "@attalabs/vinaya" && registryVersion !== parsed.version;
|
|
12811
|
+
published.push({ tag, pkg: parsed.pkg, version: parsed.version, registryVersion, lagExpected });
|
|
12812
|
+
}
|
|
12813
|
+
return { ok: true, dryRun: false, published };
|
|
12814
|
+
}
|
|
12815
|
+
async function releaseCommand(args) {
|
|
12816
|
+
const dryRun = args.includes("--dry-run");
|
|
12817
|
+
const allowAnyCommit = args.includes("--allow-any-commit");
|
|
12818
|
+
const deps = realDeps8();
|
|
12819
|
+
const outcome = runRelease({ dryRun, allowAnyCommit }, deps);
|
|
12820
|
+
if (!outcome.ok) {
|
|
12821
|
+
console.error(outcome.message);
|
|
12822
|
+
process.exit(1);
|
|
12823
|
+
}
|
|
12824
|
+
if (outcome.dryRun) {
|
|
12825
|
+
deps.log("vinaya release --dry-run: preconditions pass. Plan:");
|
|
12826
|
+
for (const step of outcome.plan)
|
|
12827
|
+
deps.log(` ${step.join(" ")}`);
|
|
12828
|
+
process.exit(0);
|
|
12829
|
+
}
|
|
12830
|
+
for (const p of outcome.published) {
|
|
12831
|
+
const registry = p.registryVersion ?? "(not found)";
|
|
12832
|
+
if (p.lagExpected) {
|
|
12833
|
+
deps.log(`${p.tag}: npm view reports \`${registry}\` — registry lag expected (~20 min)`);
|
|
12834
|
+
} else {
|
|
12835
|
+
deps.log(`${p.tag}: npm view reports \`${registry}\``);
|
|
12836
|
+
}
|
|
12837
|
+
}
|
|
12838
|
+
process.exit(0);
|
|
12839
|
+
}
|
|
12840
|
+
|
|
12538
12841
|
// src/commands/review-post.ts
|
|
12539
|
-
import { execFileSync as
|
|
12842
|
+
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
12540
12843
|
import { readFileSync as readFileSync26, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
|
|
12541
12844
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
12542
12845
|
import { join as join26 } from "node:path";
|
|
@@ -12925,7 +13228,7 @@ function normalizeSecurityVerdict(raw) {
|
|
|
12925
13228
|
}
|
|
12926
13229
|
function gh3(args) {
|
|
12927
13230
|
try {
|
|
12928
|
-
return
|
|
13231
|
+
return execFileSync21("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
12929
13232
|
} catch (err) {
|
|
12930
13233
|
const stderr = err.stderr;
|
|
12931
13234
|
throw new Error(String(stderr ?? err.message).trim() || "gh command failed");
|
|
@@ -12945,7 +13248,7 @@ function resolveHeadRefName(pr) {
|
|
|
12945
13248
|
}
|
|
12946
13249
|
function shaFromLsRemote(branch) {
|
|
12947
13250
|
try {
|
|
12948
|
-
const out =
|
|
13251
|
+
const out = execFileSync21("git", ["ls-remote", "origin", `refs/heads/${branch}`], {
|
|
12949
13252
|
encoding: "utf8",
|
|
12950
13253
|
stdio: ["ignore", "pipe", "pipe"]
|
|
12951
13254
|
}).trim();
|
|
@@ -13007,13 +13310,13 @@ function fetchComments(pr) {
|
|
|
13007
13310
|
}
|
|
13008
13311
|
function computeChangedRanges(pr, judgedHead, resolvedHead) {
|
|
13009
13312
|
try {
|
|
13010
|
-
|
|
13313
|
+
execFileSync21("git", ["fetch", "origin", resolvedHead], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
13011
13314
|
} catch (err) {
|
|
13012
13315
|
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
13316
|
}
|
|
13014
13317
|
let out;
|
|
13015
13318
|
try {
|
|
13016
|
-
out =
|
|
13319
|
+
out = execFileSync21("git", ["diff", `${judgedHead}...${resolvedHead}`, "-U0"], {
|
|
13017
13320
|
encoding: "utf8",
|
|
13018
13321
|
stdio: ["ignore", "pipe", "pipe"]
|
|
13019
13322
|
});
|
|
@@ -13312,11 +13615,11 @@ Self-verification: clean — re-parsed VERDICT is bound to head ${headSha}.
|
|
|
13312
13615
|
}
|
|
13313
13616
|
|
|
13314
13617
|
// src/commands/review-status.ts
|
|
13315
|
-
import { execFileSync as
|
|
13618
|
+
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
13316
13619
|
var MAX_ROUNDS = 3;
|
|
13317
13620
|
function fetchPr(prNumber) {
|
|
13318
13621
|
try {
|
|
13319
|
-
const out =
|
|
13622
|
+
const out = execFileSync22("gh", ["pr", "view", prNumber, "--json", "comments,headRefOid,baseRefName"], {
|
|
13320
13623
|
encoding: "utf8",
|
|
13321
13624
|
stdio: ["ignore", "pipe", "pipe"]
|
|
13322
13625
|
});
|
|
@@ -13327,7 +13630,7 @@ function fetchPr(prNumber) {
|
|
|
13327
13630
|
}
|
|
13328
13631
|
function behindBy(base) {
|
|
13329
13632
|
try {
|
|
13330
|
-
const out =
|
|
13633
|
+
const out = execFileSync22("git", ["rev-list", "--count", `HEAD..origin/${base}`], {
|
|
13331
13634
|
encoding: "utf8",
|
|
13332
13635
|
stdio: ["ignore", "pipe", "pipe"]
|
|
13333
13636
|
}).trim();
|
|
@@ -13526,7 +13829,7 @@ async function runStudio(cwd, args, moduleUrl = import.meta.url) {
|
|
|
13526
13829
|
// src/commands/upgrade.ts
|
|
13527
13830
|
import { existsSync as existsSync24, readFileSync as readFileSync28, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "node:fs";
|
|
13528
13831
|
import { join as join28 } from "node:path";
|
|
13529
|
-
function
|
|
13832
|
+
function realDeps9() {
|
|
13530
13833
|
return {
|
|
13531
13834
|
detectRepo: detectGitRepo,
|
|
13532
13835
|
hookDirFor: resolveHookDir,
|
|
@@ -13921,16 +14224,16 @@ Vinaya upgraded.
|
|
|
13921
14224
|
return 0;
|
|
13922
14225
|
}
|
|
13923
14226
|
async function upgradeCommand(args) {
|
|
13924
|
-
process.exit(await runUpgrade(args,
|
|
14227
|
+
process.exit(await runUpgrade(args, realDeps9()));
|
|
13925
14228
|
}
|
|
13926
14229
|
|
|
13927
14230
|
// src/commands/waiver.ts
|
|
13928
|
-
import { execFileSync as
|
|
14231
|
+
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
13929
14232
|
function labelFor(kind) {
|
|
13930
14233
|
return kind === "docs" ? WAIVER_LABEL : WAIVER_LABEL_REVIEW;
|
|
13931
14234
|
}
|
|
13932
14235
|
function gh4(args) {
|
|
13933
|
-
return
|
|
14236
|
+
return execFileSync23("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], env: process.env }).trim();
|
|
13934
14237
|
}
|
|
13935
14238
|
function detectPrNumber() {
|
|
13936
14239
|
try {
|
|
@@ -14030,8 +14333,8 @@ Reason: ${reason}`];
|
|
|
14030
14333
|
}
|
|
14031
14334
|
process.stdout.write(`Applying \`${label2}\` to PR #${prNumber} under your authenticated \`gh\` identity — vinaya never fabricates an actor.
|
|
14032
14335
|
`);
|
|
14033
|
-
|
|
14034
|
-
|
|
14336
|
+
execFileSync23("gh", editArgs, { stdio: "inherit", env: process.env });
|
|
14337
|
+
execFileSync23("gh", commentArgs, { stdio: "inherit", env: process.env });
|
|
14035
14338
|
process.stdout.write(`
|
|
14036
14339
|
✓ Label applied and reason posted as a PR comment.
|
|
14037
14340
|
`);
|
|
@@ -14227,6 +14530,10 @@ try {
|
|
|
14227
14530
|
await waiverCommand(args);
|
|
14228
14531
|
break;
|
|
14229
14532
|
}
|
|
14533
|
+
case "release": {
|
|
14534
|
+
await releaseCommand(args);
|
|
14535
|
+
break;
|
|
14536
|
+
}
|
|
14230
14537
|
case "quickstart": {
|
|
14231
14538
|
await quickstartCommand(args);
|
|
14232
14539
|
break;
|