@h-rig/runtime 0.0.6-alpha.34 → 0.0.6-alpha.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/rig-agent-dispatch.js +518 -459
- package/dist/bin/rig-agent.js +430 -361
- package/dist/src/control-plane/agent-wrapper.js +523 -464
- package/dist/src/control-plane/harness-main.js +528 -458
- package/dist/src/control-plane/hooks/completion-verification.js +353 -283
- package/dist/src/control-plane/hooks/inject-context.js +158 -99
- package/dist/src/control-plane/hooks/submodule-branch.js +538 -479
- package/dist/src/control-plane/hooks/task-runtime-start.js +538 -479
- package/dist/src/control-plane/materialize-task-config.js +68 -8
- package/dist/src/control-plane/native/git-ops.js +10 -0
- package/dist/src/control-plane/native/harness-cli.js +513 -443
- package/dist/src/control-plane/native/task-ops.js +392 -322
- package/dist/src/control-plane/native/validator.js +159 -100
- package/dist/src/control-plane/native/verifier.js +227 -166
- package/dist/src/control-plane/pi-settings-materializer.js +52 -0
- package/dist/src/control-plane/plugin-host-context.js +59 -0
- package/dist/src/control-plane/runtime/index.js +469 -410
- package/dist/src/control-plane/runtime/isolation/index.js +493 -434
- package/dist/src/control-plane/runtime/isolation.js +493 -434
- package/dist/src/control-plane/runtime/queue.js +411 -352
- package/dist/src/control-plane/tasks/source-lifecycle.js +87 -28
- package/package.json +8 -8
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// packages/runtime/src/control-plane/hooks/completion-verification.ts
|
|
5
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
6
|
-
import { resolve as
|
|
5
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync14 } from "fs";
|
|
6
|
+
import { resolve as resolve25 } from "path";
|
|
7
7
|
import {
|
|
8
8
|
escapeRegExp as escapeRegExp2,
|
|
9
9
|
resolveBunCli,
|
|
@@ -1073,8 +1073,8 @@ function primeGuardHotPaths() {
|
|
|
1073
1073
|
primeGuardHotPaths();
|
|
1074
1074
|
|
|
1075
1075
|
// packages/runtime/src/control-plane/native/git-ops.ts
|
|
1076
|
-
import { existsSync as
|
|
1077
|
-
import { dirname as
|
|
1076
|
+
import { existsSync as existsSync22, lstatSync, mkdirSync as mkdirSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync13 } from "fs";
|
|
1077
|
+
import { dirname as dirname11, isAbsolute as isAbsolute2, resolve as resolve24 } from "path";
|
|
1078
1078
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1079
1079
|
|
|
1080
1080
|
// packages/runtime/src/control-plane/runtime/baked-secrets.ts
|
|
@@ -1336,8 +1336,8 @@ function isAgentRuntimeContextPath(path) {
|
|
|
1336
1336
|
}
|
|
1337
1337
|
|
|
1338
1338
|
// packages/runtime/src/control-plane/native/task-ops.ts
|
|
1339
|
-
import { appendFileSync, existsSync as
|
|
1340
|
-
import { resolve as
|
|
1339
|
+
import { appendFileSync, existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
|
|
1340
|
+
import { resolve as resolve23 } from "path";
|
|
1341
1341
|
|
|
1342
1342
|
// packages/runtime/src/build-time-config.ts
|
|
1343
1343
|
function normalizeBuildConfig(value) {
|
|
@@ -1653,6 +1653,8 @@ import { basename as basename3, dirname as dirname6, resolve as resolve9 } from
|
|
|
1653
1653
|
var sharedNativeToolsOutputDir = resolve9(tmpdir4(), "rig-native");
|
|
1654
1654
|
var sharedNativeToolsOutputPath = resolve9(sharedNativeToolsOutputDir, `rig-tools-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
1655
1655
|
// packages/runtime/src/control-plane/plugin-host-context.ts
|
|
1656
|
+
import { existsSync as existsSync12 } from "fs";
|
|
1657
|
+
import { resolve as resolvePath } from "path";
|
|
1656
1658
|
import { createPluginHost } from "@rig/core";
|
|
1657
1659
|
import { loadConfig } from "@rig/core/load-config";
|
|
1658
1660
|
|
|
@@ -1973,6 +1975,55 @@ async function materializeSkills(projectRoot, entries) {
|
|
|
1973
1975
|
return written;
|
|
1974
1976
|
}
|
|
1975
1977
|
|
|
1978
|
+
// packages/runtime/src/control-plane/pi-settings-materializer.ts
|
|
1979
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
1980
|
+
import { dirname as dirname8, resolve as resolve12 } from "path";
|
|
1981
|
+
var SETTINGS_RELATIVE_PATH = ".pi/settings.json";
|
|
1982
|
+
var MANAGED_RECORD_RELATIVE_PATH = ".rig/state/pi-managed-packages.json";
|
|
1983
|
+
function readJson(path, fallback) {
|
|
1984
|
+
if (!existsSync11(path))
|
|
1985
|
+
return fallback;
|
|
1986
|
+
try {
|
|
1987
|
+
return JSON.parse(readFileSync8(path, "utf-8"));
|
|
1988
|
+
} catch {
|
|
1989
|
+
return fallback;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
function packageKey(entry) {
|
|
1993
|
+
if (typeof entry === "string")
|
|
1994
|
+
return entry;
|
|
1995
|
+
if (entry && typeof entry === "object" && typeof entry.source === "string") {
|
|
1996
|
+
return entry.source;
|
|
1997
|
+
}
|
|
1998
|
+
return JSON.stringify(entry);
|
|
1999
|
+
}
|
|
2000
|
+
function materializePiPackages(projectRoot, declaredPackages) {
|
|
2001
|
+
const settingsPath = resolve12(projectRoot, SETTINGS_RELATIVE_PATH);
|
|
2002
|
+
const managedRecordPath = resolve12(projectRoot, MANAGED_RECORD_RELATIVE_PATH);
|
|
2003
|
+
const settings = readJson(settingsPath, {});
|
|
2004
|
+
const previouslyManaged = new Set(readJson(managedRecordPath, []));
|
|
2005
|
+
const existing = Array.isArray(settings.packages) ? settings.packages : [];
|
|
2006
|
+
const operatorEntries = existing.filter((entry) => !previouslyManaged.has(packageKey(entry)));
|
|
2007
|
+
const operatorKeys = new Set(operatorEntries.map(packageKey));
|
|
2008
|
+
const managedToAdd = declaredPackages.filter((pkg) => !operatorKeys.has(pkg));
|
|
2009
|
+
const nextPackages = [...operatorEntries, ...managedToAdd];
|
|
2010
|
+
if (nextPackages.length > 0 || existsSync11(settingsPath)) {
|
|
2011
|
+
const nextSettings = { ...settings };
|
|
2012
|
+
if (nextPackages.length > 0) {
|
|
2013
|
+
nextSettings.packages = nextPackages;
|
|
2014
|
+
} else {
|
|
2015
|
+
delete nextSettings.packages;
|
|
2016
|
+
}
|
|
2017
|
+
mkdirSync6(dirname8(settingsPath), { recursive: true });
|
|
2018
|
+
writeFileSync5(settingsPath, `${JSON.stringify(nextSettings, null, 2)}
|
|
2019
|
+
`, "utf-8");
|
|
2020
|
+
}
|
|
2021
|
+
mkdirSync6(dirname8(managedRecordPath), { recursive: true });
|
|
2022
|
+
writeFileSync5(managedRecordPath, `${JSON.stringify(managedToAdd, null, 2)}
|
|
2023
|
+
`, "utf-8");
|
|
2024
|
+
return { settingsPath, packages: managedToAdd };
|
|
2025
|
+
}
|
|
2026
|
+
|
|
1976
2027
|
// packages/runtime/src/control-plane/plugin-host-context.ts
|
|
1977
2028
|
async function buildPluginHostContext(projectRoot) {
|
|
1978
2029
|
let config;
|
|
@@ -2020,6 +2071,14 @@ async function buildPluginHostContext(projectRoot) {
|
|
|
2020
2071
|
} catch (err) {
|
|
2021
2072
|
console.warn(`[plugin-host] skill materialization failed: ${err instanceof Error ? err.message : err}`);
|
|
2022
2073
|
}
|
|
2074
|
+
try {
|
|
2075
|
+
const piPackages = config.runtime?.pi?.packages ?? [];
|
|
2076
|
+
if (piPackages.length > 0 || existsSync12(resolvePath(projectRoot, ".rig/state/pi-managed-packages.json"))) {
|
|
2077
|
+
materializePiPackages(projectRoot, piPackages);
|
|
2078
|
+
}
|
|
2079
|
+
} catch (err) {
|
|
2080
|
+
console.warn(`[plugin-host] Pi package materialization failed: ${err instanceof Error ? err.message : err}`);
|
|
2081
|
+
}
|
|
2023
2082
|
return {
|
|
2024
2083
|
config,
|
|
2025
2084
|
pluginHost,
|
|
@@ -2033,12 +2092,12 @@ async function buildPluginHostContext(projectRoot) {
|
|
|
2033
2092
|
|
|
2034
2093
|
// packages/runtime/src/control-plane/tasks/source-aware-task-config-source.ts
|
|
2035
2094
|
import { spawnSync } from "child_process";
|
|
2036
|
-
import { existsSync as
|
|
2037
|
-
import { basename as basename4, join as join2, resolve as
|
|
2095
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, readdirSync as readdirSync2, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2096
|
+
import { basename as basename4, join as join2, resolve as resolve14 } from "path";
|
|
2038
2097
|
|
|
2039
2098
|
// packages/runtime/src/control-plane/tasks/legacy-task-config-source.ts
|
|
2040
|
-
import { existsSync as
|
|
2041
|
-
import { resolve as
|
|
2099
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
2100
|
+
import { resolve as resolve13 } from "path";
|
|
2042
2101
|
|
|
2043
2102
|
// packages/runtime/src/control-plane/tasks/task-record-reader.ts
|
|
2044
2103
|
async function findTaskById(reader, id) {
|
|
@@ -2061,7 +2120,7 @@ class LegacyTaskConfigReadError extends Error {
|
|
|
2061
2120
|
}
|
|
2062
2121
|
}
|
|
2063
2122
|
function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
|
|
2064
|
-
const configPath = options.configPath ??
|
|
2123
|
+
const configPath = options.configPath ?? resolve13(projectRoot, ".rig", "task-config.json");
|
|
2065
2124
|
const reader = {
|
|
2066
2125
|
async listTasks() {
|
|
2067
2126
|
return readLegacyTaskRecords(projectRoot, configPath);
|
|
@@ -2072,8 +2131,8 @@ function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
|
|
|
2072
2131
|
};
|
|
2073
2132
|
return reader;
|
|
2074
2133
|
}
|
|
2075
|
-
function readLegacyTaskRecords(projectRoot, configPath =
|
|
2076
|
-
if (!
|
|
2134
|
+
function readLegacyTaskRecords(projectRoot, configPath = resolve13(projectRoot, ".rig", "task-config.json")) {
|
|
2135
|
+
if (!existsSync13(configPath)) {
|
|
2077
2136
|
return [];
|
|
2078
2137
|
}
|
|
2079
2138
|
const rawConfig = readLegacyTaskConfigJson(projectRoot, configPath);
|
|
@@ -2081,7 +2140,7 @@ function readLegacyTaskRecords(projectRoot, configPath = resolve12(projectRoot,
|
|
|
2081
2140
|
}
|
|
2082
2141
|
function readLegacyTaskConfigJson(projectRoot, configPath) {
|
|
2083
2142
|
try {
|
|
2084
|
-
const parsed = JSON.parse(
|
|
2143
|
+
const parsed = JSON.parse(readFileSync9(configPath, "utf8"));
|
|
2085
2144
|
if (isPlainRecord(parsed)) {
|
|
2086
2145
|
return parsed;
|
|
2087
2146
|
}
|
|
@@ -2165,7 +2224,7 @@ function isPlainRecord(candidate) {
|
|
|
2165
2224
|
var STATUS_LABELS = new Set(["ready", "blocked", "in-progress", "under-review", "failed", "cancelled"]);
|
|
2166
2225
|
var FILE_TASK_PATTERN = /\.(task\.)?json$/;
|
|
2167
2226
|
function createSourceAwareTaskConfigRecordReader(projectRoot, options = {}) {
|
|
2168
|
-
const configPath = options.configPath ??
|
|
2227
|
+
const configPath = options.configPath ?? resolve14(projectRoot, ".rig", "task-config.json");
|
|
2169
2228
|
const legacy = createLegacyTaskConfigRecordReader(projectRoot, { configPath });
|
|
2170
2229
|
const spawnFn = options.spawn ?? spawnSync;
|
|
2171
2230
|
const ghBinary = options.ghBinary ?? "gh";
|
|
@@ -2231,7 +2290,7 @@ async function readSourceAwareTaskStatus(projectRoot, taskId, options = {}) {
|
|
|
2231
2290
|
}
|
|
2232
2291
|
}
|
|
2233
2292
|
function updateSourceAwareTaskConfigTask(projectRoot, taskId, update, options = {}) {
|
|
2234
|
-
const configPath = options.configPath ??
|
|
2293
|
+
const configPath = options.configPath ?? resolve14(projectRoot, ".rig", "task-config.json");
|
|
2235
2294
|
const rawEntry = readRawTaskEntry(configPath, taskId);
|
|
2236
2295
|
if (!rawEntry) {
|
|
2237
2296
|
const configuredFilesPath = readConfiguredFilesTaskSourcePath(projectRoot);
|
|
@@ -2284,10 +2343,10 @@ function readMaterializedTaskMetadata(entry) {
|
|
|
2284
2343
|
return metadata;
|
|
2285
2344
|
}
|
|
2286
2345
|
function readConfiguredFilesTaskSourcePath(projectRoot) {
|
|
2287
|
-
const jsonPath =
|
|
2288
|
-
if (
|
|
2346
|
+
const jsonPath = resolve14(projectRoot, "rig.config.json");
|
|
2347
|
+
if (existsSync14(jsonPath)) {
|
|
2289
2348
|
try {
|
|
2290
|
-
const parsed = JSON.parse(
|
|
2349
|
+
const parsed = JSON.parse(readFileSync10(jsonPath, "utf8"));
|
|
2291
2350
|
if (isPlainRecord2(parsed) && isPlainRecord2(parsed.taskSource)) {
|
|
2292
2351
|
const source = parsed.taskSource;
|
|
2293
2352
|
return source.kind === "files" && typeof source.path === "string" ? source.path : null;
|
|
@@ -2296,12 +2355,12 @@ function readConfiguredFilesTaskSourcePath(projectRoot) {
|
|
|
2296
2355
|
return null;
|
|
2297
2356
|
}
|
|
2298
2357
|
}
|
|
2299
|
-
const tsPath =
|
|
2300
|
-
if (!
|
|
2358
|
+
const tsPath = resolve14(projectRoot, "rig.config.ts");
|
|
2359
|
+
if (!existsSync14(tsPath)) {
|
|
2301
2360
|
return null;
|
|
2302
2361
|
}
|
|
2303
2362
|
try {
|
|
2304
|
-
const source =
|
|
2363
|
+
const source = readFileSync10(tsPath, "utf8");
|
|
2305
2364
|
const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
|
|
2306
2365
|
const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
|
|
2307
2366
|
if (kind !== "files") {
|
|
@@ -2321,10 +2380,10 @@ function readRawTaskEntry(configPath, taskId) {
|
|
|
2321
2380
|
return isPlainRecord2(entry) ? entry : null;
|
|
2322
2381
|
}
|
|
2323
2382
|
function readRawTaskConfig(configPath) {
|
|
2324
|
-
if (!
|
|
2383
|
+
if (!existsSync14(configPath)) {
|
|
2325
2384
|
return null;
|
|
2326
2385
|
}
|
|
2327
|
-
const parsed = JSON.parse(
|
|
2386
|
+
const parsed = JSON.parse(readFileSync10(configPath, "utf8"));
|
|
2328
2387
|
return isPlainRecord2(parsed) ? parsed : null;
|
|
2329
2388
|
}
|
|
2330
2389
|
function stripLegacyTaskConfigMetadata2(raw) {
|
|
@@ -2341,16 +2400,16 @@ function writeLegacyTaskStatus(configPath, taskId, status) {
|
|
|
2341
2400
|
return;
|
|
2342
2401
|
}
|
|
2343
2402
|
entry.status = status;
|
|
2344
|
-
|
|
2403
|
+
writeFileSync6(configPath, `${JSON.stringify(rawConfig, null, 2)}
|
|
2345
2404
|
`, "utf8");
|
|
2346
2405
|
}
|
|
2347
2406
|
function updateFileBackedTask(projectRoot, sourcePath, taskId, update) {
|
|
2348
|
-
const directory =
|
|
2407
|
+
const directory = resolve14(projectRoot, sourcePath);
|
|
2349
2408
|
const file = findFileBackedTaskFile(directory, taskId);
|
|
2350
2409
|
if (!file) {
|
|
2351
2410
|
return false;
|
|
2352
2411
|
}
|
|
2353
|
-
const raw = JSON.parse(
|
|
2412
|
+
const raw = JSON.parse(readFileSync10(file, "utf8"));
|
|
2354
2413
|
if (!isPlainRecord2(raw)) {
|
|
2355
2414
|
return false;
|
|
2356
2415
|
}
|
|
@@ -2367,13 +2426,13 @@ function updateFileBackedTask(projectRoot, sourcePath, taskId, update) {
|
|
|
2367
2426
|
{ body: update.comment, createdAt: new Date().toISOString(), source: "rig" }
|
|
2368
2427
|
];
|
|
2369
2428
|
}
|
|
2370
|
-
|
|
2429
|
+
writeFileSync6(file, `${JSON.stringify(raw, null, 2)}
|
|
2371
2430
|
`, "utf8");
|
|
2372
2431
|
return true;
|
|
2373
2432
|
}
|
|
2374
2433
|
function listFileBackedTasks(projectRoot, sourcePath) {
|
|
2375
|
-
const directory =
|
|
2376
|
-
if (!
|
|
2434
|
+
const directory = resolve14(projectRoot, sourcePath);
|
|
2435
|
+
if (!existsSync14(directory)) {
|
|
2377
2436
|
return [];
|
|
2378
2437
|
}
|
|
2379
2438
|
const tasks = [];
|
|
@@ -2388,11 +2447,11 @@ function listFileBackedTasks(projectRoot, sourcePath) {
|
|
|
2388
2447
|
return tasks;
|
|
2389
2448
|
}
|
|
2390
2449
|
function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
|
|
2391
|
-
const file = findFileBackedTaskFile(
|
|
2450
|
+
const file = findFileBackedTaskFile(resolve14(projectRoot, sourcePath), taskId);
|
|
2392
2451
|
if (!file) {
|
|
2393
2452
|
return null;
|
|
2394
2453
|
}
|
|
2395
|
-
const raw = JSON.parse(
|
|
2454
|
+
const raw = JSON.parse(readFileSync10(file, "utf8"));
|
|
2396
2455
|
if (!isPlainRecord2(raw)) {
|
|
2397
2456
|
return null;
|
|
2398
2457
|
}
|
|
@@ -2405,7 +2464,7 @@ function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
|
|
|
2405
2464
|
};
|
|
2406
2465
|
}
|
|
2407
2466
|
function findFileBackedTaskFile(directory, taskId) {
|
|
2408
|
-
if (!
|
|
2467
|
+
if (!existsSync14(directory)) {
|
|
2409
2468
|
return null;
|
|
2410
2469
|
}
|
|
2411
2470
|
for (const name of readdirSync2(directory)) {
|
|
@@ -2415,7 +2474,7 @@ function findFileBackedTaskFile(directory, taskId) {
|
|
|
2415
2474
|
try {
|
|
2416
2475
|
if (!statSync3(file).isFile())
|
|
2417
2476
|
continue;
|
|
2418
|
-
const raw = JSON.parse(
|
|
2477
|
+
const raw = JSON.parse(readFileSync10(file, "utf8"));
|
|
2419
2478
|
const inferredId = basename4(file).replace(FILE_TASK_PATTERN, "");
|
|
2420
2479
|
const id = isPlainRecord2(raw) && typeof raw.id === "string" ? raw.id : inferredId;
|
|
2421
2480
|
if (id === taskId) {
|
|
@@ -2764,8 +2823,8 @@ function buildTaskRunLifecycleComment(input) {
|
|
|
2764
2823
|
}
|
|
2765
2824
|
|
|
2766
2825
|
// packages/runtime/src/control-plane/native/task-state.ts
|
|
2767
|
-
import { existsSync as
|
|
2768
|
-
import { basename as basename5, resolve as
|
|
2826
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11, readdirSync as readdirSync3, statSync as statSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
2827
|
+
import { basename as basename5, resolve as resolve15 } from "path";
|
|
2769
2828
|
|
|
2770
2829
|
// packages/runtime/src/control-plane/state-sync/types.ts
|
|
2771
2830
|
var CANONICAL_TASK_LIFECYCLE_STATUSES = new Set([
|
|
@@ -2795,7 +2854,7 @@ function readValidationDescriptions(projectRoot) {
|
|
|
2795
2854
|
return readValidationDescriptionMap(raw);
|
|
2796
2855
|
}
|
|
2797
2856
|
function readSourceValidationDescriptions(projectRoot) {
|
|
2798
|
-
const rootRaw = readJsonFile(
|
|
2857
|
+
const rootRaw = readJsonFile(resolve15(projectRoot, "rig", "task-config.json"), {});
|
|
2799
2858
|
const sourcePath = findSourceTaskConfigPath(projectRoot);
|
|
2800
2859
|
const sourceRaw = sourcePath ? readJsonFile(sourcePath, {}) : {};
|
|
2801
2860
|
const rootDescriptions = readValidationDescriptionMap(rootRaw);
|
|
@@ -2886,25 +2945,25 @@ function lookupTask(projectRoot, input) {
|
|
|
2886
2945
|
function artifactDirForId(projectRoot, id) {
|
|
2887
2946
|
const workspaceDir = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
2888
2947
|
if (workspaceDir) {
|
|
2889
|
-
const worktreeArtifacts =
|
|
2890
|
-
if (
|
|
2948
|
+
const worktreeArtifacts = resolve15(workspaceDir, "artifacts", id);
|
|
2949
|
+
if (existsSync15(worktreeArtifacts) || existsSync15(resolve15(workspaceDir, "artifacts"))) {
|
|
2891
2950
|
return worktreeArtifacts;
|
|
2892
2951
|
}
|
|
2893
2952
|
}
|
|
2894
2953
|
try {
|
|
2895
2954
|
const paths = resolveHarnessPaths(projectRoot);
|
|
2896
|
-
return
|
|
2955
|
+
return resolve15(paths.artifactsDir, id);
|
|
2897
2956
|
} catch {
|
|
2898
|
-
return
|
|
2957
|
+
return resolve15(resolveMonorepoRoot2(projectRoot), "artifacts", id);
|
|
2899
2958
|
}
|
|
2900
2959
|
}
|
|
2901
2960
|
function resolveTaskConfigPath(projectRoot) {
|
|
2902
2961
|
const paths = resolveHarnessPaths(projectRoot);
|
|
2903
|
-
if (
|
|
2962
|
+
if (existsSync15(paths.taskConfigPath)) {
|
|
2904
2963
|
return paths.taskConfigPath;
|
|
2905
2964
|
}
|
|
2906
2965
|
for (const candidate of sourceTaskConfigCandidates(projectRoot)) {
|
|
2907
|
-
if (
|
|
2966
|
+
if (existsSync15(candidate)) {
|
|
2908
2967
|
return candidate;
|
|
2909
2968
|
}
|
|
2910
2969
|
}
|
|
@@ -2912,7 +2971,7 @@ function resolveTaskConfigPath(projectRoot) {
|
|
|
2912
2971
|
}
|
|
2913
2972
|
function findSourceTaskConfigPath(projectRoot) {
|
|
2914
2973
|
for (const candidate of sourceTaskConfigCandidates(projectRoot)) {
|
|
2915
|
-
if (
|
|
2974
|
+
if (existsSync15(candidate)) {
|
|
2916
2975
|
return candidate;
|
|
2917
2976
|
}
|
|
2918
2977
|
}
|
|
@@ -2925,7 +2984,7 @@ function readAndSyncSourceTaskConfig(projectRoot) {
|
|
|
2925
2984
|
const synced = synchronizeTaskConfigWithTracker(projectRoot, raw);
|
|
2926
2985
|
if (sourcePath && synced.updated) {
|
|
2927
2986
|
try {
|
|
2928
|
-
|
|
2987
|
+
writeFileSync7(sourcePath, `${JSON.stringify(synced.config, null, 2)}
|
|
2929
2988
|
`, "utf-8");
|
|
2930
2989
|
} catch {}
|
|
2931
2990
|
}
|
|
@@ -2977,12 +3036,12 @@ function shouldRefreshAutoSyncedTaskConfigEntry(entry) {
|
|
|
2977
3036
|
return !candidate.role;
|
|
2978
3037
|
}
|
|
2979
3038
|
function readSourceIssueRecords(projectRoot) {
|
|
2980
|
-
const issuesPath =
|
|
2981
|
-
if (!
|
|
3039
|
+
const issuesPath = resolve15(resolveMonorepoRoot2(projectRoot), ".beads", "issues.jsonl");
|
|
3040
|
+
if (!existsSync15(issuesPath)) {
|
|
2982
3041
|
return [];
|
|
2983
3042
|
}
|
|
2984
3043
|
const records = [];
|
|
2985
|
-
for (const line of
|
|
3044
|
+
for (const line of readFileSync11(issuesPath, "utf-8").split(/\r?\n/)) {
|
|
2986
3045
|
const trimmed = line.trim();
|
|
2987
3046
|
if (!trimmed) {
|
|
2988
3047
|
continue;
|
|
@@ -3038,19 +3097,19 @@ function readConfiguredFileTaskConfig(projectRoot) {
|
|
|
3038
3097
|
if (!sourcePath) {
|
|
3039
3098
|
return {};
|
|
3040
3099
|
}
|
|
3041
|
-
const directory =
|
|
3042
|
-
if (!
|
|
3100
|
+
const directory = resolve15(projectRoot, sourcePath);
|
|
3101
|
+
if (!existsSync15(directory)) {
|
|
3043
3102
|
return {};
|
|
3044
3103
|
}
|
|
3045
3104
|
const config = {};
|
|
3046
3105
|
for (const name of readdirSync3(directory)) {
|
|
3047
3106
|
if (!FILE_TASK_PATTERN2.test(name))
|
|
3048
3107
|
continue;
|
|
3049
|
-
const file =
|
|
3108
|
+
const file = resolve15(directory, name);
|
|
3050
3109
|
try {
|
|
3051
3110
|
if (!statSync4(file).isFile())
|
|
3052
3111
|
continue;
|
|
3053
|
-
const raw = JSON.parse(
|
|
3112
|
+
const raw = JSON.parse(readFileSync11(file, "utf8"));
|
|
3054
3113
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
3055
3114
|
continue;
|
|
3056
3115
|
const record = raw;
|
|
@@ -3092,10 +3151,10 @@ function firstStringList2(...candidates) {
|
|
|
3092
3151
|
return [];
|
|
3093
3152
|
}
|
|
3094
3153
|
function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
3095
|
-
const jsonPath =
|
|
3096
|
-
if (
|
|
3154
|
+
const jsonPath = resolve15(projectRoot, "rig.config.json");
|
|
3155
|
+
if (existsSync15(jsonPath)) {
|
|
3097
3156
|
try {
|
|
3098
|
-
const parsed = JSON.parse(
|
|
3157
|
+
const parsed = JSON.parse(readFileSync11(jsonPath, "utf8"));
|
|
3099
3158
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3100
3159
|
const taskSource = parsed.taskSource;
|
|
3101
3160
|
if (taskSource && typeof taskSource === "object" && !Array.isArray(taskSource)) {
|
|
@@ -3107,12 +3166,12 @@ function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
|
3107
3166
|
return null;
|
|
3108
3167
|
}
|
|
3109
3168
|
}
|
|
3110
|
-
const tsPath =
|
|
3111
|
-
if (!
|
|
3169
|
+
const tsPath = resolve15(projectRoot, "rig.config.ts");
|
|
3170
|
+
if (!existsSync15(tsPath)) {
|
|
3112
3171
|
return null;
|
|
3113
3172
|
}
|
|
3114
3173
|
try {
|
|
3115
|
-
const source =
|
|
3174
|
+
const source = readFileSync11(tsPath, "utf8");
|
|
3116
3175
|
const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
|
|
3117
3176
|
const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
|
|
3118
3177
|
if (kind !== "files") {
|
|
@@ -3126,23 +3185,23 @@ function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
|
3126
3185
|
function sourceTaskConfigCandidates(projectRoot) {
|
|
3127
3186
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
3128
3187
|
return [
|
|
3129
|
-
runtimeContext?.monorepoMainRoot ?
|
|
3130
|
-
process.env.MONOREPO_MAIN_ROOT?.trim() ?
|
|
3131
|
-
|
|
3188
|
+
runtimeContext?.monorepoMainRoot ? resolve15(runtimeContext.monorepoMainRoot, ".rig", "task-config.json") : "",
|
|
3189
|
+
process.env.MONOREPO_MAIN_ROOT?.trim() ? resolve15(process.env.MONOREPO_MAIN_ROOT.trim(), ".rig", "task-config.json") : "",
|
|
3190
|
+
resolve15(resolveMonorepoRoot2(projectRoot), ".rig", "task-config.json")
|
|
3132
3191
|
].filter(Boolean);
|
|
3133
3192
|
}
|
|
3134
3193
|
|
|
3135
3194
|
// packages/runtime/src/control-plane/native/validator.ts
|
|
3136
|
-
import { existsSync as
|
|
3137
|
-
import { resolve as
|
|
3195
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
3196
|
+
import { resolve as resolve20 } from "path";
|
|
3138
3197
|
|
|
3139
3198
|
// packages/runtime/src/control-plane/native/validator-binaries.ts
|
|
3140
|
-
import { existsSync as
|
|
3141
|
-
import { dirname as
|
|
3199
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8, rmSync as rmSync5, statSync as statSync5 } from "fs";
|
|
3200
|
+
import { dirname as dirname10, resolve as resolve19 } from "path";
|
|
3142
3201
|
|
|
3143
3202
|
// packages/runtime/src/binary-run.ts
|
|
3144
|
-
import { chmodSync as chmodSync2, cpSync, existsSync as
|
|
3145
|
-
import { basename as basename6, dirname as
|
|
3203
|
+
import { chmodSync as chmodSync2, cpSync, existsSync as existsSync16, mkdirSync as mkdirSync7, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
3204
|
+
import { basename as basename6, dirname as dirname9, resolve as resolve16 } from "path";
|
|
3146
3205
|
import { fileURLToPath } from "url";
|
|
3147
3206
|
import { drainMicrotasks, gcAndSweep } from "bun:jsc";
|
|
3148
3207
|
var runtimeBinaryBuildQueue = Promise.resolve();
|
|
@@ -3168,9 +3227,9 @@ async function buildRuntimeBinary(options) {
|
|
|
3168
3227
|
});
|
|
3169
3228
|
}
|
|
3170
3229
|
async function buildRuntimeBinaryInProcess(options, manifest) {
|
|
3171
|
-
const tempBuildDir =
|
|
3172
|
-
const tempOutputPath =
|
|
3173
|
-
|
|
3230
|
+
const tempBuildDir = resolve16(dirname9(options.outputPath), `.bun-build-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
3231
|
+
const tempOutputPath = resolve16(tempBuildDir, basename6(options.outputPath));
|
|
3232
|
+
mkdirSync7(tempBuildDir, { recursive: true });
|
|
3174
3233
|
await withTemporaryEnv({
|
|
3175
3234
|
...options.env,
|
|
3176
3235
|
...options.define ? { RIG_BUILD_CONFIG_JSON: JSON.stringify(options.define) } : {}
|
|
@@ -3195,7 +3254,7 @@ async function buildRuntimeBinaryInProcess(options, manifest) {
|
|
|
3195
3254
|
`);
|
|
3196
3255
|
throw new Error(`Failed to build ${options.entrypoint}: ${details || "Bun.build() returned errors"}`);
|
|
3197
3256
|
}
|
|
3198
|
-
if (!
|
|
3257
|
+
if (!existsSync16(tempOutputPath)) {
|
|
3199
3258
|
const emitted = buildResult.outputs.map((output) => output.path).join(", ") || "(none)";
|
|
3200
3259
|
throw new Error(`Failed to build ${options.entrypoint}: Bun.build() did not emit ${tempOutputPath}. Emitted: ${emitted}`);
|
|
3201
3260
|
}
|
|
@@ -3227,8 +3286,8 @@ function runtimeBinaryCacheManifestPath(outputPath) {
|
|
|
3227
3286
|
function resolveRuntimeBinaryBuildOptions(options) {
|
|
3228
3287
|
return {
|
|
3229
3288
|
...options,
|
|
3230
|
-
entrypoint:
|
|
3231
|
-
outputPath:
|
|
3289
|
+
entrypoint: resolve16(options.cwd, options.sourcePath),
|
|
3290
|
+
outputPath: resolve16(options.outputPath)
|
|
3232
3291
|
};
|
|
3233
3292
|
}
|
|
3234
3293
|
function shouldUseRuntimeBinaryBuildWorker() {
|
|
@@ -3242,7 +3301,7 @@ function shouldUseRuntimeBinaryBuildWorker() {
|
|
|
3242
3301
|
}
|
|
3243
3302
|
async function buildRuntimeBinaryViaWorker(options) {
|
|
3244
3303
|
const workerSourcePath = resolveRuntimeBinaryBuildWorkerSourcePath(options);
|
|
3245
|
-
if (!workerSourcePath || !
|
|
3304
|
+
if (!workerSourcePath || !existsSync16(workerSourcePath)) {
|
|
3246
3305
|
await buildRuntimeBinaryInProcess(options, {
|
|
3247
3306
|
manifestPath: runtimeBinaryCacheManifestPath(options.outputPath),
|
|
3248
3307
|
buildKey: createRuntimeBinaryBuildKey({
|
|
@@ -3279,7 +3338,7 @@ async function buildRuntimeBinaryViaWorker(options) {
|
|
|
3279
3338
|
}
|
|
3280
3339
|
}
|
|
3281
3340
|
function createRuntimeBinaryBuildWorkerPayloadPath(outputPath) {
|
|
3282
|
-
return
|
|
3341
|
+
return resolve16(dirname9(outputPath), `.bun-build-worker-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
3283
3342
|
}
|
|
3284
3343
|
function resolveRuntimeBinaryBuildWorkerSourcePath(options) {
|
|
3285
3344
|
const envRoots = [
|
|
@@ -3288,13 +3347,13 @@ function resolveRuntimeBinaryBuildWorkerSourcePath(options) {
|
|
|
3288
3347
|
process.env.PROJECT_RIG_ROOT?.trim()
|
|
3289
3348
|
].filter(Boolean);
|
|
3290
3349
|
for (const root of envRoots) {
|
|
3291
|
-
const candidate =
|
|
3292
|
-
if (
|
|
3350
|
+
const candidate = resolve16(root, "packages/runtime/src/binary-build-worker.ts");
|
|
3351
|
+
if (existsSync16(candidate)) {
|
|
3293
3352
|
return candidate;
|
|
3294
3353
|
}
|
|
3295
3354
|
}
|
|
3296
|
-
const localCandidate =
|
|
3297
|
-
return
|
|
3355
|
+
const localCandidate = resolve16(import.meta.dir, "binary-build-worker.ts");
|
|
3356
|
+
return existsSync16(localCandidate) ? localCandidate : null;
|
|
3298
3357
|
}
|
|
3299
3358
|
function resolveRuntimeBinaryBuildWorkerInvocation() {
|
|
3300
3359
|
const bunPath = Bun.which("bun");
|
|
@@ -3330,7 +3389,7 @@ function createRuntimeBinaryBuildKey(input) {
|
|
|
3330
3389
|
});
|
|
3331
3390
|
}
|
|
3332
3391
|
async function isRuntimeBinaryBuildFresh(input) {
|
|
3333
|
-
if (!
|
|
3392
|
+
if (!existsSync16(input.outputPath) || !existsSync16(input.manifestPath)) {
|
|
3334
3393
|
return false;
|
|
3335
3394
|
}
|
|
3336
3395
|
let manifest = null;
|
|
@@ -3343,7 +3402,7 @@ async function isRuntimeBinaryBuildFresh(input) {
|
|
|
3343
3402
|
return false;
|
|
3344
3403
|
}
|
|
3345
3404
|
for (const [filePath, expectedDigest] of Object.entries(manifest.inputs || {})) {
|
|
3346
|
-
if (!
|
|
3405
|
+
if (!existsSync16(filePath)) {
|
|
3347
3406
|
return false;
|
|
3348
3407
|
}
|
|
3349
3408
|
if (await sha256File(filePath) !== expectedDigest) {
|
|
@@ -3356,7 +3415,7 @@ async function writeRuntimeBinaryCacheManifest(input) {
|
|
|
3356
3415
|
const inputs = {};
|
|
3357
3416
|
for (const inputPath of Object.keys(input.metafile?.inputs || {}).sort()) {
|
|
3358
3417
|
const normalized = normalizeBuildInputPath(input.cwd, inputPath);
|
|
3359
|
-
if (!normalized || !
|
|
3418
|
+
if (!normalized || !existsSync16(normalized)) {
|
|
3360
3419
|
continue;
|
|
3361
3420
|
}
|
|
3362
3421
|
inputs[normalized] = await sha256File(normalized);
|
|
@@ -3379,7 +3438,7 @@ function normalizeBuildInputPath(cwd, inputPath) {
|
|
|
3379
3438
|
if (inputPath.startsWith("<")) {
|
|
3380
3439
|
return null;
|
|
3381
3440
|
}
|
|
3382
|
-
return
|
|
3441
|
+
return resolve16(cwd, inputPath);
|
|
3383
3442
|
}
|
|
3384
3443
|
async function sha256File(path) {
|
|
3385
3444
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
@@ -3395,8 +3454,8 @@ function sortRecord(value) {
|
|
|
3395
3454
|
async function runSerializedRuntimeBinaryBuild(action) {
|
|
3396
3455
|
const previous = runtimeBinaryBuildQueue;
|
|
3397
3456
|
let release;
|
|
3398
|
-
runtimeBinaryBuildQueue = new Promise((
|
|
3399
|
-
release =
|
|
3457
|
+
runtimeBinaryBuildQueue = new Promise((resolve17) => {
|
|
3458
|
+
release = resolve17;
|
|
3400
3459
|
});
|
|
3401
3460
|
await previous;
|
|
3402
3461
|
try {
|
|
@@ -3441,11 +3500,11 @@ async function withTemporaryCwd(cwd, action) {
|
|
|
3441
3500
|
}
|
|
3442
3501
|
|
|
3443
3502
|
// packages/runtime/src/control-plane/runtime/provisioning-env.ts
|
|
3444
|
-
import { delimiter, resolve as
|
|
3503
|
+
import { delimiter, resolve as resolve18 } from "path";
|
|
3445
3504
|
|
|
3446
3505
|
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
3447
|
-
import { existsSync as
|
|
3448
|
-
import { resolve as
|
|
3506
|
+
import { existsSync as existsSync17, readdirSync as readdirSync4, realpathSync } from "fs";
|
|
3507
|
+
import { resolve as resolve17 } from "path";
|
|
3449
3508
|
|
|
3450
3509
|
// packages/runtime/src/control-plane/runtime/sandbox/utils.ts
|
|
3451
3510
|
function uniq(values) {
|
|
@@ -3463,7 +3522,7 @@ function resolveBunBinaryPath() {
|
|
|
3463
3522
|
}
|
|
3464
3523
|
const home = process.env.HOME?.trim();
|
|
3465
3524
|
const fallbackCandidates = [
|
|
3466
|
-
home ?
|
|
3525
|
+
home ? resolve17(home, ".bun/bin/bun") : "",
|
|
3467
3526
|
"/opt/homebrew/bin/bun",
|
|
3468
3527
|
"/usr/local/bin/bun",
|
|
3469
3528
|
"/usr/bin/bun"
|
|
@@ -3491,8 +3550,8 @@ function resolveClaudeBinaryPath() {
|
|
|
3491
3550
|
}
|
|
3492
3551
|
const home = process.env.HOME?.trim();
|
|
3493
3552
|
const fallbackCandidates = [
|
|
3494
|
-
home ?
|
|
3495
|
-
home ?
|
|
3553
|
+
home ? resolve17(home, ".local/bin/claude") : "",
|
|
3554
|
+
home ? resolve17(home, ".local/share/claude/local/claude") : "",
|
|
3496
3555
|
"/opt/homebrew/bin/claude",
|
|
3497
3556
|
"/usr/local/bin/claude",
|
|
3498
3557
|
"/usr/bin/claude"
|
|
@@ -3506,51 +3565,51 @@ function resolveClaudeBinaryPath() {
|
|
|
3506
3565
|
throw new Error("claude not found in PATH");
|
|
3507
3566
|
}
|
|
3508
3567
|
function resolveBunInstallDir(bunBinaryPath = resolveBunBinaryPath()) {
|
|
3509
|
-
return
|
|
3568
|
+
return resolve17(bunBinaryPath, "../..");
|
|
3510
3569
|
}
|
|
3511
3570
|
function resolveClaudeInstallDir() {
|
|
3512
3571
|
const realPath = resolveClaudeBinaryPath();
|
|
3513
|
-
return
|
|
3572
|
+
return resolve17(realPath, "..");
|
|
3514
3573
|
}
|
|
3515
3574
|
function resolveNodeInstallDir() {
|
|
3516
3575
|
const preferredNode = resolvePreferredNodeBinary();
|
|
3517
3576
|
if (!preferredNode)
|
|
3518
3577
|
return null;
|
|
3519
3578
|
const explicitNode = process.env.RIG_NODE_BIN?.trim();
|
|
3520
|
-
if (explicitNode &&
|
|
3521
|
-
return preferredNode.endsWith("/bin/node") ?
|
|
3579
|
+
if (explicitNode && resolve17(explicitNode) === resolve17(preferredNode)) {
|
|
3580
|
+
return preferredNode.endsWith("/bin/node") ? resolve17(preferredNode, "../..") : resolve17(preferredNode, "..");
|
|
3522
3581
|
}
|
|
3523
3582
|
try {
|
|
3524
3583
|
const realPath = realpathSync(preferredNode);
|
|
3525
3584
|
if (realPath.endsWith("/bin/node")) {
|
|
3526
|
-
return
|
|
3585
|
+
return resolve17(realPath, "../..");
|
|
3527
3586
|
}
|
|
3528
|
-
return
|
|
3587
|
+
return resolve17(realPath, "..");
|
|
3529
3588
|
} catch {
|
|
3530
|
-
return
|
|
3589
|
+
return resolve17(preferredNode, "..");
|
|
3531
3590
|
}
|
|
3532
3591
|
}
|
|
3533
3592
|
function resolvePreferredNodeBinary() {
|
|
3534
3593
|
const candidates = [];
|
|
3535
3594
|
const envNode = process.env.RIG_NODE_BIN?.trim();
|
|
3536
3595
|
if (envNode) {
|
|
3537
|
-
const explicit =
|
|
3538
|
-
if (
|
|
3596
|
+
const explicit = resolve17(envNode);
|
|
3597
|
+
if (existsSync17(explicit)) {
|
|
3539
3598
|
return explicit;
|
|
3540
3599
|
}
|
|
3541
3600
|
}
|
|
3542
3601
|
const nvmBin = process.env.NVM_BIN?.trim();
|
|
3543
3602
|
if (nvmBin) {
|
|
3544
|
-
candidates.push(
|
|
3603
|
+
candidates.push(resolve17(nvmBin, "node"));
|
|
3545
3604
|
}
|
|
3546
3605
|
const home = process.env.HOME?.trim();
|
|
3547
3606
|
if (home) {
|
|
3548
|
-
const nvmVersionsDir =
|
|
3549
|
-
if (
|
|
3607
|
+
const nvmVersionsDir = resolve17(home, ".nvm/versions/node");
|
|
3608
|
+
if (existsSync17(nvmVersionsDir)) {
|
|
3550
3609
|
try {
|
|
3551
3610
|
const versionDirs = readdirSync4(nvmVersionsDir).map((entry) => entry.trim()).filter((entry) => /^v\d+\.\d+\.\d+$/.test(entry)).sort((a, b) => Bun.semver.order(b.replace(/^v/, ""), a.replace(/^v/, "")));
|
|
3552
3611
|
for (const versionDir of versionDirs) {
|
|
3553
|
-
candidates.push(
|
|
3612
|
+
candidates.push(resolve17(nvmVersionsDir, versionDir, "bin/node"));
|
|
3554
3613
|
}
|
|
3555
3614
|
} catch {}
|
|
3556
3615
|
}
|
|
@@ -3559,8 +3618,8 @@ function resolvePreferredNodeBinary() {
|
|
|
3559
3618
|
if (whichNode) {
|
|
3560
3619
|
candidates.push(whichNode);
|
|
3561
3620
|
}
|
|
3562
|
-
const deduped = uniq(candidates.map((candidate) =>
|
|
3563
|
-
const existing = deduped.filter((candidate) =>
|
|
3621
|
+
const deduped = uniq(candidates.map((candidate) => resolve17(candidate)));
|
|
3622
|
+
const existing = deduped.filter((candidate) => existsSync17(candidate));
|
|
3564
3623
|
if (existing.length === 0) {
|
|
3565
3624
|
return null;
|
|
3566
3625
|
}
|
|
@@ -3574,7 +3633,7 @@ function resolvePreferredNodeBinary() {
|
|
|
3574
3633
|
return existing[0] ?? null;
|
|
3575
3634
|
}
|
|
3576
3635
|
function inferNodeMajor(nodeBinaryPath) {
|
|
3577
|
-
const normalized =
|
|
3636
|
+
const normalized = resolve17(nodeBinaryPath).replace(/\\/g, "/");
|
|
3578
3637
|
const match = normalized.match(/(?:^|\/)(?:node-)?v?(\d+)\.\d+\.\d+(?:\/|$)/);
|
|
3579
3638
|
if (!match) {
|
|
3580
3639
|
return null;
|
|
@@ -3586,8 +3645,8 @@ function normalizeExecutablePath(candidate) {
|
|
|
3586
3645
|
if (!candidate) {
|
|
3587
3646
|
return "";
|
|
3588
3647
|
}
|
|
3589
|
-
const normalized =
|
|
3590
|
-
if (!
|
|
3648
|
+
const normalized = resolve17(candidate);
|
|
3649
|
+
if (!existsSync17(normalized)) {
|
|
3591
3650
|
return "";
|
|
3592
3651
|
}
|
|
3593
3652
|
try {
|
|
@@ -3597,7 +3656,7 @@ function normalizeExecutablePath(candidate) {
|
|
|
3597
3656
|
}
|
|
3598
3657
|
}
|
|
3599
3658
|
function looksLikeRuntimeGateway(candidate) {
|
|
3600
|
-
const normalized =
|
|
3659
|
+
const normalized = resolve17(candidate).replace(/\\/g, "/");
|
|
3601
3660
|
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
3602
3661
|
}
|
|
3603
3662
|
|
|
@@ -3618,7 +3677,7 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3618
3677
|
try {
|
|
3619
3678
|
return resolveClaudeInstallDir();
|
|
3620
3679
|
} catch {
|
|
3621
|
-
return
|
|
3680
|
+
return resolve18(claudeBinary, "..");
|
|
3622
3681
|
}
|
|
3623
3682
|
})() : "";
|
|
3624
3683
|
const nodeDir = resolveNodeInstallDir();
|
|
@@ -3628,8 +3687,8 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3628
3687
|
`${bunDir}/bin`,
|
|
3629
3688
|
claudeDir,
|
|
3630
3689
|
nodeDir ? `${nodeDir}/bin` : "",
|
|
3631
|
-
realHome ?
|
|
3632
|
-
realHome ?
|
|
3690
|
+
realHome ? resolve18(realHome, ".local/bin") : "",
|
|
3691
|
+
realHome ? resolve18(realHome, ".cargo/bin") : "",
|
|
3633
3692
|
...inheritedPath,
|
|
3634
3693
|
"/usr/local/bin",
|
|
3635
3694
|
"/usr/local/sbin",
|
|
@@ -3660,9 +3719,9 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3660
3719
|
// packages/runtime/src/control-plane/native/validator-binaries.ts
|
|
3661
3720
|
function resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext) {
|
|
3662
3721
|
if (runtimeContext) {
|
|
3663
|
-
return
|
|
3722
|
+
return resolve19(runtimeContext.binDir, "validators", binaryName);
|
|
3664
3723
|
}
|
|
3665
|
-
return
|
|
3724
|
+
return resolve19(resolveHarnessPaths(projectRoot).binDir, "validators", binaryName);
|
|
3666
3725
|
}
|
|
3667
3726
|
async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
3668
3727
|
const match = checkId.match(/^([a-z][\w-]*):([a-z][\w-]*)$/);
|
|
@@ -3677,19 +3736,19 @@ async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
|
3677
3736
|
const binaryName = `${category}-${check}`;
|
|
3678
3737
|
const binaryPath = resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext);
|
|
3679
3738
|
const hostProjectRoot = runtimeContext?.hostProjectRoot?.trim() || projectRoot;
|
|
3680
|
-
const sourcePath =
|
|
3681
|
-
if (!
|
|
3739
|
+
const sourcePath = resolve19(hostProjectRoot, "packages/runtime/src/control-plane/validators", category, `${check}.ts`);
|
|
3740
|
+
if (!existsSync18(sourcePath)) {
|
|
3682
3741
|
return null;
|
|
3683
3742
|
}
|
|
3684
3743
|
const sourceMtime = statSync5(sourcePath).mtimeMs;
|
|
3685
|
-
const binaryExists =
|
|
3744
|
+
const binaryExists = existsSync18(binaryPath);
|
|
3686
3745
|
const binaryMtime = binaryExists ? statSync5(binaryPath).mtimeMs : 0;
|
|
3687
3746
|
if (!binaryExists || sourceMtime > binaryMtime) {
|
|
3688
3747
|
if (binaryExists) {
|
|
3689
3748
|
rmSync5(binaryPath, { force: true });
|
|
3690
3749
|
rmSync5(`${binaryPath}.build-manifest.json`, { force: true });
|
|
3691
3750
|
}
|
|
3692
|
-
|
|
3751
|
+
mkdirSync8(dirname10(binaryPath), { recursive: true });
|
|
3693
3752
|
await buildRuntimeBinary({
|
|
3694
3753
|
sourcePath: `packages/runtime/src/control-plane/validators/${category}/${check}.ts`,
|
|
3695
3754
|
outputPath: binaryPath,
|
|
@@ -3698,7 +3757,7 @@ async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
|
3698
3757
|
env: runtimeProvisioningEnv()
|
|
3699
3758
|
});
|
|
3700
3759
|
}
|
|
3701
|
-
return
|
|
3760
|
+
return existsSync18(binaryPath) ? binaryPath : null;
|
|
3702
3761
|
}
|
|
3703
3762
|
|
|
3704
3763
|
// packages/runtime/src/control-plane/native/validator.ts
|
|
@@ -3735,20 +3794,20 @@ async function readTaskSourceValidation(projectRoot, taskId) {
|
|
|
3735
3794
|
function resolveValidationPaths(projectRoot, taskId, runtimeContext) {
|
|
3736
3795
|
if (runtimeContext) {
|
|
3737
3796
|
return {
|
|
3738
|
-
taskLogDir:
|
|
3739
|
-
artifactDir:
|
|
3797
|
+
taskLogDir: resolve20(runtimeContext.logsDir, taskId),
|
|
3798
|
+
artifactDir: resolve20(runtimeContext.workspaceDir, "artifacts", taskId)
|
|
3740
3799
|
};
|
|
3741
3800
|
}
|
|
3742
3801
|
const paths = resolveHarnessPaths(projectRoot);
|
|
3743
3802
|
return {
|
|
3744
|
-
taskLogDir:
|
|
3745
|
-
artifactDir:
|
|
3803
|
+
taskLogDir: resolve20(paths.logsDir, taskId),
|
|
3804
|
+
artifactDir: resolve20(paths.artifactsDir, taskId)
|
|
3746
3805
|
};
|
|
3747
3806
|
}
|
|
3748
3807
|
async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext) {
|
|
3749
3808
|
const binaryName = checkId.replace(":", "-");
|
|
3750
3809
|
const binaryPath = await ensureValidatorBinary(projectRoot, checkId, runtimeContext) ?? resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext);
|
|
3751
|
-
if (!
|
|
3810
|
+
if (!existsSync19(binaryPath)) {
|
|
3752
3811
|
return {
|
|
3753
3812
|
result: {
|
|
3754
3813
|
id: checkId,
|
|
@@ -3759,7 +3818,7 @@ async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext)
|
|
|
3759
3818
|
};
|
|
3760
3819
|
}
|
|
3761
3820
|
const validatorCwd = runtimeContext?.workspaceDir || resolveMonorepoRoot(projectRoot);
|
|
3762
|
-
const runtimeShellPath = runtimeContext ?
|
|
3821
|
+
const runtimeShellPath = runtimeContext ? resolve20(runtimeContext.binDir, "rig-shell") : "";
|
|
3763
3822
|
const monorepoMainRoot = runtimeContext?.monorepoMainRoot || process.env.MONOREPO_MAIN_ROOT?.trim() || resolveMonorepoRoot(projectRoot);
|
|
3764
3823
|
const validatorEnv = {
|
|
3765
3824
|
PROJECT_RIG_ROOT: runtimeContext?.hostProjectRoot || projectRoot,
|
|
@@ -3774,7 +3833,7 @@ async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext)
|
|
|
3774
3833
|
validatorEnv.RIG_LOGS_DIR = runtimeContext.logsDir;
|
|
3775
3834
|
validatorEnv.RIG_RUNTIME_BIN_DIR = runtimeContext.binDir;
|
|
3776
3835
|
}
|
|
3777
|
-
const { exitCode, stdout, stderr } = await runCaptureAsync(runtimeShellPath &&
|
|
3836
|
+
const { exitCode, stdout, stderr } = await runCaptureAsync(runtimeShellPath && existsSync19(runtimeShellPath) ? [runtimeShellPath, "run-binary", binaryPath] : [binaryPath], validatorCwd, validatorEnv);
|
|
3778
3837
|
try {
|
|
3779
3838
|
const result = JSON.parse(stdout.trim());
|
|
3780
3839
|
return { result, exitCode };
|
|
@@ -3814,8 +3873,8 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3814
3873
|
const configuredValidation = stringArray(taskConfig[taskId]?.validation);
|
|
3815
3874
|
const commands = resolvedContext?.validation?.length ? resolvedContext.validation : configuredValidation.length > 0 ? configuredValidation : sourceValidation.validation;
|
|
3816
3875
|
const { taskLogDir, artifactDir } = resolveValidationPaths(projectRoot, taskId, resolvedContext);
|
|
3817
|
-
|
|
3818
|
-
|
|
3876
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3877
|
+
mkdirSync9(artifactDir, { recursive: true });
|
|
3819
3878
|
if (commands.length === 0) {
|
|
3820
3879
|
const skipped = {
|
|
3821
3880
|
status: "skipped",
|
|
@@ -3824,7 +3883,7 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3824
3883
|
failed: 0,
|
|
3825
3884
|
categories: []
|
|
3826
3885
|
};
|
|
3827
|
-
|
|
3886
|
+
writeFileSync9(resolve20(artifactDir, "validation-summary.json"), `${JSON.stringify(skipped, null, 2)}
|
|
3828
3887
|
`, "utf-8");
|
|
3829
3888
|
return skipped;
|
|
3830
3889
|
}
|
|
@@ -3859,18 +3918,18 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3859
3918
|
exit_code: 2,
|
|
3860
3919
|
duration_seconds: 0
|
|
3861
3920
|
});
|
|
3862
|
-
const logFile2 =
|
|
3863
|
-
|
|
3864
|
-
|
|
3921
|
+
const logFile2 = resolve20(taskLogDir, `invalid-entry-validation.log`);
|
|
3922
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3923
|
+
writeFileSync9(logFile2, `=== ${nowIso()} :: ${cmd} ===
|
|
3865
3924
|
Invalid validation entry: not a check-ID. All entries must use format "category:check-name".
|
|
3866
3925
|
`, "utf-8");
|
|
3867
3926
|
continue;
|
|
3868
3927
|
}
|
|
3869
3928
|
const { result, exitCode } = await dispatchValidator(cmd, effectiveRegistry, validatorCtx, (id) => runValidatorBinary(projectRoot, taskId, id, resolvedContext));
|
|
3870
3929
|
const durationSeconds = Math.max(0, Math.round((Date.now() - startedAt) / 1000));
|
|
3871
|
-
const logFile =
|
|
3872
|
-
|
|
3873
|
-
|
|
3930
|
+
const logFile = resolve20(taskLogDir, `${cmd.replace(":", "-")}-validation.log`);
|
|
3931
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3932
|
+
writeFileSync9(logFile, `=== ${nowIso()} :: ${cmd} ===
|
|
3874
3933
|
${JSON.stringify(result, null, 2)}
|
|
3875
3934
|
`, "utf-8");
|
|
3876
3935
|
if (result.passed) {
|
|
@@ -3892,19 +3951,19 @@ ${JSON.stringify(result, null, 2)}
|
|
|
3892
3951
|
failed,
|
|
3893
3952
|
categories
|
|
3894
3953
|
};
|
|
3895
|
-
|
|
3896
|
-
|
|
3954
|
+
mkdirSync9(artifactDir, { recursive: true });
|
|
3955
|
+
writeFileSync9(resolve20(artifactDir, "validation-summary.json"), `${JSON.stringify(summary, null, 2)}
|
|
3897
3956
|
`, "utf-8");
|
|
3898
3957
|
return summary;
|
|
3899
3958
|
}
|
|
3900
3959
|
|
|
3901
3960
|
// packages/runtime/src/control-plane/native/verifier.ts
|
|
3902
|
-
import { existsSync as
|
|
3903
|
-
import { resolve as
|
|
3961
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
3962
|
+
import { resolve as resolve22 } from "path";
|
|
3904
3963
|
|
|
3905
3964
|
// packages/runtime/src/control-plane/native/pr-review-gate.ts
|
|
3906
|
-
import { mkdirSync as
|
|
3907
|
-
import { resolve as
|
|
3965
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
3966
|
+
import { resolve as resolve21 } from "path";
|
|
3908
3967
|
function parseJsonObject(value) {
|
|
3909
3968
|
if (!value?.trim())
|
|
3910
3969
|
return { value: {}, error: "empty JSON output" };
|
|
@@ -5228,34 +5287,34 @@ function buildStrictPrGateSteeringPrompt(result) {
|
|
|
5228
5287
|
}
|
|
5229
5288
|
function persistPrReviewCycleArtifacts(input) {
|
|
5230
5289
|
const cycleName = input.final ? `${input.cycle}-final` : String(input.cycle);
|
|
5231
|
-
const taskArtifactRoot = input.artifactRoot?.trim() ? input.artifactRoot :
|
|
5232
|
-
const root =
|
|
5233
|
-
|
|
5234
|
-
const finalMergeGateResultPath = input.final ?
|
|
5290
|
+
const taskArtifactRoot = input.artifactRoot?.trim() ? input.artifactRoot : resolve21(input.projectRoot, "artifacts", input.taskId);
|
|
5291
|
+
const root = resolve21(taskArtifactRoot, "pr-review-cycles", cycleName);
|
|
5292
|
+
mkdirSync10(root, { recursive: true });
|
|
5293
|
+
const finalMergeGateResultPath = input.final ? resolve21(taskArtifactRoot, "merge-gate-final.json") : undefined;
|
|
5235
5294
|
const paths = {
|
|
5236
5295
|
root,
|
|
5237
|
-
prTitlePath:
|
|
5238
|
-
prBodyPath:
|
|
5239
|
-
prCommentsPath:
|
|
5240
|
-
reviewThreadsPath:
|
|
5241
|
-
reviewCommentsPath:
|
|
5242
|
-
checkRollupPath:
|
|
5243
|
-
greptileEvidencePath:
|
|
5244
|
-
mergeGateResultPath:
|
|
5245
|
-
steeringPromptPath:
|
|
5296
|
+
prTitlePath: resolve21(root, "pr-title.md"),
|
|
5297
|
+
prBodyPath: resolve21(root, "pr-body.md"),
|
|
5298
|
+
prCommentsPath: resolve21(root, "pr-comments.json"),
|
|
5299
|
+
reviewThreadsPath: resolve21(root, "review-threads.json"),
|
|
5300
|
+
reviewCommentsPath: resolve21(root, "review-comments.json"),
|
|
5301
|
+
checkRollupPath: resolve21(root, "check-rollup.json"),
|
|
5302
|
+
greptileEvidencePath: resolve21(root, "greptile-evidence.json"),
|
|
5303
|
+
mergeGateResultPath: resolve21(root, "merge-gate-result.json"),
|
|
5304
|
+
steeringPromptPath: resolve21(root, "agent-steering-prompt.md"),
|
|
5246
5305
|
...finalMergeGateResultPath ? { finalMergeGateResultPath } : {}
|
|
5247
5306
|
};
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5307
|
+
writeFileSync10(paths.prTitlePath, input.result.evidence.title || "", "utf8");
|
|
5308
|
+
writeFileSync10(paths.prBodyPath, input.result.evidence.body || "", "utf8");
|
|
5309
|
+
writeFileSync10(paths.prCommentsPath, `${JSON.stringify(input.result.evidence.relevantIssueComments, null, 2)}
|
|
5251
5310
|
`, "utf8");
|
|
5252
|
-
|
|
5311
|
+
writeFileSync10(paths.reviewThreadsPath, `${JSON.stringify(input.result.evidence.reviewThreads, null, 2)}
|
|
5253
5312
|
`, "utf8");
|
|
5254
|
-
|
|
5313
|
+
writeFileSync10(paths.reviewCommentsPath, `${JSON.stringify(input.result.evidence.changedFileReviewComments, null, 2)}
|
|
5255
5314
|
`, "utf8");
|
|
5256
|
-
|
|
5315
|
+
writeFileSync10(paths.checkRollupPath, `${JSON.stringify(input.result.evidence.statusCheckRollup, null, 2)}
|
|
5257
5316
|
`, "utf8");
|
|
5258
|
-
|
|
5317
|
+
writeFileSync10(paths.greptileEvidencePath, `${JSON.stringify(input.result.evidence.greptile, null, 2)}
|
|
5259
5318
|
`, "utf8");
|
|
5260
5319
|
const mergeGatePayload = {
|
|
5261
5320
|
approved: input.result.approved,
|
|
@@ -5272,13 +5331,13 @@ function persistPrReviewCycleArtifacts(input) {
|
|
|
5272
5331
|
evidence: input.result.evidence,
|
|
5273
5332
|
cycleArtifactRoot: root
|
|
5274
5333
|
};
|
|
5275
|
-
|
|
5334
|
+
writeFileSync10(paths.mergeGateResultPath, `${JSON.stringify(mergeGatePayload, null, 2)}
|
|
5276
5335
|
`, "utf8");
|
|
5277
5336
|
if (paths.finalMergeGateResultPath) {
|
|
5278
|
-
|
|
5337
|
+
writeFileSync10(paths.finalMergeGateResultPath, `${JSON.stringify(mergeGatePayload, null, 2)}
|
|
5279
5338
|
`, "utf8");
|
|
5280
5339
|
}
|
|
5281
|
-
|
|
5340
|
+
writeFileSync10(paths.steeringPromptPath, input.steeringPrompt, "utf8");
|
|
5282
5341
|
return paths;
|
|
5283
5342
|
}
|
|
5284
5343
|
async function runStrictPrMergeGate(input) {
|
|
@@ -5295,7 +5354,7 @@ async function runStrictPrMergeGate(input) {
|
|
|
5295
5354
|
final: input.final
|
|
5296
5355
|
});
|
|
5297
5356
|
const steeringPrompt = buildStrictPrGateSteeringPrompt({ ...base, artifacts });
|
|
5298
|
-
|
|
5357
|
+
writeFileSync10(artifacts.steeringPromptPath, steeringPrompt, "utf8");
|
|
5299
5358
|
return { ...base, artifacts, steeringPrompt };
|
|
5300
5359
|
}
|
|
5301
5360
|
|
|
@@ -5305,11 +5364,11 @@ async function verifyTask(options) {
|
|
|
5305
5364
|
const taskId = options.taskId;
|
|
5306
5365
|
const normalizedTaskId = lookupTask(options.projectRoot, taskId);
|
|
5307
5366
|
const artifactDir = artifactDirForId(options.projectRoot, taskId);
|
|
5308
|
-
|
|
5309
|
-
const validationSummaryPath =
|
|
5310
|
-
const reviewFeedbackPath =
|
|
5311
|
-
const reviewStatePath =
|
|
5312
|
-
const greptileRawPath =
|
|
5367
|
+
mkdirSync11(artifactDir, { recursive: true });
|
|
5368
|
+
const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
|
|
5369
|
+
const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
|
|
5370
|
+
const reviewStatePath = resolve22(artifactDir, "review-state.json");
|
|
5371
|
+
const greptileRawPath = resolve22(artifactDir, "review-greptile-raw.json");
|
|
5313
5372
|
const prStates = readPrMetadata(options.projectRoot, taskId);
|
|
5314
5373
|
const prState = prStates[0] || null;
|
|
5315
5374
|
const localReasons = [];
|
|
@@ -5321,7 +5380,7 @@ async function verifyTask(options) {
|
|
|
5321
5380
|
if (!normalizedTaskId && !await hasConfiguredSourceTask(options.projectRoot, taskId)) {
|
|
5322
5381
|
localReasons.push(`[Task Config] Unknown task id '${taskId}' in task-config or configured task source.`);
|
|
5323
5382
|
}
|
|
5324
|
-
if (!
|
|
5383
|
+
if (!existsSync20(validationSummaryPath)) {
|
|
5325
5384
|
localReasons.push(`[Artifact Quality] validation-summary.json not found at ${validationSummaryPath}.`);
|
|
5326
5385
|
} else {
|
|
5327
5386
|
const summary = await parseValidationSummary(validationSummaryPath);
|
|
@@ -5330,13 +5389,13 @@ async function verifyTask(options) {
|
|
|
5330
5389
|
}
|
|
5331
5390
|
}
|
|
5332
5391
|
for (const file of ["task-result.json", "decision-log.md", "next-actions.md", "changed-files.txt"]) {
|
|
5333
|
-
const requiredPath =
|
|
5334
|
-
if (!
|
|
5392
|
+
const requiredPath = resolve22(artifactDir, file);
|
|
5393
|
+
if (!existsSync20(requiredPath)) {
|
|
5335
5394
|
localReasons.push(`[Artifact Quality] Missing required artifact file: ${requiredPath}`);
|
|
5336
5395
|
}
|
|
5337
5396
|
}
|
|
5338
|
-
const taskResultPath =
|
|
5339
|
-
if (
|
|
5397
|
+
const taskResultPath = resolve22(artifactDir, "task-result.json");
|
|
5398
|
+
if (existsSync20(taskResultPath)) {
|
|
5340
5399
|
const taskResult = await readJsonFile2(taskResultPath);
|
|
5341
5400
|
const artifactStatus = typeof taskResult?.status === "string" ? taskResult.status.trim().toLowerCase() : "";
|
|
5342
5401
|
if (artifactStatus === "partial") {
|
|
@@ -5349,8 +5408,8 @@ async function verifyTask(options) {
|
|
|
5349
5408
|
localReasons.push("[Artifact Quality] task-result.json next actions indicate remaining implementation scope.");
|
|
5350
5409
|
}
|
|
5351
5410
|
}
|
|
5352
|
-
const nextActionsPath =
|
|
5353
|
-
if (
|
|
5411
|
+
const nextActionsPath = resolve22(artifactDir, "next-actions.md");
|
|
5412
|
+
if (existsSync20(nextActionsPath)) {
|
|
5354
5413
|
const nextActionsContent = await Bun.file(nextActionsPath).text();
|
|
5355
5414
|
if (nextActionsContent.includes("TODO: Replace this scaffold") || nextActionsContent.includes("bd-<downstream-task-id>")) {
|
|
5356
5415
|
localReasons.push("[Artifact Quality] next-actions.md still contains scaffold placeholder text. Replace with real recommendations.");
|
|
@@ -5381,7 +5440,7 @@ async function verifyTask(options) {
|
|
|
5381
5440
|
aiReasons.push(`[AI Review] Required mode needs a completed Greptile approval; current verdict is ${ai.verdict}.`);
|
|
5382
5441
|
}
|
|
5383
5442
|
if (persistArtifacts && ai.rawResponse) {
|
|
5384
|
-
|
|
5443
|
+
writeFileSync11(greptileRawPath, `${ai.rawResponse}
|
|
5385
5444
|
`, "utf-8");
|
|
5386
5445
|
}
|
|
5387
5446
|
} else if (!options.skipAiReview && reviewMode === "off") {
|
|
@@ -5624,7 +5683,7 @@ function evaluateSourceCloseoutChecks(prState, prLabel) {
|
|
|
5624
5683
|
if (!Array.isArray(checks) || checks.length === 0) {
|
|
5625
5684
|
return [`[Source Issue] PR ${prLabel} must have green check evidence before completion.`];
|
|
5626
5685
|
}
|
|
5627
|
-
const ciGate = evaluatePullRequestCiChecks(checks, "PR", 0);
|
|
5686
|
+
const ciGate = evaluatePullRequestCiChecks(checks, "PR", 0, { mergeStateStatus: prState.mergeStateStatus });
|
|
5628
5687
|
if (ciGate.verdict === "APPROVE") {
|
|
5629
5688
|
return [];
|
|
5630
5689
|
}
|
|
@@ -5720,7 +5779,7 @@ function isAcceptedValidationSummary(summary) {
|
|
|
5720
5779
|
return summary.status === "skipped" && summary.total === 0 && summary.failed === 0;
|
|
5721
5780
|
}
|
|
5722
5781
|
async function loadReviewMode(reviewProfilePath, fallback) {
|
|
5723
|
-
const parsed =
|
|
5782
|
+
const parsed = existsSync20(reviewProfilePath) ? await readJsonFile2(reviewProfilePath) : null;
|
|
5724
5783
|
const mode = parsed?.mode;
|
|
5725
5784
|
if (mode === "off" || mode === "advisory" || mode === "required") {
|
|
5726
5785
|
return mode;
|
|
@@ -5731,7 +5790,7 @@ async function loadReviewMode(reviewProfilePath, fallback) {
|
|
|
5731
5790
|
return "advisory";
|
|
5732
5791
|
}
|
|
5733
5792
|
async function loadReviewProvider(reviewProfilePath, fallback) {
|
|
5734
|
-
const parsed =
|
|
5793
|
+
const parsed = existsSync20(reviewProfilePath) ? await readJsonFile2(reviewProfilePath) : null;
|
|
5735
5794
|
const provider = parsed?.provider;
|
|
5736
5795
|
if (typeof provider === "string" && provider.trim().length > 0) {
|
|
5737
5796
|
return provider;
|
|
@@ -5890,7 +5949,7 @@ function writeFeedbackFile(options) {
|
|
|
5890
5949
|
if (options.aiRawFeedback) {
|
|
5891
5950
|
lines.push("## Raw Reviewer Feedback", "", "```text", options.aiRawFeedback, "```", "");
|
|
5892
5951
|
}
|
|
5893
|
-
|
|
5952
|
+
writeFileSync11(options.output, `${lines.join(`
|
|
5894
5953
|
`)}
|
|
5895
5954
|
`, "utf-8");
|
|
5896
5955
|
}
|
|
@@ -5907,7 +5966,7 @@ function writeReviewStateFile(options) {
|
|
|
5907
5966
|
ai_warnings: options.aiWarnings,
|
|
5908
5967
|
updated_at: nowIso()
|
|
5909
5968
|
};
|
|
5910
|
-
|
|
5969
|
+
writeFileSync11(options.output, `${JSON.stringify(payload, null, 2)}
|
|
5911
5970
|
`, "utf-8");
|
|
5912
5971
|
}
|
|
5913
5972
|
async function runGreptileReviewForPr(options) {
|
|
@@ -5988,7 +6047,7 @@ async function runGreptileReviewForPr(options) {
|
|
|
5988
6047
|
}
|
|
5989
6048
|
await Bun.sleep(options.pollIntervalMs);
|
|
5990
6049
|
}
|
|
5991
|
-
const ciGate = evaluatePullRequestCiChecks(githubCheckRollup, repoName, prNumber);
|
|
6050
|
+
const ciGate = evaluatePullRequestCiChecks(githubCheckRollup, repoName, prNumber, { mergeStateStatus: options.prState.mergeStateStatus });
|
|
5992
6051
|
if (ciGate.verdict !== "APPROVE") {
|
|
5993
6052
|
return {
|
|
5994
6053
|
verdict: ciGate.verdict,
|
|
@@ -6246,7 +6305,7 @@ async function runGithubGreptileFallbackReviewForPr(options) {
|
|
|
6246
6305
|
}
|
|
6247
6306
|
await Bun.sleep(options.pollIntervalMs);
|
|
6248
6307
|
}
|
|
6249
|
-
const ciGate = evaluatePullRequestCiChecks(checkRollup, repoName, prNumber);
|
|
6308
|
+
const ciGate = evaluatePullRequestCiChecks(checkRollup, repoName, prNumber, { mergeStateStatus: options.prState.mergeStateStatus });
|
|
6250
6309
|
if (ciGate.verdict !== "APPROVE") {
|
|
6251
6310
|
return {
|
|
6252
6311
|
verdict: ciGate.verdict,
|
|
@@ -6601,7 +6660,7 @@ function loadGithubPullRequestCheckRollup(projectRoot, repoName, prNumber) {
|
|
|
6601
6660
|
]);
|
|
6602
6661
|
return response.statusCheckRollup || [];
|
|
6603
6662
|
}
|
|
6604
|
-
function evaluatePullRequestCiChecks(checks, repoName, prNumber) {
|
|
6663
|
+
function evaluatePullRequestCiChecks(checks, repoName, prNumber, options = {}) {
|
|
6605
6664
|
const nonGreptileChecks = checks.filter((check) => {
|
|
6606
6665
|
const label = (check.name || check.context || "").toLowerCase();
|
|
6607
6666
|
return label.length > 0 && !label.includes("greptile");
|
|
@@ -6613,7 +6672,8 @@ function evaluatePullRequestCiChecks(checks, repoName, prNumber) {
|
|
|
6613
6672
|
const state = (check.state || check.status || "").toUpperCase();
|
|
6614
6673
|
return state === "PENDING" || state === "EXPECTED" || state === "QUEUED" || state === "IN_PROGRESS";
|
|
6615
6674
|
});
|
|
6616
|
-
|
|
6675
|
+
const mergeClean = (options.mergeStateStatus || "").toUpperCase() === "CLEAN";
|
|
6676
|
+
if (pending.length > 0 && !mergeClean) {
|
|
6617
6677
|
return {
|
|
6618
6678
|
verdict: "SKIP",
|
|
6619
6679
|
reasons: pending.map((check) => `[CI] ${repoName}#${prNumber} check is still pending: ${check.name || check.context || "unknown"}.`)
|
|
@@ -6694,7 +6754,7 @@ function filterActionableGithubGreptileThreads(threads) {
|
|
|
6694
6754
|
}
|
|
6695
6755
|
function resolvePrRepoRoot(projectRoot, prState) {
|
|
6696
6756
|
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
6697
|
-
if (prState.target === "monorepo" && runtimeWorkspace &&
|
|
6757
|
+
if (prState.target === "monorepo" && runtimeWorkspace && existsSync20(resolve22(runtimeWorkspace, ".git"))) {
|
|
6698
6758
|
return runtimeWorkspace;
|
|
6699
6759
|
}
|
|
6700
6760
|
const paths = resolveHarnessPaths(projectRoot);
|
|
@@ -6762,40 +6822,40 @@ function taskArtifacts(projectRoot, taskId) {
|
|
|
6762
6822
|
throw new Error("No active task.");
|
|
6763
6823
|
}
|
|
6764
6824
|
const paths = resolveHarnessPaths(projectRoot);
|
|
6765
|
-
const artifactDir =
|
|
6766
|
-
|
|
6825
|
+
const artifactDir = resolve23(paths.artifactsDir, activeTask);
|
|
6826
|
+
mkdirSync12(artifactDir, { recursive: true });
|
|
6767
6827
|
const changed = changedFilesForTask(projectRoot, activeTask, true);
|
|
6768
|
-
|
|
6828
|
+
writeFileSync12(resolve23(artifactDir, "changed-files.txt"), `${changed.join(`
|
|
6769
6829
|
`)}
|
|
6770
6830
|
`, "utf-8");
|
|
6771
6831
|
console.log(`changed-files.txt: ${changed.length} files`);
|
|
6772
|
-
const taskResultPath =
|
|
6773
|
-
if (!
|
|
6832
|
+
const taskResultPath = resolve23(artifactDir, "task-result.json");
|
|
6833
|
+
if (!existsSync21(taskResultPath)) {
|
|
6774
6834
|
const template = {
|
|
6775
6835
|
task_id: activeTask,
|
|
6776
6836
|
status: "completed",
|
|
6777
6837
|
summary: "TODO: Write a one-line summary of what you did",
|
|
6778
6838
|
completed_at: nowIso()
|
|
6779
6839
|
};
|
|
6780
|
-
|
|
6840
|
+
writeFileSync12(taskResultPath, `${JSON.stringify(template, null, 2)}
|
|
6781
6841
|
`, "utf-8");
|
|
6782
6842
|
console.log("task-result.json: created (update the summary!)");
|
|
6783
6843
|
} else {
|
|
6784
6844
|
console.log("task-result.json: already exists");
|
|
6785
6845
|
}
|
|
6786
|
-
const decisionLogPath =
|
|
6787
|
-
if (!
|
|
6846
|
+
const decisionLogPath = resolve23(artifactDir, "decision-log.md");
|
|
6847
|
+
if (!existsSync21(decisionLogPath)) {
|
|
6788
6848
|
const content = `# Decision Log: ${activeTask}
|
|
6789
6849
|
|
|
6790
6850
|
Record key decisions here using: rig-agent record decision "..."
|
|
6791
6851
|
`;
|
|
6792
|
-
|
|
6852
|
+
writeFileSync12(decisionLogPath, content, "utf-8");
|
|
6793
6853
|
console.log("decision-log.md: created (record your decisions!)");
|
|
6794
6854
|
} else {
|
|
6795
6855
|
console.log("decision-log.md: already exists");
|
|
6796
6856
|
}
|
|
6797
|
-
const nextActionsPath =
|
|
6798
|
-
if (!
|
|
6857
|
+
const nextActionsPath = resolve23(artifactDir, "next-actions.md");
|
|
6858
|
+
if (!existsSync21(nextActionsPath)) {
|
|
6799
6859
|
const content = [
|
|
6800
6860
|
`# Next Actions: ${activeTask}`,
|
|
6801
6861
|
"",
|
|
@@ -6812,13 +6872,13 @@ Record key decisions here using: rig-agent record decision "..."
|
|
|
6812
6872
|
""
|
|
6813
6873
|
].join(`
|
|
6814
6874
|
`);
|
|
6815
|
-
|
|
6875
|
+
writeFileSync12(nextActionsPath, content, "utf-8");
|
|
6816
6876
|
console.log("next-actions.md: created (add recommendations for downstream tasks!)");
|
|
6817
6877
|
} else {
|
|
6818
6878
|
console.log("next-actions.md: already exists");
|
|
6819
6879
|
}
|
|
6820
|
-
const validationSummaryPath =
|
|
6821
|
-
if (
|
|
6880
|
+
const validationSummaryPath = resolve23(artifactDir, "validation-summary.json");
|
|
6881
|
+
if (existsSync21(validationSummaryPath)) {
|
|
6822
6882
|
console.log("validation-summary.json: already exists");
|
|
6823
6883
|
} else {
|
|
6824
6884
|
console.log("validation-summary.json: not yet created (run: rig-agent validate)");
|
|
@@ -6884,7 +6944,7 @@ function collectTaskChangedFiles(projectRoot, taskId, includeCommitted) {
|
|
|
6884
6944
|
[projectRoot, ""],
|
|
6885
6945
|
[monorepoRepoRoot, ""]
|
|
6886
6946
|
]) {
|
|
6887
|
-
if (!
|
|
6947
|
+
if (!existsSync21(resolve23(repo, ".git"))) {
|
|
6888
6948
|
continue;
|
|
6889
6949
|
}
|
|
6890
6950
|
if (includeCommitted && repo === monorepoRepoRoot) {
|
|
@@ -6909,12 +6969,22 @@ function filterTaskChangedFiles(projectRoot, taskId, files, scoped) {
|
|
|
6909
6969
|
}
|
|
6910
6970
|
function resolveTaskMonorepoRoot(projectRoot) {
|
|
6911
6971
|
const runtimeWorkspace = loadRuntimeContextFromEnv()?.workspaceDir || process.env.RIG_TASK_WORKSPACE?.trim();
|
|
6912
|
-
if (runtimeWorkspace &&
|
|
6913
|
-
return
|
|
6972
|
+
if (runtimeWorkspace && existsSync21(resolve23(runtimeWorkspace, ".git"))) {
|
|
6973
|
+
return resolve23(runtimeWorkspace);
|
|
6914
6974
|
}
|
|
6915
6975
|
return resolveHarnessPaths(projectRoot).monorepoRoot;
|
|
6916
6976
|
}
|
|
6917
6977
|
function collectCommittedMonorepoFiles(projectRoot, repo) {
|
|
6978
|
+
const defaultRef = runCapture(["git", "-C", repo, "rev-parse", "--abbrev-ref", "origin/HEAD"], projectRoot);
|
|
6979
|
+
if (defaultRef.exitCode === 0 && defaultRef.stdout.trim()) {
|
|
6980
|
+
const mergeBase = runCapture(["git", "-C", repo, "merge-base", "HEAD", defaultRef.stdout.trim()], projectRoot);
|
|
6981
|
+
if (mergeBase.exitCode === 0 && mergeBase.stdout.trim()) {
|
|
6982
|
+
const result = runCapture(["git", "-C", repo, "diff", "--name-only", `${mergeBase.stdout.trim()}..HEAD`], projectRoot);
|
|
6983
|
+
if (result.exitCode === 0) {
|
|
6984
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
6985
|
+
}
|
|
6986
|
+
}
|
|
6987
|
+
}
|
|
6918
6988
|
const initialHeadCommit = resolveRuntimeInitialHeadCommit(projectRoot, repo);
|
|
6919
6989
|
if (initialHeadCommit) {
|
|
6920
6990
|
const result = runCapture(["git", "-C", repo, "diff", "--name-only", `${initialHeadCommit}..HEAD`], projectRoot);
|
|
@@ -6938,7 +7008,7 @@ function resolveRuntimeInitialHeadCommit(projectRoot, repo) {
|
|
|
6938
7008
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
6939
7009
|
if (runtimeContext?.initialHeadCommits?.monorepo?.trim()) {
|
|
6940
7010
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6941
|
-
if (
|
|
7011
|
+
if (resolve23(monorepoRoot) === resolve23(repo)) {
|
|
6942
7012
|
return runtimeContext.initialHeadCommits.monorepo.trim();
|
|
6943
7013
|
}
|
|
6944
7014
|
}
|
|
@@ -6948,7 +7018,7 @@ function resolveMonorepoBaseCommit(projectRoot, repo) {
|
|
|
6948
7018
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
6949
7019
|
if (runtimeContext?.monorepoBaseCommit?.trim()) {
|
|
6950
7020
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6951
|
-
if (
|
|
7021
|
+
if (resolve23(monorepoRoot) === resolve23(repo)) {
|
|
6952
7022
|
return runtimeContext.monorepoBaseCommit.trim();
|
|
6953
7023
|
}
|
|
6954
7024
|
}
|
|
@@ -6993,7 +7063,7 @@ function resolveRuntimeDirtyBaseline(projectRoot, repo) {
|
|
|
6993
7063
|
return new Set;
|
|
6994
7064
|
}
|
|
6995
7065
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6996
|
-
const selected =
|
|
7066
|
+
const selected = resolve23(repo) === resolve23(monorepoRoot) ? dirtyFiles.monorepo : resolve23(repo) === resolve23(projectRoot) ? dirtyFiles.project : undefined;
|
|
6997
7067
|
return new Set((selected || []).map((file) => normalizeChangedFilePath(file)).filter(Boolean));
|
|
6998
7068
|
}
|
|
6999
7069
|
function normalizeChangedFilePath(file) {
|
|
@@ -7051,8 +7121,8 @@ function isRuntimeGatewayGhPath(candidate) {
|
|
|
7051
7121
|
}
|
|
7052
7122
|
function resolveOptionalMonorepoRoot(projectRoot) {
|
|
7053
7123
|
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
7054
|
-
if (runtimeWorkspace &&
|
|
7055
|
-
return
|
|
7124
|
+
if (runtimeWorkspace && existsSync22(resolve24(runtimeWorkspace, ".git"))) {
|
|
7125
|
+
return resolve24(runtimeWorkspace);
|
|
7056
7126
|
}
|
|
7057
7127
|
try {
|
|
7058
7128
|
return resolveMonorepoRoot2(projectRoot);
|
|
@@ -7077,7 +7147,7 @@ function resolveGitBinary(projectRoot) {
|
|
|
7077
7147
|
if (!candidate || isRuntimeGatewayGitPath(candidate)) {
|
|
7078
7148
|
continue;
|
|
7079
7149
|
}
|
|
7080
|
-
if (
|
|
7150
|
+
if (existsSync22(candidate)) {
|
|
7081
7151
|
return candidate;
|
|
7082
7152
|
}
|
|
7083
7153
|
}
|
|
@@ -7104,7 +7174,7 @@ function gitSyncBranch(projectRoot, taskId, targetRepo = "monorepo") {
|
|
|
7104
7174
|
}
|
|
7105
7175
|
const repoRoot = targetRepo === "monorepo" ? resolveOptionalMonorepoRoot(projectRoot) || resolveMonorepoRoot2(projectRoot) : projectRoot;
|
|
7106
7176
|
const repoLabel = targetRepo === "monorepo" ? "Monorepo" : "Project";
|
|
7107
|
-
if (!
|
|
7177
|
+
if (!existsSync22(resolve24(repoRoot, ".git"))) {
|
|
7108
7178
|
throw new Error(`${repoLabel} repo not found at ${repoRoot}`);
|
|
7109
7179
|
}
|
|
7110
7180
|
const branchId = resolveTaskBranchId(projectRoot, resolvedTask);
|
|
@@ -7164,7 +7234,7 @@ function gitOpenPr(options) {
|
|
|
7164
7234
|
} else if (taskId) {
|
|
7165
7235
|
gitSyncBranch(options.projectRoot, taskId, "project");
|
|
7166
7236
|
}
|
|
7167
|
-
if (!
|
|
7237
|
+
if (!existsSync22(resolve24(repoRoot, ".git"))) {
|
|
7168
7238
|
throw new Error(`Repository not available for open-pr target ${target}: ${repoRoot}`);
|
|
7169
7239
|
}
|
|
7170
7240
|
const branch = branchName(options.projectRoot, repoRoot);
|
|
@@ -7397,7 +7467,7 @@ function gitMergePr(options) {
|
|
|
7397
7467
|
}
|
|
7398
7468
|
const repoRoot = resolveRepoRoot(options.projectRoot, options.pr.target);
|
|
7399
7469
|
const repoNameWithOwner = resolveRepoNameWithOwner(options.projectRoot, repoRoot);
|
|
7400
|
-
if (!
|
|
7470
|
+
if (!existsSync22(resolve24(repoRoot, ".git"))) {
|
|
7401
7471
|
throw new Error(`Repository not available for merge-pr target ${options.pr.target}: ${repoRoot}`);
|
|
7402
7472
|
}
|
|
7403
7473
|
const prState = readPrViewState(gh, repoRoot, repoNameWithOwner, options.pr.url);
|
|
@@ -7458,12 +7528,12 @@ function assertPrHasNoGitConflicts(prState, repoLabel, baseRef) {
|
|
|
7458
7528
|
}
|
|
7459
7529
|
function writePrMetadata(projectRoot, taskId, result) {
|
|
7460
7530
|
const dir = artifactDirForId(projectRoot, taskId);
|
|
7461
|
-
|
|
7462
|
-
const path =
|
|
7531
|
+
mkdirSync13(dir, { recursive: true });
|
|
7532
|
+
const path = resolve24(dir, "pr-state.json");
|
|
7463
7533
|
let prs = {};
|
|
7464
|
-
if (
|
|
7534
|
+
if (existsSync22(path)) {
|
|
7465
7535
|
try {
|
|
7466
|
-
const parsed = JSON.parse(
|
|
7536
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
7467
7537
|
if (parsed && typeof parsed === "object" && parsed.prs && typeof parsed.prs === "object") {
|
|
7468
7538
|
prs = parsed.prs;
|
|
7469
7539
|
}
|
|
@@ -7479,16 +7549,16 @@ function writePrMetadata(projectRoot, taskId, result) {
|
|
|
7479
7549
|
...primary || {},
|
|
7480
7550
|
updated_at: nowIso()
|
|
7481
7551
|
};
|
|
7482
|
-
|
|
7552
|
+
writeFileSync13(path, `${JSON.stringify(artifact, null, 2)}
|
|
7483
7553
|
`, "utf-8");
|
|
7484
7554
|
}
|
|
7485
7555
|
function readPrMetadata(projectRoot, taskId) {
|
|
7486
|
-
const path =
|
|
7487
|
-
if (!
|
|
7556
|
+
const path = resolve24(artifactDirForId(projectRoot, taskId), "pr-state.json");
|
|
7557
|
+
if (!existsSync22(path)) {
|
|
7488
7558
|
return [];
|
|
7489
7559
|
}
|
|
7490
7560
|
try {
|
|
7491
|
-
const parsed = JSON.parse(
|
|
7561
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
7492
7562
|
if (!parsed || typeof parsed !== "object") {
|
|
7493
7563
|
return [];
|
|
7494
7564
|
}
|
|
@@ -7560,14 +7630,14 @@ function resolveGithubCliBinary(projectRoot) {
|
|
|
7560
7630
|
}
|
|
7561
7631
|
const explicitPathEntries = (process.env.PATH || "").split(":").map((entry) => entry.trim()).filter(Boolean);
|
|
7562
7632
|
for (const entry of explicitPathEntries) {
|
|
7563
|
-
candidates.add(
|
|
7633
|
+
candidates.add(resolve24(entry, "gh"));
|
|
7564
7634
|
}
|
|
7565
7635
|
const bunResolved = Bun.which("gh");
|
|
7566
7636
|
if (bunResolved) {
|
|
7567
7637
|
candidates.add(bunResolved);
|
|
7568
7638
|
}
|
|
7569
7639
|
for (const candidate of candidates) {
|
|
7570
|
-
if (candidate &&
|
|
7640
|
+
if (candidate && existsSync22(candidate) && !isRuntimeGatewayGhPath(candidate)) {
|
|
7571
7641
|
return candidate;
|
|
7572
7642
|
}
|
|
7573
7643
|
}
|
|
@@ -7597,7 +7667,7 @@ function resolveRepoNameWithOwner(projectRoot, repoRoot) {
|
|
|
7597
7667
|
return resolveGithubRepoNameWithOwnerFromGitRoot(projectRoot, repoRoot, repoRoot, visited);
|
|
7598
7668
|
}
|
|
7599
7669
|
function resolveGithubRepoNameWithOwnerFromGitRoot(projectRoot, gitRoot, cwd, visited) {
|
|
7600
|
-
const normalizedGitRoot =
|
|
7670
|
+
const normalizedGitRoot = resolve24(gitRoot);
|
|
7601
7671
|
if (visited.has(normalizedGitRoot)) {
|
|
7602
7672
|
return "";
|
|
7603
7673
|
}
|
|
@@ -7669,7 +7739,7 @@ function resolveNetworkRemoteName(projectRoot, repoRoot, repoNameWithOwner) {
|
|
|
7669
7739
|
return remotes.includes("origin") ? "origin" : remotes[0];
|
|
7670
7740
|
}
|
|
7671
7741
|
function gitQuery(projectRoot, gitRoot, cwd, ...args) {
|
|
7672
|
-
const gitArgs =
|
|
7742
|
+
const gitArgs = existsSync22(resolve24(gitRoot, ".git")) ? gitCmd(projectRoot, gitRoot, ...args) : [resolveGitBinary(projectRoot), "--git-dir", gitRoot, ...args];
|
|
7673
7743
|
return runCapture2(gitArgs, cwd, projectRoot);
|
|
7674
7744
|
}
|
|
7675
7745
|
function resolveLocalGitRemoteRoot(remoteUrl, gitRoot) {
|
|
@@ -7687,9 +7757,9 @@ function resolveLocalGitRemoteRoot(remoteUrl, gitRoot) {
|
|
|
7687
7757
|
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(normalized) || /^[^@]+@[^:]+:.+$/.test(normalized)) {
|
|
7688
7758
|
return "";
|
|
7689
7759
|
} else if (!isAbsolute2(normalized)) {
|
|
7690
|
-
candidate =
|
|
7760
|
+
candidate = resolve24(gitRoot, normalized);
|
|
7691
7761
|
}
|
|
7692
|
-
return
|
|
7762
|
+
return existsSync22(candidate) ? candidate : "";
|
|
7693
7763
|
}
|
|
7694
7764
|
function normalizeGithubRepoNameWithOwner(value) {
|
|
7695
7765
|
const normalized = value.trim();
|
|
@@ -7816,7 +7886,7 @@ function inferReviewerFromChangedFiles(projectRoot, repoRoot, baseRef, branchRef
|
|
|
7816
7886
|
return best;
|
|
7817
7887
|
}
|
|
7818
7888
|
function commitRepo(projectRoot, repo, label, message, allowEmpty, scoped, files, changedFilesManifest) {
|
|
7819
|
-
if (!
|
|
7889
|
+
if (!existsSync22(resolve24(repo, ".git"))) {
|
|
7820
7890
|
console.log(`Skipping ${label}: repo not available (${repo})`);
|
|
7821
7891
|
return;
|
|
7822
7892
|
}
|
|
@@ -7848,18 +7918,18 @@ function commitRepo(projectRoot, repo, label, message, allowEmpty, scoped, files
|
|
|
7848
7918
|
console.log(`Committed ${label}: ${message}`);
|
|
7849
7919
|
}
|
|
7850
7920
|
function readChangedFilesManifest(projectRoot, taskId) {
|
|
7851
|
-
const manifestPath =
|
|
7852
|
-
if (!
|
|
7921
|
+
const manifestPath = resolve24(artifactDirForId(projectRoot, taskId), "changed-files.txt");
|
|
7922
|
+
if (!existsSync22(manifestPath)) {
|
|
7853
7923
|
return [];
|
|
7854
7924
|
}
|
|
7855
|
-
const files =
|
|
7925
|
+
const files = readFileSync13(manifestPath, "utf-8").split(/\r?\n/).map((line) => normalizeChangedFilePath2(line)).filter(Boolean);
|
|
7856
7926
|
return [...new Set(files)];
|
|
7857
7927
|
}
|
|
7858
7928
|
function refreshChangedFilesManifest(projectRoot, taskId) {
|
|
7859
|
-
const manifestPath =
|
|
7860
|
-
|
|
7929
|
+
const manifestPath = resolve24(artifactDirForId(projectRoot, taskId), "changed-files.txt");
|
|
7930
|
+
mkdirSync13(dirname11(manifestPath), { recursive: true });
|
|
7861
7931
|
const changedFiles = changedFilesForTask(projectRoot, taskId, true);
|
|
7862
|
-
|
|
7932
|
+
writeFileSync13(manifestPath, `${changedFiles.join(`
|
|
7863
7933
|
`)}
|
|
7864
7934
|
`, "utf-8");
|
|
7865
7935
|
return manifestPath;
|
|
@@ -7972,7 +8042,7 @@ function repoHasPathChange(projectRoot, repoRoot, relativePath) {
|
|
|
7972
8042
|
return result.exitCode === 0 && result.stdout.trim().length > 0;
|
|
7973
8043
|
}
|
|
7974
8044
|
function stageExcludePathspecs(repoRoot) {
|
|
7975
|
-
const patterns =
|
|
8045
|
+
const patterns = existsSync22(resolve24(repoRoot, ".rig", "task-config.json")) ? [...TASK_RUNTIME_STAGE_EXCLUDES, ...GENERATED_STAGE_EXCLUDES] : [".rig/**", ...GENERATED_STAGE_EXCLUDES];
|
|
7976
8046
|
return patterns.map((pattern) => `:(glob,exclude)${pattern}`);
|
|
7977
8047
|
}
|
|
7978
8048
|
function pathResolvesBeyondSymlink(repoRoot, relativePath) {
|
|
@@ -7982,7 +8052,7 @@ function pathResolvesBeyondSymlink(repoRoot, relativePath) {
|
|
|
7982
8052
|
}
|
|
7983
8053
|
let current = repoRoot;
|
|
7984
8054
|
for (let index = 0;index < parts.length - 1; index += 1) {
|
|
7985
|
-
current =
|
|
8055
|
+
current = resolve24(current, parts[index]);
|
|
7986
8056
|
try {
|
|
7987
8057
|
if (lstatSync(current).isSymbolicLink()) {
|
|
7988
8058
|
return true;
|
|
@@ -8016,7 +8086,7 @@ function resolveTaskBranchId(projectRoot, taskId) {
|
|
|
8016
8086
|
}
|
|
8017
8087
|
} catch {}
|
|
8018
8088
|
const artifactDir = artifactDirForId(projectRoot, taskId);
|
|
8019
|
-
if (
|
|
8089
|
+
if (existsSync22(artifactDir)) {
|
|
8020
8090
|
return taskId;
|
|
8021
8091
|
}
|
|
8022
8092
|
throw new Error(`Unknown task id: ${taskId}`);
|
|
@@ -8052,11 +8122,11 @@ function runCapture2(command, cwd, projectRoot = cwd) {
|
|
|
8052
8122
|
}
|
|
8053
8123
|
function runtimeGitEnv(projectRoot) {
|
|
8054
8124
|
const { ctx, runtimeRoot } = resolveRuntimeMetadata(projectRoot);
|
|
8055
|
-
const runtimeHome = runtimeRoot ?
|
|
8056
|
-
const runtimeTmp = runtimeRoot ?
|
|
8057
|
-
const runtimeCache = runtimeRoot ?
|
|
8058
|
-
const runtimeKnownHosts = runtimeHome ?
|
|
8059
|
-
const runtimeKey = runtimeHome ?
|
|
8125
|
+
const runtimeHome = runtimeRoot ? resolve24(runtimeRoot, "home") : "";
|
|
8126
|
+
const runtimeTmp = runtimeRoot ? resolve24(runtimeRoot, "tmp") : "";
|
|
8127
|
+
const runtimeCache = runtimeRoot ? resolve24(runtimeRoot, "cache") : "";
|
|
8128
|
+
const runtimeKnownHosts = runtimeHome ? resolve24(runtimeHome, ".ssh", "known_hosts") : "";
|
|
8129
|
+
const runtimeKey = runtimeHome ? resolve24(runtimeHome, ".ssh", "rig-agent-key") : "";
|
|
8060
8130
|
const env = {};
|
|
8061
8131
|
if (ctx?.workspaceDir) {
|
|
8062
8132
|
env.PROJECT_RIG_ROOT = projectRoot;
|
|
@@ -8069,14 +8139,14 @@ function runtimeGitEnv(projectRoot) {
|
|
|
8069
8139
|
if (runtimeRoot) {
|
|
8070
8140
|
env.RIG_RUNTIME_HOME = runtimeRoot;
|
|
8071
8141
|
}
|
|
8072
|
-
if (runtimeHome &&
|
|
8142
|
+
if (runtimeHome && existsSync22(runtimeHome)) {
|
|
8073
8143
|
env.HOME = runtimeHome;
|
|
8074
8144
|
env.OPENSSL_CONF = ensureRuntimeOpenSslConfig(runtimeHome);
|
|
8075
8145
|
}
|
|
8076
|
-
if (runtimeTmp &&
|
|
8146
|
+
if (runtimeTmp && existsSync22(runtimeTmp)) {
|
|
8077
8147
|
env.TMPDIR = runtimeTmp;
|
|
8078
8148
|
}
|
|
8079
|
-
if (runtimeCache &&
|
|
8149
|
+
if (runtimeCache && existsSync22(runtimeCache)) {
|
|
8080
8150
|
env.XDG_CACHE_HOME = runtimeCache;
|
|
8081
8151
|
}
|
|
8082
8152
|
const workspaceSecrets = loadDotEnvSecrets(ctx?.workspaceDir || projectRoot, process.env);
|
|
@@ -8120,14 +8190,14 @@ function runtimeGitEnv(projectRoot) {
|
|
|
8120
8190
|
env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
|
|
8121
8191
|
applyGitHubCredentialHelperEnv(env);
|
|
8122
8192
|
}
|
|
8123
|
-
if (runtimeKnownHosts &&
|
|
8193
|
+
if (runtimeKnownHosts && existsSync22(runtimeKnownHosts)) {
|
|
8124
8194
|
const sshParts = [
|
|
8125
8195
|
"ssh",
|
|
8126
8196
|
`-o UserKnownHostsFile="${runtimeKnownHosts}"`,
|
|
8127
8197
|
"-o StrictHostKeyChecking=yes",
|
|
8128
8198
|
"-F /dev/null"
|
|
8129
8199
|
];
|
|
8130
|
-
if (runtimeKey &&
|
|
8200
|
+
if (runtimeKey && existsSync22(runtimeKey)) {
|
|
8131
8201
|
sshParts.splice(1, 0, `-i "${runtimeKey}"`, "-o IdentitiesOnly=yes");
|
|
8132
8202
|
}
|
|
8133
8203
|
env.GIT_SSH_COMMAND = sshParts.join(" ");
|
|
@@ -8148,12 +8218,12 @@ function loadPersistedRuntimeSecrets(runtimeRoot) {
|
|
|
8148
8218
|
if (!runtimeRoot) {
|
|
8149
8219
|
return {};
|
|
8150
8220
|
}
|
|
8151
|
-
const path =
|
|
8152
|
-
if (!
|
|
8221
|
+
const path = resolve24(runtimeRoot, "runtime-secrets.json");
|
|
8222
|
+
if (!existsSync22(path)) {
|
|
8153
8223
|
return {};
|
|
8154
8224
|
}
|
|
8155
8225
|
try {
|
|
8156
|
-
const parsed = JSON.parse(
|
|
8226
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
8157
8227
|
const entries = Object.entries(parsed).filter((entry) => typeof entry[1] === "string");
|
|
8158
8228
|
return Object.fromEntries(entries);
|
|
8159
8229
|
} catch {
|
|
@@ -8161,13 +8231,13 @@ function loadPersistedRuntimeSecrets(runtimeRoot) {
|
|
|
8161
8231
|
}
|
|
8162
8232
|
}
|
|
8163
8233
|
function ensureRuntimeOpenSslConfig(runtimeHome) {
|
|
8164
|
-
const sslDir =
|
|
8165
|
-
const sslConfig =
|
|
8166
|
-
if (!
|
|
8167
|
-
|
|
8234
|
+
const sslDir = resolve24(runtimeHome, ".ssl");
|
|
8235
|
+
const sslConfig = resolve24(sslDir, "openssl.cnf");
|
|
8236
|
+
if (!existsSync22(sslDir)) {
|
|
8237
|
+
mkdirSync13(sslDir, { recursive: true });
|
|
8168
8238
|
}
|
|
8169
|
-
if (!
|
|
8170
|
-
|
|
8239
|
+
if (!existsSync22(sslConfig)) {
|
|
8240
|
+
writeFileSync13(sslConfig, `# Rig runtime OpenSSL config placeholder
|
|
8171
8241
|
`);
|
|
8172
8242
|
}
|
|
8173
8243
|
return sslConfig;
|
|
@@ -8185,29 +8255,29 @@ function resolveRuntimeMetadata(projectRoot) {
|
|
|
8185
8255
|
if (contextFile) {
|
|
8186
8256
|
return {
|
|
8187
8257
|
ctx,
|
|
8188
|
-
runtimeRoot:
|
|
8258
|
+
runtimeRoot: dirname11(resolve24(contextFile))
|
|
8189
8259
|
};
|
|
8190
8260
|
}
|
|
8191
8261
|
const inferredContextFile = findRuntimeContextFile2(projectRoot);
|
|
8192
|
-
if (
|
|
8262
|
+
if (existsSync22(inferredContextFile)) {
|
|
8193
8263
|
try {
|
|
8194
8264
|
ctx = loadRuntimeContext(inferredContextFile);
|
|
8195
8265
|
} catch {}
|
|
8196
8266
|
return {
|
|
8197
8267
|
ctx,
|
|
8198
|
-
runtimeRoot:
|
|
8268
|
+
runtimeRoot: dirname11(inferredContextFile)
|
|
8199
8269
|
};
|
|
8200
8270
|
}
|
|
8201
8271
|
return { ctx, runtimeRoot: "" };
|
|
8202
8272
|
}
|
|
8203
8273
|
function findRuntimeContextFile2(startPath) {
|
|
8204
|
-
let current =
|
|
8274
|
+
let current = resolve24(startPath);
|
|
8205
8275
|
while (true) {
|
|
8206
|
-
const candidate =
|
|
8207
|
-
if (
|
|
8276
|
+
const candidate = resolve24(current, "runtime-context.json");
|
|
8277
|
+
if (existsSync22(candidate)) {
|
|
8208
8278
|
return candidate;
|
|
8209
8279
|
}
|
|
8210
|
-
const parent =
|
|
8280
|
+
const parent = dirname11(current);
|
|
8211
8281
|
if (parent === current) {
|
|
8212
8282
|
return "";
|
|
8213
8283
|
}
|
|
@@ -8443,9 +8513,9 @@ async function main() {
|
|
|
8443
8513
|
console.log(`
|
|
8444
8514
|
[post] Auto-merge: skipped (not in policy completion.checks)`);
|
|
8445
8515
|
}
|
|
8446
|
-
const artifactDir =
|
|
8447
|
-
|
|
8448
|
-
|
|
8516
|
+
const artifactDir = resolve25(paths.artifactsDir, taskId);
|
|
8517
|
+
mkdirSync14(artifactDir, { recursive: true });
|
|
8518
|
+
writeFileSync14(resolve25(artifactDir, "review-status.txt"), failed ? `REJECTED
|
|
8449
8519
|
` : `APPROVED
|
|
8450
8520
|
`, "utf-8");
|
|
8451
8521
|
if (!failed) {
|
|
@@ -8492,8 +8562,8 @@ async function runBunTool(args, cwd) {
|
|
|
8492
8562
|
};
|
|
8493
8563
|
}
|
|
8494
8564
|
async function runProtoQualityGate(monorepoRoot) {
|
|
8495
|
-
const protosDir =
|
|
8496
|
-
if (!
|
|
8565
|
+
const protosDir = resolve25(monorepoRoot, "packages", "protos");
|
|
8566
|
+
if (!existsSync23(protosDir)) {
|
|
8497
8567
|
console.log(`FAIL: Proto workspace not found at ${protosDir}`);
|
|
8498
8568
|
return false;
|
|
8499
8569
|
}
|
|
@@ -8541,12 +8611,12 @@ async function runProtoQualityGate(monorepoRoot) {
|
|
|
8541
8611
|
} else {
|
|
8542
8612
|
console.log("OK: Generated TypeScript compiles");
|
|
8543
8613
|
}
|
|
8544
|
-
const workflowPath =
|
|
8545
|
-
if (!
|
|
8614
|
+
const workflowPath = resolve25(monorepoRoot, ".github", "workflows", "pull-request-gate.yml");
|
|
8615
|
+
if (!existsSync23(workflowPath)) {
|
|
8546
8616
|
console.log(`FAIL: Missing workflow gate file at ${workflowPath}`);
|
|
8547
8617
|
ok = false;
|
|
8548
8618
|
} else {
|
|
8549
|
-
const workflow =
|
|
8619
|
+
const workflow = readFileSync14(workflowPath, "utf-8");
|
|
8550
8620
|
if (workflow.includes("if: false && needs.detect.outputs.protos_changed == 'true'")) {
|
|
8551
8621
|
console.log("FAIL: Proto quality CI gate is disabled in pull-request-gate.yml");
|
|
8552
8622
|
ok = false;
|
|
@@ -8575,7 +8645,7 @@ function repoHasPublishedTaskBranch(projectRoot, repoRoot, taskId) {
|
|
|
8575
8645
|
return runCapture(["git", "-C", repoRoot, "ls-remote", "--exit-code", "origin", `refs/heads/${branchRef}`], projectRoot).exitCode === 0;
|
|
8576
8646
|
}
|
|
8577
8647
|
async function readJsonFileIfPresent(path) {
|
|
8578
|
-
if (!
|
|
8648
|
+
if (!existsSync23(path)) {
|
|
8579
8649
|
return null;
|
|
8580
8650
|
}
|
|
8581
8651
|
try {
|
|
@@ -8586,9 +8656,9 @@ async function readJsonFileIfPresent(path) {
|
|
|
8586
8656
|
}
|
|
8587
8657
|
async function recordVerifierFailure(projectRoot, taskId, paths) {
|
|
8588
8658
|
const failedApproachesPath = paths.failedApproachesPath;
|
|
8589
|
-
const artifactDir =
|
|
8590
|
-
const reviewStatePath =
|
|
8591
|
-
const reviewFeedbackPath =
|
|
8659
|
+
const artifactDir = resolve25(paths.artifactsDir, taskId);
|
|
8660
|
+
const reviewStatePath = resolve25(artifactDir, "review-state.json");
|
|
8661
|
+
const reviewFeedbackPath = resolve25(artifactDir, "review-feedback.md");
|
|
8592
8662
|
let summary = "Verifier rejected completion. Read review-feedback.md for required fixes.";
|
|
8593
8663
|
const parsedReviewState = await readJsonFileIfPresent(reviewStatePath);
|
|
8594
8664
|
if (parsedReviewState) {
|
|
@@ -8598,12 +8668,12 @@ async function recordVerifierFailure(projectRoot, taskId, paths) {
|
|
|
8598
8668
|
}
|
|
8599
8669
|
}
|
|
8600
8670
|
let attempts = 1;
|
|
8601
|
-
if (
|
|
8602
|
-
const content =
|
|
8671
|
+
if (existsSync23(failedApproachesPath)) {
|
|
8672
|
+
const content = readFileSync14(failedApproachesPath, "utf-8");
|
|
8603
8673
|
attempts = (content.match(new RegExp(`^## ${escapeRegExp2(taskId)}\\b`, "gm")) || []).length + 1;
|
|
8604
8674
|
} else {
|
|
8605
|
-
|
|
8606
|
-
|
|
8675
|
+
mkdirSync14(resolve25(failedApproachesPath, ".."), { recursive: true });
|
|
8676
|
+
writeFileSync14(failedApproachesPath, `# Failed Approaches
|
|
8607
8677
|
|
|
8608
8678
|
`, "utf-8");
|
|
8609
8679
|
}
|
|
@@ -8641,8 +8711,8 @@ async function recordTaskRepoCommits(projectRoot, taskId, paths) {
|
|
|
8641
8711
|
recorded_at: new Date().toISOString(),
|
|
8642
8712
|
repos
|
|
8643
8713
|
};
|
|
8644
|
-
|
|
8645
|
-
|
|
8714
|
+
mkdirSync14(resolve25(statePath, ".."), { recursive: true });
|
|
8715
|
+
writeFileSync14(statePath, `${JSON.stringify(state, null, 2)}
|
|
8646
8716
|
`, "utf-8");
|
|
8647
8717
|
}
|
|
8648
8718
|
}
|