@h-rig/runtime 0.0.6-alpha.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.
- package/README.md +27 -0
- package/dist/bin/rig-agent-dispatch.js +9615 -0
- package/dist/bin/rig-agent.js +9512 -0
- package/dist/bin/rig-browser-tool.js +269 -0
- package/dist/src/agent-mode.js +48 -0
- package/dist/src/baked-secrets.js +121 -0
- package/dist/src/binary-build-worker.js +312 -0
- package/dist/src/binary-run.js +540 -0
- package/dist/src/boundaries.js +1 -0
- package/dist/src/build-time-config.js +25 -0
- package/dist/src/control-plane/agent-roles.js +27 -0
- package/dist/src/control-plane/agent-wrapper.js +9621 -0
- package/dist/src/control-plane/authority-files.js +582 -0
- package/dist/src/control-plane/browser-contract.js +135 -0
- package/dist/src/control-plane/controlled-bash.js +1111 -0
- package/dist/src/control-plane/errors.js +13 -0
- package/dist/src/control-plane/harness-main.js +10828 -0
- package/dist/src/control-plane/hook-materializer.js +75 -0
- package/dist/src/control-plane/hooks/audit-trail.js +353 -0
- package/dist/src/control-plane/hooks/completion-verification.js +7552 -0
- package/dist/src/control-plane/hooks/import-guard.js +890 -0
- package/dist/src/control-plane/hooks/inject-context.js +4189 -0
- package/dist/src/control-plane/hooks/post-edit-lint.js +43 -0
- package/dist/src/control-plane/hooks/safety-guard.js +910 -0
- package/dist/src/control-plane/hooks/scope-guard.js +907 -0
- package/dist/src/control-plane/hooks/shared.js +44 -0
- package/dist/src/control-plane/hooks/submodule-branch.js +7797 -0
- package/dist/src/control-plane/hooks/task-runtime-start.js +7799 -0
- package/dist/src/control-plane/hooks/test-integrity-guard.js +891 -0
- package/dist/src/control-plane/materialize-task-config.js +453 -0
- package/dist/src/control-plane/memory-sync/cli.js +2019 -0
- package/dist/src/control-plane/memory-sync/db.js +753 -0
- package/dist/src/control-plane/memory-sync/embed.js +281 -0
- package/dist/src/control-plane/memory-sync/index.js +2049 -0
- package/dist/src/control-plane/memory-sync/query.js +294 -0
- package/dist/src/control-plane/memory-sync/read.js +784 -0
- package/dist/src/control-plane/memory-sync/types.js +6 -0
- package/dist/src/control-plane/memory-sync/write.js +1547 -0
- package/dist/src/control-plane/native/git-native.js +490 -0
- package/dist/src/control-plane/native/git-ops.js +2860 -0
- package/dist/src/control-plane/native/harness-cli.js +9721 -0
- package/dist/src/control-plane/native/pr-automation.js +373 -0
- package/dist/src/control-plane/native/profile-ops.js +481 -0
- package/dist/src/control-plane/native/repo-ops.js +2342 -0
- package/dist/src/control-plane/native/root-resolver.js +66 -0
- package/dist/src/control-plane/native/run-ops.js +3281 -0
- package/dist/src/control-plane/native/runtime-native-sidecar.js +299 -0
- package/dist/src/control-plane/native/runtime-native.js +392 -0
- package/dist/src/control-plane/native/scope-rules.js +17 -0
- package/dist/src/control-plane/native/task-ops.js +6320 -0
- package/dist/src/control-plane/native/task-state.js +1512 -0
- package/dist/src/control-plane/native/utils.js +535 -0
- package/dist/src/control-plane/native/validator-binaries.js +889 -0
- package/dist/src/control-plane/native/validator.js +2197 -0
- package/dist/src/control-plane/native/verifier.js +3249 -0
- package/dist/src/control-plane/native/workspace-ops.js +1635 -0
- package/dist/src/control-plane/plugin-host-context.js +334 -0
- package/dist/src/control-plane/project-main-pre-run-sync.js +630 -0
- package/dist/src/control-plane/provider/claude-stream-records.js +158 -0
- package/dist/src/control-plane/provider/codex-app-server.js +885 -0
- package/dist/src/control-plane/provider/codex-exec-records.js +203 -0
- package/dist/src/control-plane/provider/rig-task-run-skill.js +39 -0
- package/dist/src/control-plane/provider/runtime-instructions.js +96 -0
- package/dist/src/control-plane/remote.js +854 -0
- package/dist/src/control-plane/repos/index.js +473 -0
- package/dist/src/control-plane/repos/layout.js +124 -0
- package/dist/src/control-plane/repos/mirror/bootstrap.js +268 -0
- package/dist/src/control-plane/repos/mirror/refresh.js +398 -0
- package/dist/src/control-plane/repos/mirror/state.js +167 -0
- package/dist/src/control-plane/repos/registry.js +77 -0
- package/dist/src/control-plane/repos/types.js +1 -0
- package/dist/src/control-plane/runtime/agent-mode.js +48 -0
- package/dist/src/control-plane/runtime/baked-secrets.js +120 -0
- package/dist/src/control-plane/runtime/claude-tool-router-binary.js +343 -0
- package/dist/src/control-plane/runtime/claude-tool-router.js +520 -0
- package/dist/src/control-plane/runtime/context.js +216 -0
- package/dist/src/control-plane/runtime/events.js +218 -0
- package/dist/src/control-plane/runtime/guard-types.js +6 -0
- package/dist/src/control-plane/runtime/guard.js +880 -0
- package/dist/src/control-plane/runtime/image/fingerprint-sidecar.js +1194 -0
- package/dist/src/control-plane/runtime/image/index.js +2255 -0
- package/dist/src/control-plane/runtime/image-fingerprint-sidecar.js +1191 -0
- package/dist/src/control-plane/runtime/image.js +2255 -0
- package/dist/src/control-plane/runtime/index.js +8511 -0
- package/dist/src/control-plane/runtime/isolation/discovery.js +599 -0
- package/dist/src/control-plane/runtime/isolation/home.js +1217 -0
- package/dist/src/control-plane/runtime/isolation/index.js +8193 -0
- package/dist/src/control-plane/runtime/isolation/runner.js +2651 -0
- package/dist/src/control-plane/runtime/isolation/shared.js +501 -0
- package/dist/src/control-plane/runtime/isolation/toolchain.js +1892 -0
- package/dist/src/control-plane/runtime/isolation/types.js +1 -0
- package/dist/src/control-plane/runtime/isolation/worktree.js +509 -0
- package/dist/src/control-plane/runtime/isolation.js +8193 -0
- package/dist/src/control-plane/runtime/overlay.js +67 -0
- package/dist/src/control-plane/runtime/plugin-mode.js +41 -0
- package/dist/src/control-plane/runtime/plugins.js +1131 -0
- package/dist/src/control-plane/runtime/provisioning-env.js +220 -0
- package/dist/src/control-plane/runtime/queue.js +8358 -0
- package/dist/src/control-plane/runtime/rig-shell.js +205 -0
- package/dist/src/control-plane/runtime/rig-tools.js +182 -0
- package/dist/src/control-plane/runtime/runner-context.js +1 -0
- package/dist/src/control-plane/runtime/runtime-paths.js +184 -0
- package/dist/src/control-plane/runtime/sandbox/backend-bwrap.js +311 -0
- package/dist/src/control-plane/runtime/sandbox/backend-none.js +21 -0
- package/dist/src/control-plane/runtime/sandbox/backend-seatbelt.js +268 -0
- package/dist/src/control-plane/runtime/sandbox/backend.js +1718 -0
- package/dist/src/control-plane/runtime/sandbox/orchestrator.js +1745 -0
- package/dist/src/control-plane/runtime/sandbox/utils.js +137 -0
- package/dist/src/control-plane/runtime/sandbox-backend-bwrap.js +311 -0
- package/dist/src/control-plane/runtime/sandbox-backend-none.js +21 -0
- package/dist/src/control-plane/runtime/sandbox-backend-seatbelt.js +268 -0
- package/dist/src/control-plane/runtime/sandbox-backend.js +1718 -0
- package/dist/src/control-plane/runtime/sandbox-orchestrator.js +1745 -0
- package/dist/src/control-plane/runtime/sandbox-utils.js +137 -0
- package/dist/src/control-plane/runtime/snapshot/index.js +454 -0
- package/dist/src/control-plane/runtime/snapshot/sidecar.js +502 -0
- package/dist/src/control-plane/runtime/snapshot/task-run.js +1578 -0
- package/dist/src/control-plane/runtime/snapshot-sidecar.js +498 -0
- package/dist/src/control-plane/runtime/snapshot.js +454 -0
- package/dist/src/control-plane/runtime/task-run-snapshot.js +1578 -0
- package/dist/src/control-plane/runtime/tool-gateway.js +422 -0
- package/dist/src/control-plane/runtime/tooling/browser-tools.js +32 -0
- package/dist/src/control-plane/runtime/tooling/claude-router-binary.js +343 -0
- package/dist/src/control-plane/runtime/tooling/claude-router.js +524 -0
- package/dist/src/control-plane/runtime/tooling/file-tools.js +182 -0
- package/dist/src/control-plane/runtime/tooling/gateway.js +422 -0
- package/dist/src/control-plane/runtime/tooling/index.js +1290 -0
- package/dist/src/control-plane/runtime/tooling/shell.js +205 -0
- package/dist/src/control-plane/runtime/types.js +1 -0
- package/dist/src/control-plane/setup-version.js +14 -0
- package/dist/src/control-plane/state-sync/index.js +1509 -0
- package/dist/src/control-plane/state-sync/read.js +856 -0
- package/dist/src/control-plane/state-sync/reconcile.js +260 -0
- package/dist/src/control-plane/state-sync/repo.js +302 -0
- package/dist/src/control-plane/state-sync/types.js +111 -0
- package/dist/src/control-plane/state-sync/write.js +1469 -0
- package/dist/src/control-plane/task-fields.js +38 -0
- package/dist/src/control-plane/task-source-bootstrap.js +46 -0
- package/dist/src/control-plane/task-source.js +30 -0
- package/dist/src/control-plane/tasks/legacy-task-config-source.js +130 -0
- package/dist/src/control-plane/tasks/plugin-task-source.js +103 -0
- package/dist/src/control-plane/tasks/source-aware-task-config-source.js +611 -0
- package/dist/src/control-plane/tasks/source-lifecycle.js +1093 -0
- package/dist/src/control-plane/tasks/task-record-reader.js +9 -0
- package/dist/src/control-plane/validators/boundary/public-apis.js +107 -0
- package/dist/src/control-plane/validators/integration/_shared.js +51 -0
- package/dist/src/control-plane/validators/integration/adm-audit-http.js +85 -0
- package/dist/src/control-plane/validators/integration/adm-auth-http.js +78 -0
- package/dist/src/control-plane/validators/integration/adm-issuer-http.js +80 -0
- package/dist/src/control-plane/validators/integration/adm-migration.js +78 -0
- package/dist/src/control-plane/validators/integration/adm-scaffold.js +78 -0
- package/dist/src/control-plane/validators/runtime-registration.js +64 -0
- package/dist/src/control-plane/validators/shared.js +683 -0
- package/dist/src/events.js +218 -0
- package/dist/src/execution.js +35 -0
- package/dist/src/index.js +1633 -0
- package/dist/src/layout.js +145 -0
- package/dist/src/local-server.js +202 -0
- package/dist/src/plugins.js +329 -0
- package/dist/src/remote-http.js +83 -0
- package/dist/src/runtime-context.js +216 -0
- package/dist/src/types.js +1 -0
- package/native/darwin-arm64/bin/rig-git +0 -0
- package/native/darwin-arm64/bin/rig-shell +0 -0
- package/native/darwin-arm64/bin/rig-tools +0 -0
- package/native/darwin-arm64/lib/runtime-native-darwin-arm64.dylib +0 -0
- package/native/darwin-arm64/lib/runtime-native.dylib +0 -0
- package/native/darwin-arm64/manifest.json +1 -0
- package/native/linux-x64/bin/rig-git +0 -0
- package/native/linux-x64/bin/rig-shell +0 -0
- package/native/linux-x64/bin/rig-tools +0 -0
- package/native/linux-x64/lib/runtime-native-linux-x64.so +0 -0
- package/native/linux-x64/lib/runtime-native.so +0 -0
- package/native/linux-x64/manifest.json +1 -0
- package/package.json +74 -0
- package/skills/rig-task-run.md +71 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/runtime/src/control-plane/native/pr-automation.ts
|
|
3
|
+
var UPLOADED_SNAPSHOT_PR_MARKER = "<!-- rig:uploaded-snapshot -->";
|
|
4
|
+
function positiveInt(value, fallback) {
|
|
5
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
6
|
+
}
|
|
7
|
+
function resolvePrAutomationLimits(config) {
|
|
8
|
+
return {
|
|
9
|
+
maxPrFixIterations: positiveInt(config?.automation?.maxPrFixIterations, 30)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function buildPrAutomationBody(input) {
|
|
13
|
+
const lines = [
|
|
14
|
+
input.summary?.trim() || "Rig completed this task autonomously.",
|
|
15
|
+
"",
|
|
16
|
+
`Run: ${input.runId}`
|
|
17
|
+
];
|
|
18
|
+
if (/^\d+$/.test(input.taskId)) {
|
|
19
|
+
lines.push("", `Closes #${input.taskId}`);
|
|
20
|
+
}
|
|
21
|
+
if (input.uploadedSnapshot) {
|
|
22
|
+
lines.push("", UPLOADED_SNAPSHOT_PR_MARKER, "This PR was seeded from an uploaded local working-tree snapshot. Review local snapshot provenance before merging if repository policy requires it.");
|
|
23
|
+
}
|
|
24
|
+
return lines.join(`
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
function wildcardToRegExp(pattern) {
|
|
28
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
29
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
30
|
+
}
|
|
31
|
+
function isAllowedFailure(name, allowedFailures) {
|
|
32
|
+
return allowedFailures.some((pattern) => wildcardToRegExp(pattern).test(name));
|
|
33
|
+
}
|
|
34
|
+
function isPendingCheck(check) {
|
|
35
|
+
const conclusion = String(check.conclusion ?? "").toLowerCase();
|
|
36
|
+
const state = String(check.state ?? check.status ?? "").toLowerCase();
|
|
37
|
+
return ["pending", "queued", "in_progress", "waiting", "requested", "expected", "action_required"].includes(conclusion) || ["pending", "queued", "in_progress", "waiting", "requested", "expected"].includes(state);
|
|
38
|
+
}
|
|
39
|
+
function isPassingCheck(check) {
|
|
40
|
+
const conclusion = String(check.conclusion ?? "").toLowerCase();
|
|
41
|
+
const state = String(check.state ?? check.status ?? "").toLowerCase();
|
|
42
|
+
return ["success", "successful", "passed", "neutral", "skipped"].includes(conclusion) || ["success", "successful", "passed", "completed"].includes(state);
|
|
43
|
+
}
|
|
44
|
+
function isFailingCheck(check) {
|
|
45
|
+
const conclusion = String(check.conclusion ?? "").toLowerCase();
|
|
46
|
+
const state = String(check.state ?? check.status ?? "").toLowerCase();
|
|
47
|
+
return ["failure", "failed", "timed_out", "action_required", "cancelled", "error"].includes(conclusion) || ["failure", "failed", "timed_out", "action_required", "cancelled", "error"].includes(state);
|
|
48
|
+
}
|
|
49
|
+
function collectPendingPrChecks(input) {
|
|
50
|
+
const allowedFailures = input.allowedFailures ?? [];
|
|
51
|
+
const pending = [];
|
|
52
|
+
for (const check of input.checks ?? []) {
|
|
53
|
+
const name = check.name.trim();
|
|
54
|
+
if (!name || isAllowedFailure(name, allowedFailures))
|
|
55
|
+
continue;
|
|
56
|
+
if (isPendingCheck(check) && !isPassingCheck(check))
|
|
57
|
+
pending.push(name);
|
|
58
|
+
}
|
|
59
|
+
return pending;
|
|
60
|
+
}
|
|
61
|
+
function collectActionablePrFeedback(input) {
|
|
62
|
+
const allowedFailures = input.allowedFailures ?? [];
|
|
63
|
+
const feedback = [];
|
|
64
|
+
for (const check of input.checks ?? []) {
|
|
65
|
+
const name = check.name.trim();
|
|
66
|
+
if (!name || !isFailingCheck(check) || isAllowedFailure(name, allowedFailures))
|
|
67
|
+
continue;
|
|
68
|
+
feedback.push(`Check failed: ${name}${check.detailsUrl ? ` (${check.detailsUrl})` : ""}`);
|
|
69
|
+
}
|
|
70
|
+
for (const thread of input.reviewThreads ?? []) {
|
|
71
|
+
if (thread.resolved === true)
|
|
72
|
+
continue;
|
|
73
|
+
const body = thread.body.trim();
|
|
74
|
+
if (!body)
|
|
75
|
+
continue;
|
|
76
|
+
feedback.push(`Review feedback from ${thread.author?.trim() || "reviewer"}: ${body}`);
|
|
77
|
+
}
|
|
78
|
+
return feedback;
|
|
79
|
+
}
|
|
80
|
+
function parseJsonArray(value) {
|
|
81
|
+
if (!value?.trim())
|
|
82
|
+
return [];
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(value);
|
|
85
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
86
|
+
} catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function parsePrChecks(value) {
|
|
91
|
+
return parseJsonArray(value).flatMap((entry) => {
|
|
92
|
+
if (!entry || typeof entry !== "object")
|
|
93
|
+
return [];
|
|
94
|
+
const record = entry;
|
|
95
|
+
const name = typeof record.name === "string" ? record.name : "";
|
|
96
|
+
if (!name.trim())
|
|
97
|
+
return [];
|
|
98
|
+
return [{
|
|
99
|
+
name,
|
|
100
|
+
status: typeof record.status === "string" ? record.status : null,
|
|
101
|
+
state: typeof record.state === "string" ? record.state : null,
|
|
102
|
+
conclusion: typeof record.conclusion === "string" ? record.conclusion : null,
|
|
103
|
+
detailsUrl: typeof record.detailsUrl === "string" ? record.detailsUrl : typeof record.link === "string" ? record.link : null
|
|
104
|
+
}];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function parsePrViewReviewThreads(value) {
|
|
108
|
+
if (!value?.trim())
|
|
109
|
+
return [];
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(value);
|
|
112
|
+
const feedback = [];
|
|
113
|
+
const reviewThreads = parsed.reviewThreads;
|
|
114
|
+
if (Array.isArray(reviewThreads)) {
|
|
115
|
+
feedback.push(...reviewThreads.flatMap((entry) => {
|
|
116
|
+
if (!entry || typeof entry !== "object")
|
|
117
|
+
return [];
|
|
118
|
+
const record = entry;
|
|
119
|
+
const body = typeof record.body === "string" ? record.body : null;
|
|
120
|
+
if (!body)
|
|
121
|
+
return [];
|
|
122
|
+
return [{
|
|
123
|
+
id: typeof record.id === "string" ? record.id : null,
|
|
124
|
+
body,
|
|
125
|
+
resolved: typeof record.resolved === "boolean" ? record.resolved : false,
|
|
126
|
+
author: typeof record.author === "string" ? record.author : null
|
|
127
|
+
}];
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
const reviews = parsed.reviews;
|
|
131
|
+
if (Array.isArray(reviews)) {
|
|
132
|
+
feedback.push(...reviews.flatMap((entry) => {
|
|
133
|
+
if (!entry || typeof entry !== "object")
|
|
134
|
+
return [];
|
|
135
|
+
const record = entry;
|
|
136
|
+
const state = typeof record.state === "string" ? record.state.toUpperCase() : "";
|
|
137
|
+
if (state !== "CHANGES_REQUESTED")
|
|
138
|
+
return [];
|
|
139
|
+
const body = typeof record.body === "string" && record.body.trim() ? record.body : "Changes requested by reviewer.";
|
|
140
|
+
const author = record.author && typeof record.author === "object" ? record.author.login : null;
|
|
141
|
+
return [{
|
|
142
|
+
id: typeof record.id === "string" ? record.id : null,
|
|
143
|
+
body,
|
|
144
|
+
resolved: false,
|
|
145
|
+
author: typeof author === "string" ? author : null
|
|
146
|
+
}];
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
const reviewDecision = typeof parsed.reviewDecision === "string" ? parsed.reviewDecision.toUpperCase() : "";
|
|
150
|
+
if (reviewDecision === "CHANGES_REQUESTED" && feedback.length === 0) {
|
|
151
|
+
feedback.push({ body: "Changes requested by reviewer.", resolved: false });
|
|
152
|
+
}
|
|
153
|
+
return feedback;
|
|
154
|
+
} catch {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function findPrUrl(output) {
|
|
159
|
+
return output?.split(/\s+/).map((part) => part.trim()).find((part) => /^https?:\/\/\S+\/pull\/\d+$/i.test(part)) ?? null;
|
|
160
|
+
}
|
|
161
|
+
function normalizePrUrl(stdout) {
|
|
162
|
+
const url = findPrUrl(stdout);
|
|
163
|
+
if (!url)
|
|
164
|
+
throw new Error("gh pr create did not return a PR URL");
|
|
165
|
+
return url;
|
|
166
|
+
}
|
|
167
|
+
async function runChecked(command, args, cwd, label = "gh") {
|
|
168
|
+
const result = await command(args, cwd ? { cwd } : undefined);
|
|
169
|
+
if (result.exitCode !== 0) {
|
|
170
|
+
throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
var RIG_RUNTIME_COMMIT_EXCLUDES = [
|
|
175
|
+
".rig",
|
|
176
|
+
"artifacts",
|
|
177
|
+
"node_modules"
|
|
178
|
+
];
|
|
179
|
+
function statusPathFromShortLine(line) {
|
|
180
|
+
const rawPath = line.length > 3 ? line.slice(3).trim() : "";
|
|
181
|
+
const renamedPath = rawPath.includes(" -> ") ? rawPath.split(" -> ").at(-1).trim() : rawPath;
|
|
182
|
+
if (renamedPath.startsWith('"') && renamedPath.endsWith('"')) {
|
|
183
|
+
try {
|
|
184
|
+
return JSON.parse(renamedPath);
|
|
185
|
+
} catch {
|
|
186
|
+
return renamedPath.slice(1, -1);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return renamedPath;
|
|
190
|
+
}
|
|
191
|
+
function hasCommittableRunChanges(statusText) {
|
|
192
|
+
return statusText.split(/\r?\n/).map((line) => statusPathFromShortLine(line)).filter(Boolean).some((path) => !RIG_RUNTIME_COMMIT_EXCLUDES.some((excluded) => path === excluded || path.startsWith(`${excluded}/`)));
|
|
193
|
+
}
|
|
194
|
+
async function commitRunChanges(input) {
|
|
195
|
+
const status = await runChecked(input.command, ["status", "--short"], input.cwd, "git");
|
|
196
|
+
const statusText = status.stdout ?? "";
|
|
197
|
+
if (!statusText.trim() || !hasCommittableRunChanges(statusText)) {
|
|
198
|
+
return { committed: false, status: statusText };
|
|
199
|
+
}
|
|
200
|
+
await runChecked(input.command, [
|
|
201
|
+
"add",
|
|
202
|
+
"-A",
|
|
203
|
+
"--",
|
|
204
|
+
".",
|
|
205
|
+
":(exclude).rig",
|
|
206
|
+
":(exclude).rig/**",
|
|
207
|
+
":(exclude)artifacts",
|
|
208
|
+
":(exclude)artifacts/**",
|
|
209
|
+
":(exclude)node_modules",
|
|
210
|
+
":(exclude)node_modules/**"
|
|
211
|
+
], input.cwd, "git");
|
|
212
|
+
const staged = await input.command(["diff", "--cached", "--quiet"], { cwd: input.cwd });
|
|
213
|
+
if (staged.exitCode === 0) {
|
|
214
|
+
return { committed: false, status: statusText };
|
|
215
|
+
}
|
|
216
|
+
if (staged.exitCode !== 1) {
|
|
217
|
+
throw new Error(`git diff --cached --quiet failed (${staged.exitCode}): ${staged.stderr ?? staged.stdout ?? ""}`.trim());
|
|
218
|
+
}
|
|
219
|
+
await runChecked(input.command, ["commit", "-m", input.message], input.cwd, "git");
|
|
220
|
+
return { committed: true, status: statusText };
|
|
221
|
+
}
|
|
222
|
+
async function closeIssueAfterMergedPr(input) {
|
|
223
|
+
await input.updateTaskSource(input.projectRoot, {
|
|
224
|
+
taskId: input.taskId,
|
|
225
|
+
sourceTask: input.sourceTask,
|
|
226
|
+
update: {
|
|
227
|
+
status: "closed",
|
|
228
|
+
comment: [
|
|
229
|
+
"<!-- rig:status-comment -->",
|
|
230
|
+
"### Rig status: closed",
|
|
231
|
+
"",
|
|
232
|
+
"Rig PR merged and closed this task.",
|
|
233
|
+
"",
|
|
234
|
+
`- Run: ${input.runId}`,
|
|
235
|
+
`- PR merged: ${input.prUrl}`
|
|
236
|
+
].join(`
|
|
237
|
+
`)
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function parseGitHubRepoFromPrUrl(prUrl) {
|
|
242
|
+
const match = /^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/\d+\/?$/i.exec(prUrl.trim());
|
|
243
|
+
return match ? { owner: match[1], repo: match[2] } : null;
|
|
244
|
+
}
|
|
245
|
+
async function resolveRepoDefaultMergeFlag(input) {
|
|
246
|
+
const repo = parseGitHubRepoFromPrUrl(input.prUrl);
|
|
247
|
+
if (!repo)
|
|
248
|
+
throw new Error(`Cannot resolve GitHub repository from PR URL: ${input.prUrl}`);
|
|
249
|
+
const result = await input.command(["api", `repos/${repo.owner}/${repo.repo}`], input.cwd ? { cwd: input.cwd } : undefined);
|
|
250
|
+
if (result.exitCode !== 0) {
|
|
251
|
+
throw new Error(`Could not read repository merge policy for ${repo.owner}/${repo.repo}: ${result.stderr ?? result.stdout ?? "gh api failed"}`.trim());
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
const parsed = JSON.parse(result.stdout ?? "{}");
|
|
255
|
+
if (parsed.allow_merge_commit !== false)
|
|
256
|
+
return "--merge";
|
|
257
|
+
if (parsed.allow_squash_merge !== false)
|
|
258
|
+
return "--squash";
|
|
259
|
+
if (parsed.allow_rebase_merge !== false)
|
|
260
|
+
return "--rebase";
|
|
261
|
+
} catch (error) {
|
|
262
|
+
throw new Error(`Could not parse repository merge policy for ${repo.owner}/${repo.repo}: ${error instanceof Error ? error.message : String(error)}`);
|
|
263
|
+
}
|
|
264
|
+
throw new Error(`Repository ${repo.owner}/${repo.repo} has no enabled merge method for repo-default merge.`);
|
|
265
|
+
}
|
|
266
|
+
async function runRepoDefaultMerge(input) {
|
|
267
|
+
const merge = input.config?.merge ?? {};
|
|
268
|
+
if (merge.mode === "off")
|
|
269
|
+
return;
|
|
270
|
+
const method = merge.method ?? "repo-default";
|
|
271
|
+
const args = ["pr", "merge", input.prUrl];
|
|
272
|
+
if (method === "repo-default") {
|
|
273
|
+
args.push(await resolveRepoDefaultMergeFlag({ prUrl: input.prUrl, command: input.command, cwd: input.cwd }));
|
|
274
|
+
} else {
|
|
275
|
+
args.push(`--${method}`);
|
|
276
|
+
}
|
|
277
|
+
if (merge.deleteBranch === true) {
|
|
278
|
+
args.push("--delete-branch");
|
|
279
|
+
}
|
|
280
|
+
if (merge.bypass === true) {
|
|
281
|
+
args.push("--admin");
|
|
282
|
+
}
|
|
283
|
+
await runChecked(input.command, args, input.cwd);
|
|
284
|
+
}
|
|
285
|
+
async function runPrAutomation(input) {
|
|
286
|
+
const prConfig = input.config?.pr ?? {};
|
|
287
|
+
if (prConfig.mode === "off") {
|
|
288
|
+
return { status: "skipped", iterations: 0, actionableFeedback: [] };
|
|
289
|
+
}
|
|
290
|
+
const body = buildPrAutomationBody({
|
|
291
|
+
taskId: input.taskId,
|
|
292
|
+
runId: input.runId,
|
|
293
|
+
summary: input.sourceTask?.title ? `Rig completed: ${input.sourceTask.title}` : null,
|
|
294
|
+
uploadedSnapshot: input.uploadedSnapshot
|
|
295
|
+
});
|
|
296
|
+
if (input.gitCommand) {
|
|
297
|
+
await runChecked(input.gitCommand, ["push", "--set-upstream", "origin", input.branch], input.projectRoot, "git");
|
|
298
|
+
}
|
|
299
|
+
const createArgs = [
|
|
300
|
+
"pr",
|
|
301
|
+
"create",
|
|
302
|
+
"--head",
|
|
303
|
+
input.branch,
|
|
304
|
+
"--title",
|
|
305
|
+
input.sourceTask?.title?.trim() || `Rig task ${input.taskId}`,
|
|
306
|
+
"--body",
|
|
307
|
+
body
|
|
308
|
+
];
|
|
309
|
+
const createResult = await input.command(createArgs, { cwd: input.projectRoot });
|
|
310
|
+
const existingPrUrl = createResult.exitCode === 0 ? null : /pull request .*already exists/i.test(`${createResult.stderr ?? ""}
|
|
311
|
+
${createResult.stdout ?? ""}`) ? findPrUrl(`${createResult.stderr ?? ""}
|
|
312
|
+
${createResult.stdout ?? ""}`) : null;
|
|
313
|
+
if (createResult.exitCode !== 0 && !existingPrUrl) {
|
|
314
|
+
throw new Error(`gh ${createArgs.join(" ")} failed (${createResult.exitCode}): ${createResult.stderr ?? createResult.stdout ?? ""}`.trim());
|
|
315
|
+
}
|
|
316
|
+
const prUrl = existingPrUrl ?? normalizePrUrl(createResult.stdout);
|
|
317
|
+
await input.lifecycle?.onPrOpened?.({ prUrl });
|
|
318
|
+
const { maxPrFixIterations } = resolvePrAutomationLimits(input.config);
|
|
319
|
+
let latestFeedback = [];
|
|
320
|
+
for (let iteration = 1;iteration <= maxPrFixIterations; iteration += 1) {
|
|
321
|
+
await input.lifecycle?.onReviewCiStarted?.({ prUrl, iteration });
|
|
322
|
+
const checks = prConfig.watchChecks === false ? [] : parsePrChecks((await runChecked(input.command, ["pr", "checks", prUrl, "--json", "name,state,link"], input.projectRoot)).stdout);
|
|
323
|
+
const reviewThreads = prConfig.autoFixReview === false ? [] : parsePrViewReviewThreads((await runChecked(input.command, ["pr", "view", prUrl, "--json", "reviewDecision,reviews"], input.projectRoot)).stdout);
|
|
324
|
+
latestFeedback = collectActionablePrFeedback({
|
|
325
|
+
checks,
|
|
326
|
+
reviewThreads,
|
|
327
|
+
allowedFailures: input.config?.merge?.allowedFailures ?? []
|
|
328
|
+
});
|
|
329
|
+
const pendingChecks = collectPendingPrChecks({ checks, allowedFailures: input.config?.merge?.allowedFailures ?? [] });
|
|
330
|
+
if (latestFeedback.length === 0 && pendingChecks.length > 0) {
|
|
331
|
+
const timeoutMs = positiveInt(prConfig.pendingTimeoutMs, 600000);
|
|
332
|
+
const pollMs = positiveInt(prConfig.pendingPollMs, 15000);
|
|
333
|
+
if (iteration >= maxPrFixIterations || timeoutMs <= 0) {
|
|
334
|
+
return { status: "needs_attention", prUrl, iterations: iteration, actionableFeedback: pendingChecks.map((name) => `Check still pending: ${name}`), merged: false };
|
|
335
|
+
}
|
|
336
|
+
await (input.sleep ?? Bun.sleep)(Math.min(pollMs, timeoutMs));
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (latestFeedback.length === 0) {
|
|
340
|
+
if (input.config?.merge?.mode === "off" || input.config?.merge?.mode === "pr-ready") {
|
|
341
|
+
return { status: "opened", prUrl, iterations: iteration, actionableFeedback: [], merged: false };
|
|
342
|
+
}
|
|
343
|
+
await input.lifecycle?.onMergeStarted?.({ prUrl });
|
|
344
|
+
await runRepoDefaultMerge({ prUrl, config: input.config, command: input.command, cwd: input.projectRoot });
|
|
345
|
+
await input.lifecycle?.onMerged?.({ prUrl });
|
|
346
|
+
return { status: "merged", prUrl, iterations: iteration, actionableFeedback: [], merged: true };
|
|
347
|
+
}
|
|
348
|
+
if (iteration >= maxPrFixIterations || prConfig.autoFixChecks === false && prConfig.autoFixReview === false) {
|
|
349
|
+
return { status: "needs_attention", prUrl, iterations: iteration, actionableFeedback: latestFeedback, merged: false };
|
|
350
|
+
}
|
|
351
|
+
await input.lifecycle?.onFeedback?.({ prUrl, iteration, feedback: latestFeedback });
|
|
352
|
+
await input.steerPi([
|
|
353
|
+
`PR automation found actionable feedback on ${prUrl}.`,
|
|
354
|
+
`Fix iteration ${iteration + 1}/${maxPrFixIterations}.`,
|
|
355
|
+
"",
|
|
356
|
+
...latestFeedback.map((entry) => `- ${entry}`)
|
|
357
|
+
].join(`
|
|
358
|
+
`));
|
|
359
|
+
}
|
|
360
|
+
return { status: "needs_attention", prUrl, iterations: maxPrFixIterations, actionableFeedback: latestFeedback, merged: false };
|
|
361
|
+
}
|
|
362
|
+
export {
|
|
363
|
+
runRepoDefaultMerge,
|
|
364
|
+
runPrAutomation,
|
|
365
|
+
resolvePrAutomationLimits,
|
|
366
|
+
hasCommittableRunChanges,
|
|
367
|
+
commitRunChanges,
|
|
368
|
+
collectPendingPrChecks,
|
|
369
|
+
collectActionablePrFeedback,
|
|
370
|
+
closeIssueAfterMergedPr,
|
|
371
|
+
buildPrAutomationBody,
|
|
372
|
+
UPLOADED_SNAPSHOT_PR_MARKER
|
|
373
|
+
};
|