@h-rig/runtime 0.0.6-alpha.33 → 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 +431 -362
- package/dist/src/control-plane/agent-wrapper.js +523 -464
- package/dist/src/control-plane/harness-main.js +529 -459
- 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 +11 -1
- package/dist/src/control-plane/native/harness-cli.js +514 -444
- 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-sessiond/bin.js +62 -0
- package/dist/src/control-plane/pi-sessiond/server.js +62 -0
- package/dist/src/control-plane/pi-sessiond/session-service.js +62 -0
- 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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/runtime/src/control-plane/native/harness-cli.ts
|
|
3
|
-
import { existsSync as
|
|
4
|
-
import { resolve as
|
|
3
|
+
import { existsSync as existsSync31 } from "fs";
|
|
4
|
+
import { resolve as resolve31 } from "path";
|
|
5
5
|
|
|
6
6
|
// packages/runtime/src/control-plane/native/git-ops.ts
|
|
7
|
-
import { existsSync as
|
|
8
|
-
import { dirname as
|
|
7
|
+
import { existsSync as existsSync24, lstatSync, mkdirSync as mkdirSync12, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "fs";
|
|
8
|
+
import { dirname as dirname12, isAbsolute as isAbsolute2, resolve as resolve26 } from "path";
|
|
9
9
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10
10
|
|
|
11
11
|
// packages/runtime/src/control-plane/runtime/baked-secrets.ts
|
|
@@ -274,8 +274,8 @@ function isAgentRuntimeContextPath(path) {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
// packages/runtime/src/control-plane/native/task-ops.ts
|
|
277
|
-
import { appendFileSync, existsSync as
|
|
278
|
-
import { resolve as
|
|
277
|
+
import { appendFileSync, existsSync as existsSync23, mkdirSync as mkdirSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
|
|
278
|
+
import { resolve as resolve25 } from "path";
|
|
279
279
|
|
|
280
280
|
// packages/runtime/src/build-time-config.ts
|
|
281
281
|
function normalizeBuildConfig(value) {
|
|
@@ -824,6 +824,8 @@ function buildBrowserGuidanceLines(browser) {
|
|
|
824
824
|
}
|
|
825
825
|
|
|
826
826
|
// packages/runtime/src/control-plane/plugin-host-context.ts
|
|
827
|
+
import { existsSync as existsSync8 } from "fs";
|
|
828
|
+
import { resolve as resolvePath } from "path";
|
|
827
829
|
import { createPluginHost } from "@rig/core";
|
|
828
830
|
import { loadConfig } from "@rig/core/load-config";
|
|
829
831
|
|
|
@@ -1171,6 +1173,55 @@ async function materializeSkills(projectRoot, entries) {
|
|
|
1171
1173
|
return written;
|
|
1172
1174
|
}
|
|
1173
1175
|
|
|
1176
|
+
// packages/runtime/src/control-plane/pi-settings-materializer.ts
|
|
1177
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1178
|
+
import { dirname as dirname6, resolve as resolve9 } from "path";
|
|
1179
|
+
var SETTINGS_RELATIVE_PATH = ".pi/settings.json";
|
|
1180
|
+
var MANAGED_RECORD_RELATIVE_PATH = ".rig/state/pi-managed-packages.json";
|
|
1181
|
+
function readJson(path, fallback) {
|
|
1182
|
+
if (!existsSync7(path))
|
|
1183
|
+
return fallback;
|
|
1184
|
+
try {
|
|
1185
|
+
return JSON.parse(readFileSync6(path, "utf-8"));
|
|
1186
|
+
} catch {
|
|
1187
|
+
return fallback;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
function packageKey(entry) {
|
|
1191
|
+
if (typeof entry === "string")
|
|
1192
|
+
return entry;
|
|
1193
|
+
if (entry && typeof entry === "object" && typeof entry.source === "string") {
|
|
1194
|
+
return entry.source;
|
|
1195
|
+
}
|
|
1196
|
+
return JSON.stringify(entry);
|
|
1197
|
+
}
|
|
1198
|
+
function materializePiPackages(projectRoot, declaredPackages) {
|
|
1199
|
+
const settingsPath = resolve9(projectRoot, SETTINGS_RELATIVE_PATH);
|
|
1200
|
+
const managedRecordPath = resolve9(projectRoot, MANAGED_RECORD_RELATIVE_PATH);
|
|
1201
|
+
const settings = readJson(settingsPath, {});
|
|
1202
|
+
const previouslyManaged = new Set(readJson(managedRecordPath, []));
|
|
1203
|
+
const existing = Array.isArray(settings.packages) ? settings.packages : [];
|
|
1204
|
+
const operatorEntries = existing.filter((entry) => !previouslyManaged.has(packageKey(entry)));
|
|
1205
|
+
const operatorKeys = new Set(operatorEntries.map(packageKey));
|
|
1206
|
+
const managedToAdd = declaredPackages.filter((pkg) => !operatorKeys.has(pkg));
|
|
1207
|
+
const nextPackages = [...operatorEntries, ...managedToAdd];
|
|
1208
|
+
if (nextPackages.length > 0 || existsSync7(settingsPath)) {
|
|
1209
|
+
const nextSettings = { ...settings };
|
|
1210
|
+
if (nextPackages.length > 0) {
|
|
1211
|
+
nextSettings.packages = nextPackages;
|
|
1212
|
+
} else {
|
|
1213
|
+
delete nextSettings.packages;
|
|
1214
|
+
}
|
|
1215
|
+
mkdirSync5(dirname6(settingsPath), { recursive: true });
|
|
1216
|
+
writeFileSync5(settingsPath, `${JSON.stringify(nextSettings, null, 2)}
|
|
1217
|
+
`, "utf-8");
|
|
1218
|
+
}
|
|
1219
|
+
mkdirSync5(dirname6(managedRecordPath), { recursive: true });
|
|
1220
|
+
writeFileSync5(managedRecordPath, `${JSON.stringify(managedToAdd, null, 2)}
|
|
1221
|
+
`, "utf-8");
|
|
1222
|
+
return { settingsPath, packages: managedToAdd };
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1174
1225
|
// packages/runtime/src/control-plane/plugin-host-context.ts
|
|
1175
1226
|
async function buildPluginHostContext(projectRoot) {
|
|
1176
1227
|
let config;
|
|
@@ -1218,6 +1269,14 @@ async function buildPluginHostContext(projectRoot) {
|
|
|
1218
1269
|
} catch (err) {
|
|
1219
1270
|
console.warn(`[plugin-host] skill materialization failed: ${err instanceof Error ? err.message : err}`);
|
|
1220
1271
|
}
|
|
1272
|
+
try {
|
|
1273
|
+
const piPackages = config.runtime?.pi?.packages ?? [];
|
|
1274
|
+
if (piPackages.length > 0 || existsSync8(resolvePath(projectRoot, ".rig/state/pi-managed-packages.json"))) {
|
|
1275
|
+
materializePiPackages(projectRoot, piPackages);
|
|
1276
|
+
}
|
|
1277
|
+
} catch (err) {
|
|
1278
|
+
console.warn(`[plugin-host] Pi package materialization failed: ${err instanceof Error ? err.message : err}`);
|
|
1279
|
+
}
|
|
1221
1280
|
return {
|
|
1222
1281
|
config,
|
|
1223
1282
|
pluginHost,
|
|
@@ -1231,12 +1290,12 @@ async function buildPluginHostContext(projectRoot) {
|
|
|
1231
1290
|
|
|
1232
1291
|
// packages/runtime/src/control-plane/tasks/source-aware-task-config-source.ts
|
|
1233
1292
|
import { spawnSync } from "child_process";
|
|
1234
|
-
import { existsSync as
|
|
1235
|
-
import { basename as basename3, join as join2, resolve as
|
|
1293
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync, writeFileSync as writeFileSync6 } from "fs";
|
|
1294
|
+
import { basename as basename3, join as join2, resolve as resolve11 } from "path";
|
|
1236
1295
|
|
|
1237
1296
|
// packages/runtime/src/control-plane/tasks/legacy-task-config-source.ts
|
|
1238
|
-
import { existsSync as
|
|
1239
|
-
import { resolve as
|
|
1297
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
1298
|
+
import { resolve as resolve10 } from "path";
|
|
1240
1299
|
|
|
1241
1300
|
// packages/runtime/src/control-plane/tasks/task-record-reader.ts
|
|
1242
1301
|
async function findTaskById(reader, id) {
|
|
@@ -1259,7 +1318,7 @@ class LegacyTaskConfigReadError extends Error {
|
|
|
1259
1318
|
}
|
|
1260
1319
|
}
|
|
1261
1320
|
function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
|
|
1262
|
-
const configPath = options.configPath ??
|
|
1321
|
+
const configPath = options.configPath ?? resolve10(projectRoot, ".rig", "task-config.json");
|
|
1263
1322
|
const reader = {
|
|
1264
1323
|
async listTasks() {
|
|
1265
1324
|
return readLegacyTaskRecords(projectRoot, configPath);
|
|
@@ -1270,8 +1329,8 @@ function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
|
|
|
1270
1329
|
};
|
|
1271
1330
|
return reader;
|
|
1272
1331
|
}
|
|
1273
|
-
function readLegacyTaskRecords(projectRoot, configPath =
|
|
1274
|
-
if (!
|
|
1332
|
+
function readLegacyTaskRecords(projectRoot, configPath = resolve10(projectRoot, ".rig", "task-config.json")) {
|
|
1333
|
+
if (!existsSync9(configPath)) {
|
|
1275
1334
|
return [];
|
|
1276
1335
|
}
|
|
1277
1336
|
const rawConfig = readLegacyTaskConfigJson(projectRoot, configPath);
|
|
@@ -1279,7 +1338,7 @@ function readLegacyTaskRecords(projectRoot, configPath = resolve9(projectRoot, "
|
|
|
1279
1338
|
}
|
|
1280
1339
|
function readLegacyTaskConfigJson(projectRoot, configPath) {
|
|
1281
1340
|
try {
|
|
1282
|
-
const parsed = JSON.parse(
|
|
1341
|
+
const parsed = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
1283
1342
|
if (isPlainRecord(parsed)) {
|
|
1284
1343
|
return parsed;
|
|
1285
1344
|
}
|
|
@@ -1363,7 +1422,7 @@ function isPlainRecord(candidate) {
|
|
|
1363
1422
|
var STATUS_LABELS = new Set(["ready", "blocked", "in-progress", "under-review", "failed", "cancelled"]);
|
|
1364
1423
|
var FILE_TASK_PATTERN = /\.(task\.)?json$/;
|
|
1365
1424
|
function createSourceAwareTaskConfigRecordReader(projectRoot, options = {}) {
|
|
1366
|
-
const configPath = options.configPath ??
|
|
1425
|
+
const configPath = options.configPath ?? resolve11(projectRoot, ".rig", "task-config.json");
|
|
1367
1426
|
const legacy = createLegacyTaskConfigRecordReader(projectRoot, { configPath });
|
|
1368
1427
|
const spawnFn = options.spawn ?? spawnSync;
|
|
1369
1428
|
const ghBinary = options.ghBinary ?? "gh";
|
|
@@ -1446,10 +1505,10 @@ function readMaterializedTaskMetadata(entry) {
|
|
|
1446
1505
|
return metadata;
|
|
1447
1506
|
}
|
|
1448
1507
|
function readConfiguredFilesTaskSourcePath(projectRoot) {
|
|
1449
|
-
const jsonPath =
|
|
1450
|
-
if (
|
|
1508
|
+
const jsonPath = resolve11(projectRoot, "rig.config.json");
|
|
1509
|
+
if (existsSync10(jsonPath)) {
|
|
1451
1510
|
try {
|
|
1452
|
-
const parsed = JSON.parse(
|
|
1511
|
+
const parsed = JSON.parse(readFileSync8(jsonPath, "utf8"));
|
|
1453
1512
|
if (isPlainRecord2(parsed) && isPlainRecord2(parsed.taskSource)) {
|
|
1454
1513
|
const source = parsed.taskSource;
|
|
1455
1514
|
return source.kind === "files" && typeof source.path === "string" ? source.path : null;
|
|
@@ -1458,12 +1517,12 @@ function readConfiguredFilesTaskSourcePath(projectRoot) {
|
|
|
1458
1517
|
return null;
|
|
1459
1518
|
}
|
|
1460
1519
|
}
|
|
1461
|
-
const tsPath =
|
|
1462
|
-
if (!
|
|
1520
|
+
const tsPath = resolve11(projectRoot, "rig.config.ts");
|
|
1521
|
+
if (!existsSync10(tsPath)) {
|
|
1463
1522
|
return null;
|
|
1464
1523
|
}
|
|
1465
1524
|
try {
|
|
1466
|
-
const source =
|
|
1525
|
+
const source = readFileSync8(tsPath, "utf8");
|
|
1467
1526
|
const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
|
|
1468
1527
|
const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
|
|
1469
1528
|
if (kind !== "files") {
|
|
@@ -1483,10 +1542,10 @@ function readRawTaskEntry(configPath, taskId) {
|
|
|
1483
1542
|
return isPlainRecord2(entry) ? entry : null;
|
|
1484
1543
|
}
|
|
1485
1544
|
function readRawTaskConfig(configPath) {
|
|
1486
|
-
if (!
|
|
1545
|
+
if (!existsSync10(configPath)) {
|
|
1487
1546
|
return null;
|
|
1488
1547
|
}
|
|
1489
|
-
const parsed = JSON.parse(
|
|
1548
|
+
const parsed = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
1490
1549
|
return isPlainRecord2(parsed) ? parsed : null;
|
|
1491
1550
|
}
|
|
1492
1551
|
function stripLegacyTaskConfigMetadata2(raw) {
|
|
@@ -1494,8 +1553,8 @@ function stripLegacyTaskConfigMetadata2(raw) {
|
|
|
1494
1553
|
return tasks;
|
|
1495
1554
|
}
|
|
1496
1555
|
function listFileBackedTasks(projectRoot, sourcePath) {
|
|
1497
|
-
const directory =
|
|
1498
|
-
if (!
|
|
1556
|
+
const directory = resolve11(projectRoot, sourcePath);
|
|
1557
|
+
if (!existsSync10(directory)) {
|
|
1499
1558
|
return [];
|
|
1500
1559
|
}
|
|
1501
1560
|
const tasks = [];
|
|
@@ -1510,11 +1569,11 @@ function listFileBackedTasks(projectRoot, sourcePath) {
|
|
|
1510
1569
|
return tasks;
|
|
1511
1570
|
}
|
|
1512
1571
|
function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
|
|
1513
|
-
const file = findFileBackedTaskFile(
|
|
1572
|
+
const file = findFileBackedTaskFile(resolve11(projectRoot, sourcePath), taskId);
|
|
1514
1573
|
if (!file) {
|
|
1515
1574
|
return null;
|
|
1516
1575
|
}
|
|
1517
|
-
const raw = JSON.parse(
|
|
1576
|
+
const raw = JSON.parse(readFileSync8(file, "utf8"));
|
|
1518
1577
|
if (!isPlainRecord2(raw)) {
|
|
1519
1578
|
return null;
|
|
1520
1579
|
}
|
|
@@ -1527,7 +1586,7 @@ function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
|
|
|
1527
1586
|
};
|
|
1528
1587
|
}
|
|
1529
1588
|
function findFileBackedTaskFile(directory, taskId) {
|
|
1530
|
-
if (!
|
|
1589
|
+
if (!existsSync10(directory)) {
|
|
1531
1590
|
return null;
|
|
1532
1591
|
}
|
|
1533
1592
|
for (const name of readdirSync2(directory)) {
|
|
@@ -1537,7 +1596,7 @@ function findFileBackedTaskFile(directory, taskId) {
|
|
|
1537
1596
|
try {
|
|
1538
1597
|
if (!statSync(file).isFile())
|
|
1539
1598
|
continue;
|
|
1540
|
-
const raw = JSON.parse(
|
|
1599
|
+
const raw = JSON.parse(readFileSync8(file, "utf8"));
|
|
1541
1600
|
const inferredId = basename3(file).replace(FILE_TASK_PATTERN, "");
|
|
1542
1601
|
const id = isPlainRecord2(raw) && typeof raw.id === "string" ? raw.id : inferredId;
|
|
1543
1602
|
if (id === taskId) {
|
|
@@ -1697,8 +1756,8 @@ async function readConfiguredTaskSourceTask(projectRoot, taskId) {
|
|
|
1697
1756
|
}
|
|
1698
1757
|
|
|
1699
1758
|
// packages/runtime/src/control-plane/native/task-state.ts
|
|
1700
|
-
import { existsSync as
|
|
1701
|
-
import { basename as basename6, resolve as
|
|
1759
|
+
import { existsSync as existsSync17, readFileSync as readFileSync11, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
1760
|
+
import { basename as basename6, resolve as resolve18 } from "path";
|
|
1702
1761
|
|
|
1703
1762
|
// packages/runtime/src/control-plane/state-sync/types.ts
|
|
1704
1763
|
var SUPPORTED_TASK_STATE_SCHEMA_VERSION = 1;
|
|
@@ -1806,50 +1865,50 @@ function readTaskStateMetadataEnvelope(raw) {
|
|
|
1806
1865
|
};
|
|
1807
1866
|
}
|
|
1808
1867
|
// packages/runtime/src/control-plane/state-sync/read.ts
|
|
1809
|
-
import { existsSync as
|
|
1810
|
-
import { resolve as
|
|
1868
|
+
import { existsSync as existsSync16, readFileSync as readFileSync10 } from "fs";
|
|
1869
|
+
import { resolve as resolve17 } from "path";
|
|
1811
1870
|
|
|
1812
1871
|
// packages/runtime/src/control-plane/native/utils.ts
|
|
1813
|
-
import { existsSync as
|
|
1814
|
-
import { resolve as
|
|
1872
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
1873
|
+
import { resolve as resolve14 } from "path";
|
|
1815
1874
|
|
|
1816
1875
|
// packages/runtime/src/layout.ts
|
|
1817
|
-
import { existsSync as
|
|
1818
|
-
import { basename as basename4, dirname as
|
|
1876
|
+
import { existsSync as existsSync11 } from "fs";
|
|
1877
|
+
import { basename as basename4, dirname as dirname7, resolve as resolve12 } from "path";
|
|
1819
1878
|
var RIG_DEFINITION_DIRNAME = "rig";
|
|
1820
1879
|
var RIG_ARTIFACTS_DIRNAME = "artifacts";
|
|
1821
1880
|
function resolveMonorepoRoot(projectRoot) {
|
|
1822
|
-
const normalizedProjectRoot =
|
|
1881
|
+
const normalizedProjectRoot = resolve12(projectRoot);
|
|
1823
1882
|
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
1824
1883
|
if (explicit) {
|
|
1825
|
-
const explicitRoot =
|
|
1826
|
-
const explicitParent =
|
|
1884
|
+
const explicitRoot = resolve12(explicit);
|
|
1885
|
+
const explicitParent = dirname7(explicitRoot);
|
|
1827
1886
|
if (basename4(explicitParent) === ".worktrees") {
|
|
1828
|
-
const owner =
|
|
1829
|
-
const ownerHasGit =
|
|
1830
|
-
const ownerHasTaskConfig =
|
|
1831
|
-
const ownerHasRigConfig =
|
|
1887
|
+
const owner = dirname7(explicitParent);
|
|
1888
|
+
const ownerHasGit = existsSync11(resolve12(owner, ".git"));
|
|
1889
|
+
const ownerHasTaskConfig = existsSync11(resolve12(owner, ".rig", "task-config.json"));
|
|
1890
|
+
const ownerHasRigConfig = existsSync11(resolve12(owner, "rig.config.ts"));
|
|
1832
1891
|
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
1833
1892
|
return owner;
|
|
1834
1893
|
}
|
|
1835
1894
|
throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
|
|
1836
1895
|
}
|
|
1837
|
-
if (!
|
|
1896
|
+
if (!existsSync11(resolve12(explicitRoot, ".git"))) {
|
|
1838
1897
|
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
|
|
1839
1898
|
}
|
|
1840
|
-
const hasTaskConfig =
|
|
1841
|
-
const hasRigConfig =
|
|
1899
|
+
const hasTaskConfig = existsSync11(resolve12(explicitRoot, ".rig", "task-config.json"));
|
|
1900
|
+
const hasRigConfig = existsSync11(resolve12(explicitRoot, "rig.config.ts"));
|
|
1842
1901
|
if (!hasTaskConfig && !hasRigConfig) {
|
|
1843
1902
|
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
|
|
1844
1903
|
}
|
|
1845
1904
|
return explicitRoot;
|
|
1846
1905
|
}
|
|
1847
|
-
const projectParent =
|
|
1906
|
+
const projectParent = dirname7(normalizedProjectRoot);
|
|
1848
1907
|
if (basename4(projectParent) === ".worktrees") {
|
|
1849
|
-
const worktreeOwner =
|
|
1850
|
-
const ownerHasGit =
|
|
1851
|
-
const ownerHasTaskConfig =
|
|
1852
|
-
const ownerHasRigConfig =
|
|
1908
|
+
const worktreeOwner = dirname7(projectParent);
|
|
1909
|
+
const ownerHasGit = existsSync11(resolve12(worktreeOwner, ".git"));
|
|
1910
|
+
const ownerHasTaskConfig = existsSync11(resolve12(worktreeOwner, ".rig", "task-config.json"));
|
|
1911
|
+
const ownerHasRigConfig = existsSync11(resolve12(worktreeOwner, "rig.config.ts"));
|
|
1853
1912
|
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
1854
1913
|
return worktreeOwner;
|
|
1855
1914
|
}
|
|
@@ -1857,28 +1916,28 @@ function resolveMonorepoRoot(projectRoot) {
|
|
|
1857
1916
|
return normalizedProjectRoot;
|
|
1858
1917
|
}
|
|
1859
1918
|
function resolveRuntimeWorkspaceLayout(workspaceDir) {
|
|
1860
|
-
const root =
|
|
1861
|
-
const rigRoot =
|
|
1862
|
-
const logsDir =
|
|
1863
|
-
const stateDir =
|
|
1864
|
-
const runtimeDir =
|
|
1865
|
-
const binDir =
|
|
1919
|
+
const root = resolve12(workspaceDir);
|
|
1920
|
+
const rigRoot = resolve12(root, ".rig");
|
|
1921
|
+
const logsDir = resolve12(rigRoot, "logs");
|
|
1922
|
+
const stateDir = resolve12(rigRoot, "state");
|
|
1923
|
+
const runtimeDir = resolve12(rigRoot, "runtime");
|
|
1924
|
+
const binDir = resolve12(rigRoot, "bin");
|
|
1866
1925
|
return {
|
|
1867
1926
|
workspaceDir: root,
|
|
1868
1927
|
rigRoot,
|
|
1869
1928
|
stateDir,
|
|
1870
1929
|
logsDir,
|
|
1871
|
-
artifactsRoot:
|
|
1930
|
+
artifactsRoot: resolve12(root, RIG_ARTIFACTS_DIRNAME),
|
|
1872
1931
|
runtimeDir,
|
|
1873
|
-
homeDir:
|
|
1874
|
-
tmpDir:
|
|
1875
|
-
cacheDir:
|
|
1876
|
-
sessionDir:
|
|
1932
|
+
homeDir: resolve12(rigRoot, "home"),
|
|
1933
|
+
tmpDir: resolve12(rigRoot, "tmp"),
|
|
1934
|
+
cacheDir: resolve12(rigRoot, "cache"),
|
|
1935
|
+
sessionDir: resolve12(rigRoot, "session"),
|
|
1877
1936
|
binDir,
|
|
1878
|
-
distDir:
|
|
1879
|
-
pluginBinDir:
|
|
1880
|
-
contextPath:
|
|
1881
|
-
controlPlaneEventsFile:
|
|
1937
|
+
distDir: resolve12(rigRoot, "dist"),
|
|
1938
|
+
pluginBinDir: resolve12(binDir, "plugins"),
|
|
1939
|
+
contextPath: resolve12(rigRoot, "runtime-context.json"),
|
|
1940
|
+
controlPlaneEventsFile: resolve12(logsDir, "control-plane.events.jsonl")
|
|
1882
1941
|
};
|
|
1883
1942
|
}
|
|
1884
1943
|
function resolveActiveRuntimeWorkspaceRoot(monorepoRoot) {
|
|
@@ -1886,14 +1945,14 @@ function resolveActiveRuntimeWorkspaceRoot(monorepoRoot) {
|
|
|
1886
1945
|
if (!explicit) {
|
|
1887
1946
|
throw new Error("No active runtime workspace. Set RIG_TASK_WORKSPACE or provision a task runtime first.");
|
|
1888
1947
|
}
|
|
1889
|
-
return
|
|
1948
|
+
return resolve12(explicit);
|
|
1890
1949
|
}
|
|
1891
1950
|
function resolveRigLayout(projectRoot) {
|
|
1892
1951
|
const monorepoRoot = resolveMonorepoRoot(projectRoot);
|
|
1893
|
-
const definitionRoot =
|
|
1952
|
+
const definitionRoot = resolve12(projectRoot, RIG_DEFINITION_DIRNAME);
|
|
1894
1953
|
const runtimeWorkspaceRoot = resolveActiveRuntimeWorkspaceRoot(monorepoRoot);
|
|
1895
1954
|
const runtimeLayout = resolveRuntimeWorkspaceLayout(runtimeWorkspaceRoot);
|
|
1896
|
-
const policyDir =
|
|
1955
|
+
const policyDir = resolve12(definitionRoot, "policy");
|
|
1897
1956
|
return {
|
|
1898
1957
|
projectRoot,
|
|
1899
1958
|
monorepoRoot,
|
|
@@ -1901,48 +1960,48 @@ function resolveRigLayout(projectRoot) {
|
|
|
1901
1960
|
runtimeWorkspaceRoot,
|
|
1902
1961
|
stateRoot: runtimeLayout.rigRoot,
|
|
1903
1962
|
artifactsRoot: runtimeLayout.artifactsRoot,
|
|
1904
|
-
configPath:
|
|
1905
|
-
taskConfigPath:
|
|
1963
|
+
configPath: resolve12(definitionRoot, "config.sh"),
|
|
1964
|
+
taskConfigPath: resolve12(runtimeWorkspaceRoot, ".rig", "task-config.json"),
|
|
1906
1965
|
policyDir,
|
|
1907
|
-
policyFile:
|
|
1908
|
-
pluginsDir:
|
|
1909
|
-
hooksDir:
|
|
1910
|
-
toolsDir:
|
|
1911
|
-
templatesDir:
|
|
1912
|
-
validationDir:
|
|
1966
|
+
policyFile: resolve12(policyDir, "policy.json"),
|
|
1967
|
+
pluginsDir: resolve12(definitionRoot, "plugins"),
|
|
1968
|
+
hooksDir: resolve12(definitionRoot, "hooks"),
|
|
1969
|
+
toolsDir: resolve12(definitionRoot, "tools"),
|
|
1970
|
+
templatesDir: resolve12(definitionRoot, "templates"),
|
|
1971
|
+
validationDir: resolve12(definitionRoot, "validation"),
|
|
1913
1972
|
stateDir: runtimeLayout.stateDir,
|
|
1914
1973
|
logsDir: runtimeLayout.logsDir,
|
|
1915
|
-
notificationsDir:
|
|
1974
|
+
notificationsDir: resolve12(definitionRoot, "notifications"),
|
|
1916
1975
|
runtimeDir: runtimeLayout.runtimeDir,
|
|
1917
1976
|
distDir: runtimeLayout.distDir,
|
|
1918
1977
|
binDir: runtimeLayout.binDir,
|
|
1919
1978
|
pluginBinDir: runtimeLayout.pluginBinDir,
|
|
1920
|
-
keybindingsPath:
|
|
1979
|
+
keybindingsPath: resolve12(definitionRoot, "keybindings.json"),
|
|
1921
1980
|
controlPlaneEventsFile: runtimeLayout.controlPlaneEventsFile
|
|
1922
1981
|
};
|
|
1923
1982
|
}
|
|
1924
1983
|
|
|
1925
1984
|
// packages/runtime/src/control-plane/native/runtime-native.ts
|
|
1926
1985
|
import { dlopen, ptr, suffix, toBuffer } from "bun:ffi";
|
|
1927
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
1986
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync6, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
1928
1987
|
import { tmpdir as tmpdir4 } from "os";
|
|
1929
|
-
import { dirname as
|
|
1930
|
-
var sharedNativeRuntimeOutputDir =
|
|
1931
|
-
var sharedNativeRuntimeOutputPath =
|
|
1988
|
+
import { dirname as dirname8, resolve as resolve13 } from "path";
|
|
1989
|
+
var sharedNativeRuntimeOutputDir = resolve13(tmpdir4(), "rig-native");
|
|
1990
|
+
var sharedNativeRuntimeOutputPath = resolve13(sharedNativeRuntimeOutputDir, `runtime-native-${process.platform}-${process.arch}.${suffix}`);
|
|
1932
1991
|
var colocatedNativeRuntimeFileName = `runtime-native.${suffix}`;
|
|
1933
1992
|
var nativeRuntimeLibrary = await loadNativeRuntimeLibrary();
|
|
1934
1993
|
async function ensureNativeRuntimeLibraryPath(outputPath = sharedNativeRuntimeOutputPath, options = {}) {
|
|
1935
1994
|
if (await buildNativeRuntimeLibrary(outputPath, options)) {
|
|
1936
1995
|
return outputPath;
|
|
1937
1996
|
}
|
|
1938
|
-
return !options.force &&
|
|
1997
|
+
return !options.force && existsSync12(outputPath) ? outputPath : null;
|
|
1939
1998
|
}
|
|
1940
1999
|
async function loadNativeRuntimeLibrary() {
|
|
1941
2000
|
if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
|
|
1942
2001
|
return null;
|
|
1943
2002
|
}
|
|
1944
2003
|
for (const candidate of nativeRuntimeLibraryCandidates()) {
|
|
1945
|
-
if (!candidate || !
|
|
2004
|
+
if (!candidate || !existsSync12(candidate)) {
|
|
1946
2005
|
continue;
|
|
1947
2006
|
}
|
|
1948
2007
|
const loaded = tryDlopenNativeRuntimeLibrary(candidate);
|
|
@@ -1958,12 +2017,12 @@ async function loadNativeRuntimeLibrary() {
|
|
|
1958
2017
|
}
|
|
1959
2018
|
function nativePackageLibraryCandidates(fromDir, names) {
|
|
1960
2019
|
const candidates = [];
|
|
1961
|
-
let cursor =
|
|
2020
|
+
let cursor = resolve13(fromDir);
|
|
1962
2021
|
for (let index = 0;index < 8; index += 1) {
|
|
1963
2022
|
for (const name of names) {
|
|
1964
|
-
candidates.push(
|
|
2023
|
+
candidates.push(resolve13(cursor, "native", `${process.platform}-${process.arch}`, name), resolve13(cursor, "native", `${process.platform}-${process.arch}`, "lib", name), resolve13(cursor, "native", name), resolve13(cursor, "native", "lib", name));
|
|
1965
2024
|
}
|
|
1966
|
-
const parent =
|
|
2025
|
+
const parent = dirname8(cursor);
|
|
1967
2026
|
if (parent === cursor)
|
|
1968
2027
|
break;
|
|
1969
2028
|
cursor = parent;
|
|
@@ -1972,27 +2031,27 @@ function nativePackageLibraryCandidates(fromDir, names) {
|
|
|
1972
2031
|
}
|
|
1973
2032
|
function nativeRuntimeLibraryCandidates() {
|
|
1974
2033
|
const explicit = process.env.RIG_NATIVE_RUNTIME_LIB?.trim() || "";
|
|
1975
|
-
const execDir = process.execPath?.trim() ?
|
|
2034
|
+
const execDir = process.execPath?.trim() ? dirname8(process.execPath.trim()) : "";
|
|
1976
2035
|
const platformSpecific = `runtime-native-${process.platform}-${process.arch}.${suffix}`;
|
|
1977
2036
|
return [...new Set([
|
|
1978
2037
|
explicit,
|
|
1979
2038
|
...nativePackageLibraryCandidates(import.meta.dir, [colocatedNativeRuntimeFileName, platformSpecific]),
|
|
1980
|
-
execDir ?
|
|
1981
|
-
execDir ?
|
|
1982
|
-
execDir ?
|
|
1983
|
-
execDir ?
|
|
1984
|
-
execDir ?
|
|
1985
|
-
execDir ?
|
|
2039
|
+
execDir ? resolve13(execDir, colocatedNativeRuntimeFileName) : "",
|
|
2040
|
+
execDir ? resolve13(execDir, platformSpecific) : "",
|
|
2041
|
+
execDir ? resolve13(execDir, "..", colocatedNativeRuntimeFileName) : "",
|
|
2042
|
+
execDir ? resolve13(execDir, "..", platformSpecific) : "",
|
|
2043
|
+
execDir ? resolve13(execDir, "lib", colocatedNativeRuntimeFileName) : "",
|
|
2044
|
+
execDir ? resolve13(execDir, "..", "lib", colocatedNativeRuntimeFileName) : "",
|
|
1986
2045
|
sharedNativeRuntimeOutputPath
|
|
1987
2046
|
].filter(Boolean))];
|
|
1988
2047
|
}
|
|
1989
2048
|
function resolveNativeRuntimeSourcePath() {
|
|
1990
2049
|
const explicit = process.env.RIG_NATIVE_RUNTIME_SOURCE?.trim();
|
|
1991
|
-
if (explicit &&
|
|
2050
|
+
if (explicit && existsSync12(explicit)) {
|
|
1992
2051
|
return explicit;
|
|
1993
2052
|
}
|
|
1994
|
-
const bundled =
|
|
1995
|
-
return
|
|
2053
|
+
const bundled = resolve13(import.meta.dir, "../../../native/snapshot.zig");
|
|
2054
|
+
return existsSync12(bundled) ? bundled : null;
|
|
1996
2055
|
}
|
|
1997
2056
|
async function buildNativeRuntimeLibrary(outputPath, options = {}) {
|
|
1998
2057
|
if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
|
|
@@ -2005,8 +2064,8 @@ async function buildNativeRuntimeLibrary(outputPath, options = {}) {
|
|
|
2005
2064
|
}
|
|
2006
2065
|
const tempOutputPath = `${outputPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
2007
2066
|
try {
|
|
2008
|
-
|
|
2009
|
-
const needsBuild = options.force === true || !
|
|
2067
|
+
mkdirSync6(dirname8(outputPath), { recursive: true });
|
|
2068
|
+
const needsBuild = options.force === true || !existsSync12(outputPath) || statSync2(sourcePath).mtimeMs > statSync2(outputPath).mtimeMs;
|
|
2010
2069
|
if (!needsBuild) {
|
|
2011
2070
|
return true;
|
|
2012
2071
|
}
|
|
@@ -2024,7 +2083,7 @@ async function buildNativeRuntimeLibrary(outputPath, options = {}) {
|
|
|
2024
2083
|
stderr: "pipe"
|
|
2025
2084
|
});
|
|
2026
2085
|
const exitCode = await build.exited;
|
|
2027
|
-
if (exitCode !== 0 || !
|
|
2086
|
+
if (exitCode !== 0 || !existsSync12(tempOutputPath)) {
|
|
2028
2087
|
rmSync3(tempOutputPath, { force: true });
|
|
2029
2088
|
return false;
|
|
2030
2089
|
}
|
|
@@ -2154,11 +2213,11 @@ async function runCaptureAsync(command, cwd, env, timeoutMs) {
|
|
|
2154
2213
|
return { exitCode, stdout, stderr };
|
|
2155
2214
|
}
|
|
2156
2215
|
function readJsonFile(path, fallback) {
|
|
2157
|
-
if (!
|
|
2216
|
+
if (!existsSync13(path)) {
|
|
2158
2217
|
return fallback;
|
|
2159
2218
|
}
|
|
2160
2219
|
try {
|
|
2161
|
-
return JSON.parse(
|
|
2220
|
+
return JSON.parse(readFileSync9(path, "utf-8"));
|
|
2162
2221
|
} catch {
|
|
2163
2222
|
return fallback;
|
|
2164
2223
|
}
|
|
@@ -2172,31 +2231,31 @@ function unique(values) {
|
|
|
2172
2231
|
function resolveHarnessPaths(projectRoot) {
|
|
2173
2232
|
const hasRuntimeWorkspace = Boolean(process.env.RIG_TASK_WORKSPACE?.trim());
|
|
2174
2233
|
const monorepoRoot = resolveMonorepoRoot2(projectRoot);
|
|
2175
|
-
const harnessRoot =
|
|
2176
|
-
const stateRoot =
|
|
2234
|
+
const harnessRoot = resolve14(projectRoot, "rig");
|
|
2235
|
+
const stateRoot = resolve14(projectRoot, ".rig");
|
|
2177
2236
|
const layout = hasRuntimeWorkspace ? resolveRigLayout(projectRoot) : null;
|
|
2178
|
-
const stateDir = layout?.stateDir ??
|
|
2179
|
-
const logsDir = layout?.logsDir ??
|
|
2180
|
-
const artifactsDir = layout?.artifactsRoot ??
|
|
2181
|
-
const taskConfigPath = layout?.taskConfigPath ??
|
|
2182
|
-
const binDir = layout?.binDir ??
|
|
2237
|
+
const stateDir = layout?.stateDir ?? resolve14(stateRoot, "state");
|
|
2238
|
+
const logsDir = layout?.logsDir ?? resolve14(stateRoot, "logs");
|
|
2239
|
+
const artifactsDir = layout?.artifactsRoot ?? resolve14(monorepoRoot, "artifacts");
|
|
2240
|
+
const taskConfigPath = layout?.taskConfigPath ?? resolve14(monorepoRoot, ".rig", "task-config.json");
|
|
2241
|
+
const binDir = layout?.binDir ?? resolve14(stateRoot, "bin");
|
|
2183
2242
|
return {
|
|
2184
2243
|
harnessRoot,
|
|
2185
2244
|
stateDir: process.env.RIG_STATE_DIR || stateDir,
|
|
2186
2245
|
artifactsDir,
|
|
2187
2246
|
logsDir: process.env.RIG_LOGS_DIR || logsDir,
|
|
2188
2247
|
binDir,
|
|
2189
|
-
hooksDir:
|
|
2190
|
-
validationDir:
|
|
2248
|
+
hooksDir: resolve14(harnessRoot, "hooks"),
|
|
2249
|
+
validationDir: resolve14(harnessRoot, "validation"),
|
|
2191
2250
|
taskConfigPath,
|
|
2192
|
-
sessionPath: process.env.RIG_SESSION_FILE ||
|
|
2251
|
+
sessionPath: process.env.RIG_SESSION_FILE || resolve14(stateRoot, "session", "session.json"),
|
|
2193
2252
|
monorepoRoot,
|
|
2194
|
-
tsApiTestsDir: process.env.TS_API_TESTS_DIR ||
|
|
2195
|
-
taskRepoCommitsPath:
|
|
2196
|
-
baseRepoPinsPath:
|
|
2197
|
-
failedApproachesPath:
|
|
2198
|
-
agentProfilePath:
|
|
2199
|
-
reviewProfilePath:
|
|
2253
|
+
tsApiTestsDir: process.env.TS_API_TESTS_DIR || resolve14(monorepoRoot, "TSAPITests"),
|
|
2254
|
+
taskRepoCommitsPath: resolve14(stateDir, "task-repo-commits.json"),
|
|
2255
|
+
baseRepoPinsPath: resolve14(stateDir, "base-repo-pins.json"),
|
|
2256
|
+
failedApproachesPath: resolve14(stateDir, "failed_approaches.md"),
|
|
2257
|
+
agentProfilePath: resolve14(stateDir, "agent-profile.json"),
|
|
2258
|
+
reviewProfilePath: resolve14(stateDir, "review-profile.json")
|
|
2200
2259
|
};
|
|
2201
2260
|
}
|
|
2202
2261
|
function normalizeRelativeScopePath(inputPath) {
|
|
@@ -2254,34 +2313,34 @@ function monorepoSearchCandidates(inputPath) {
|
|
|
2254
2313
|
}
|
|
2255
2314
|
|
|
2256
2315
|
// packages/runtime/src/control-plane/state-sync/repo.ts
|
|
2257
|
-
import { existsSync as
|
|
2258
|
-
import { resolve as
|
|
2316
|
+
import { existsSync as existsSync15 } from "fs";
|
|
2317
|
+
import { resolve as resolve16 } from "path";
|
|
2259
2318
|
|
|
2260
2319
|
// packages/runtime/src/control-plane/repos/layout.ts
|
|
2261
|
-
import { existsSync as
|
|
2262
|
-
import { basename as basename5, dirname as
|
|
2320
|
+
import { existsSync as existsSync14 } from "fs";
|
|
2321
|
+
import { basename as basename5, dirname as dirname9, join as join3, resolve as resolve15 } from "path";
|
|
2263
2322
|
function resolveRepoStateDir(projectRoot) {
|
|
2264
|
-
const normalizedProjectRoot =
|
|
2265
|
-
const projectParent =
|
|
2323
|
+
const normalizedProjectRoot = resolve15(projectRoot);
|
|
2324
|
+
const projectParent = dirname9(normalizedProjectRoot);
|
|
2266
2325
|
if (basename5(projectParent) === ".worktrees") {
|
|
2267
|
-
const ownerRoot =
|
|
2268
|
-
const ownerHasRepoMarkers =
|
|
2326
|
+
const ownerRoot = dirname9(projectParent);
|
|
2327
|
+
const ownerHasRepoMarkers = existsSync14(resolve15(ownerRoot, ".git")) || existsSync14(resolve15(ownerRoot, ".rig", "state"));
|
|
2269
2328
|
if (ownerHasRepoMarkers) {
|
|
2270
|
-
return
|
|
2329
|
+
return resolve15(ownerRoot, ".rig", "state");
|
|
2271
2330
|
}
|
|
2272
2331
|
}
|
|
2273
|
-
return
|
|
2332
|
+
return resolve15(projectRoot, ".rig", "state");
|
|
2274
2333
|
}
|
|
2275
2334
|
function resolveManagedRepoLayout(projectRoot, repoId) {
|
|
2276
|
-
const normalizedProjectRoot =
|
|
2335
|
+
const normalizedProjectRoot = resolve15(projectRoot);
|
|
2277
2336
|
const entry = getManagedRepoEntry(repoId);
|
|
2278
2337
|
const stateDir = resolveRepoStateDir(normalizedProjectRoot);
|
|
2279
2338
|
const metadataRelativePath = join3("repos", entry.id);
|
|
2280
|
-
const metadataRoot =
|
|
2339
|
+
const metadataRoot = resolve15(stateDir, metadataRelativePath);
|
|
2281
2340
|
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
2282
|
-
const runsInsideTaskWorktree = runtimeWorkspace &&
|
|
2341
|
+
const runsInsideTaskWorktree = runtimeWorkspace && resolve15(runtimeWorkspace) === normalizedProjectRoot || basename5(dirname9(normalizedProjectRoot)) === ".worktrees";
|
|
2283
2342
|
const isPrimaryManagedRepo = listManagedRepoEntries()[0]?.id === repoId;
|
|
2284
|
-
const checkoutRoot = isPrimaryManagedRepo && runsInsideTaskWorktree ? resolveMonorepoRoot(normalizedProjectRoot) : entry.checkoutEnvVar && process.env[entry.checkoutEnvVar]?.trim() ?
|
|
2343
|
+
const checkoutRoot = isPrimaryManagedRepo && runsInsideTaskWorktree ? resolveMonorepoRoot(normalizedProjectRoot) : entry.checkoutEnvVar && process.env[entry.checkoutEnvVar]?.trim() ? resolve15(process.env[entry.checkoutEnvVar].trim()) : resolve15(normalizedProjectRoot, entry.alias);
|
|
2285
2344
|
return {
|
|
2286
2345
|
projectRoot: normalizedProjectRoot,
|
|
2287
2346
|
repoId: entry.id,
|
|
@@ -2289,12 +2348,12 @@ function resolveManagedRepoLayout(projectRoot, repoId) {
|
|
|
2289
2348
|
defaultBranch: entry.defaultBranch,
|
|
2290
2349
|
remoteUrl: entry.remoteEnvVar && process.env[entry.remoteEnvVar]?.trim() ? process.env[entry.remoteEnvVar].trim() : entry.defaultRemoteUrl,
|
|
2291
2350
|
checkoutRoot,
|
|
2292
|
-
worktreesRoot:
|
|
2351
|
+
worktreesRoot: resolve15(checkoutRoot, ".worktrees"),
|
|
2293
2352
|
stateDir,
|
|
2294
2353
|
metadataRoot,
|
|
2295
2354
|
metadataRelativePath,
|
|
2296
|
-
mirrorRoot:
|
|
2297
|
-
mirrorStatePath:
|
|
2355
|
+
mirrorRoot: resolve15(metadataRoot, "mirror.git"),
|
|
2356
|
+
mirrorStatePath: resolve15(metadataRoot, "mirror-state.json"),
|
|
2298
2357
|
mirrorStateRelativePath: join3(metadataRelativePath, "mirror-state.json")
|
|
2299
2358
|
};
|
|
2300
2359
|
}
|
|
@@ -2316,7 +2375,7 @@ function resolveTrackerRepoPath(projectRoot) {
|
|
|
2316
2375
|
const monorepoRoot = resolveMonorepoRoot2(projectRoot);
|
|
2317
2376
|
try {
|
|
2318
2377
|
const layout = resolveMonorepoRepoLayout(projectRoot);
|
|
2319
|
-
if (
|
|
2378
|
+
if (existsSync15(resolve16(layout.mirrorRoot, "HEAD"))) {
|
|
2320
2379
|
return layout.mirrorRoot;
|
|
2321
2380
|
}
|
|
2322
2381
|
} catch {}
|
|
@@ -2327,8 +2386,8 @@ function resolveTrackerRepoPath(projectRoot) {
|
|
|
2327
2386
|
var DEFAULT_READ_DEPS = {
|
|
2328
2387
|
fetchRef: nativeFetchRef,
|
|
2329
2388
|
readBlobAtRef: nativeReadBlobAtRef,
|
|
2330
|
-
exists:
|
|
2331
|
-
readFile: (path) =>
|
|
2389
|
+
exists: existsSync16,
|
|
2390
|
+
readFile: (path) => readFileSync10(path, "utf8")
|
|
2332
2391
|
};
|
|
2333
2392
|
function parseIssueStatus(rawStatus) {
|
|
2334
2393
|
const normalized = normalizeTaskLifecycleStatus(rawStatus);
|
|
@@ -2409,12 +2468,12 @@ function shouldPreferLocalTrackerState(options) {
|
|
|
2409
2468
|
if (runtimeContextPath) {
|
|
2410
2469
|
return true;
|
|
2411
2470
|
}
|
|
2412
|
-
return
|
|
2471
|
+
return existsSync16(resolve17(runtimeWorkspace, ".rig", "runtime-context.json"));
|
|
2413
2472
|
}
|
|
2414
2473
|
function readLocalTrackerState(projectRoot, deps) {
|
|
2415
2474
|
const monorepoRoot = resolveMonorepoRoot2(projectRoot);
|
|
2416
|
-
const issuesPath =
|
|
2417
|
-
const taskStatePath =
|
|
2475
|
+
const issuesPath = resolve17(monorepoRoot, ".beads", "issues.jsonl");
|
|
2476
|
+
const taskStatePath = resolve17(monorepoRoot, ".beads", "task-state.json");
|
|
2418
2477
|
return projectSyncedTrackerSnapshot({
|
|
2419
2478
|
source: "local",
|
|
2420
2479
|
issuesBaseOid: null,
|
|
@@ -2476,7 +2535,7 @@ function readValidationDescriptions(projectRoot) {
|
|
|
2476
2535
|
return readValidationDescriptionMap(raw);
|
|
2477
2536
|
}
|
|
2478
2537
|
function readSourceValidationDescriptions(projectRoot) {
|
|
2479
|
-
const rootRaw = readJsonFile(
|
|
2538
|
+
const rootRaw = readJsonFile(resolve18(projectRoot, "rig", "task-config.json"), {});
|
|
2480
2539
|
const sourcePath = findSourceTaskConfigPath(projectRoot);
|
|
2481
2540
|
const sourceRaw = sourcePath ? readJsonFile(sourcePath, {}) : {};
|
|
2482
2541
|
const rootDescriptions = readValidationDescriptionMap(rootRaw);
|
|
@@ -2552,15 +2611,15 @@ function readValidationDescriptionsFromMeta(meta) {
|
|
|
2552
2611
|
return meta.validation_descriptions;
|
|
2553
2612
|
}
|
|
2554
2613
|
function readLocalSourceTaskStateEnvelope(projectRoot) {
|
|
2555
|
-
const taskStatePath =
|
|
2614
|
+
const taskStatePath = resolve18(resolveMonorepoRoot2(projectRoot), ".beads", "task-state.json");
|
|
2556
2615
|
return readTaskStateMetadataEnvelope(readJsonFile(taskStatePath, {}));
|
|
2557
2616
|
}
|
|
2558
2617
|
function readLocalSourceTaskLifecycleStatus(projectRoot, taskId) {
|
|
2559
|
-
const issuesPath =
|
|
2560
|
-
if (!
|
|
2618
|
+
const issuesPath = resolve18(resolveMonorepoRoot2(projectRoot), ".beads", "issues.jsonl");
|
|
2619
|
+
if (!existsSync17(issuesPath)) {
|
|
2561
2620
|
return null;
|
|
2562
2621
|
}
|
|
2563
|
-
for (const line of
|
|
2622
|
+
for (const line of readFileSync11(issuesPath, "utf8").split(/\r?\n/)) {
|
|
2564
2623
|
const trimmed = line.trim();
|
|
2565
2624
|
if (!trimmed) {
|
|
2566
2625
|
continue;
|
|
@@ -2601,25 +2660,25 @@ function lookupTask(projectRoot, input) {
|
|
|
2601
2660
|
function artifactDirForId(projectRoot, id) {
|
|
2602
2661
|
const workspaceDir = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
2603
2662
|
if (workspaceDir) {
|
|
2604
|
-
const worktreeArtifacts =
|
|
2605
|
-
if (
|
|
2663
|
+
const worktreeArtifacts = resolve18(workspaceDir, "artifacts", id);
|
|
2664
|
+
if (existsSync17(worktreeArtifacts) || existsSync17(resolve18(workspaceDir, "artifacts"))) {
|
|
2606
2665
|
return worktreeArtifacts;
|
|
2607
2666
|
}
|
|
2608
2667
|
}
|
|
2609
2668
|
try {
|
|
2610
2669
|
const paths = resolveHarnessPaths(projectRoot);
|
|
2611
|
-
return
|
|
2670
|
+
return resolve18(paths.artifactsDir, id);
|
|
2612
2671
|
} catch {
|
|
2613
|
-
return
|
|
2672
|
+
return resolve18(resolveMonorepoRoot2(projectRoot), "artifacts", id);
|
|
2614
2673
|
}
|
|
2615
2674
|
}
|
|
2616
2675
|
function resolveTaskConfigPath(projectRoot) {
|
|
2617
2676
|
const paths = resolveHarnessPaths(projectRoot);
|
|
2618
|
-
if (
|
|
2677
|
+
if (existsSync17(paths.taskConfigPath)) {
|
|
2619
2678
|
return paths.taskConfigPath;
|
|
2620
2679
|
}
|
|
2621
2680
|
for (const candidate of sourceTaskConfigCandidates(projectRoot)) {
|
|
2622
|
-
if (
|
|
2681
|
+
if (existsSync17(candidate)) {
|
|
2623
2682
|
return candidate;
|
|
2624
2683
|
}
|
|
2625
2684
|
}
|
|
@@ -2627,7 +2686,7 @@ function resolveTaskConfigPath(projectRoot) {
|
|
|
2627
2686
|
}
|
|
2628
2687
|
function findSourceTaskConfigPath(projectRoot) {
|
|
2629
2688
|
for (const candidate of sourceTaskConfigCandidates(projectRoot)) {
|
|
2630
|
-
if (
|
|
2689
|
+
if (existsSync17(candidate)) {
|
|
2631
2690
|
return candidate;
|
|
2632
2691
|
}
|
|
2633
2692
|
}
|
|
@@ -2640,7 +2699,7 @@ function readAndSyncSourceTaskConfig(projectRoot) {
|
|
|
2640
2699
|
const synced = synchronizeTaskConfigWithTracker(projectRoot, raw);
|
|
2641
2700
|
if (sourcePath && synced.updated) {
|
|
2642
2701
|
try {
|
|
2643
|
-
|
|
2702
|
+
writeFileSync7(sourcePath, `${JSON.stringify(synced.config, null, 2)}
|
|
2644
2703
|
`, "utf-8");
|
|
2645
2704
|
} catch {}
|
|
2646
2705
|
}
|
|
@@ -2692,12 +2751,12 @@ function shouldRefreshAutoSyncedTaskConfigEntry(entry) {
|
|
|
2692
2751
|
return !candidate.role;
|
|
2693
2752
|
}
|
|
2694
2753
|
function readSourceIssueRecords(projectRoot) {
|
|
2695
|
-
const issuesPath =
|
|
2696
|
-
if (!
|
|
2754
|
+
const issuesPath = resolve18(resolveMonorepoRoot2(projectRoot), ".beads", "issues.jsonl");
|
|
2755
|
+
if (!existsSync17(issuesPath)) {
|
|
2697
2756
|
return [];
|
|
2698
2757
|
}
|
|
2699
2758
|
const records = [];
|
|
2700
|
-
for (const line of
|
|
2759
|
+
for (const line of readFileSync11(issuesPath, "utf-8").split(/\r?\n/)) {
|
|
2701
2760
|
const trimmed = line.trim();
|
|
2702
2761
|
if (!trimmed) {
|
|
2703
2762
|
continue;
|
|
@@ -2753,19 +2812,19 @@ function readConfiguredFileTaskConfig(projectRoot) {
|
|
|
2753
2812
|
if (!sourcePath) {
|
|
2754
2813
|
return {};
|
|
2755
2814
|
}
|
|
2756
|
-
const directory =
|
|
2757
|
-
if (!
|
|
2815
|
+
const directory = resolve18(projectRoot, sourcePath);
|
|
2816
|
+
if (!existsSync17(directory)) {
|
|
2758
2817
|
return {};
|
|
2759
2818
|
}
|
|
2760
2819
|
const config = {};
|
|
2761
2820
|
for (const name of readdirSync3(directory)) {
|
|
2762
2821
|
if (!FILE_TASK_PATTERN2.test(name))
|
|
2763
2822
|
continue;
|
|
2764
|
-
const file =
|
|
2823
|
+
const file = resolve18(directory, name);
|
|
2765
2824
|
try {
|
|
2766
2825
|
if (!statSync3(file).isFile())
|
|
2767
2826
|
continue;
|
|
2768
|
-
const raw = JSON.parse(
|
|
2827
|
+
const raw = JSON.parse(readFileSync11(file, "utf8"));
|
|
2769
2828
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
2770
2829
|
continue;
|
|
2771
2830
|
const record = raw;
|
|
@@ -2807,10 +2866,10 @@ function firstStringList2(...candidates) {
|
|
|
2807
2866
|
return [];
|
|
2808
2867
|
}
|
|
2809
2868
|
function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
2810
|
-
const jsonPath =
|
|
2811
|
-
if (
|
|
2869
|
+
const jsonPath = resolve18(projectRoot, "rig.config.json");
|
|
2870
|
+
if (existsSync17(jsonPath)) {
|
|
2812
2871
|
try {
|
|
2813
|
-
const parsed = JSON.parse(
|
|
2872
|
+
const parsed = JSON.parse(readFileSync11(jsonPath, "utf8"));
|
|
2814
2873
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2815
2874
|
const taskSource = parsed.taskSource;
|
|
2816
2875
|
if (taskSource && typeof taskSource === "object" && !Array.isArray(taskSource)) {
|
|
@@ -2822,12 +2881,12 @@ function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
|
2822
2881
|
return null;
|
|
2823
2882
|
}
|
|
2824
2883
|
}
|
|
2825
|
-
const tsPath =
|
|
2826
|
-
if (!
|
|
2884
|
+
const tsPath = resolve18(projectRoot, "rig.config.ts");
|
|
2885
|
+
if (!existsSync17(tsPath)) {
|
|
2827
2886
|
return null;
|
|
2828
2887
|
}
|
|
2829
2888
|
try {
|
|
2830
|
-
const source =
|
|
2889
|
+
const source = readFileSync11(tsPath, "utf8");
|
|
2831
2890
|
const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
|
|
2832
2891
|
const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
|
|
2833
2892
|
if (kind !== "files") {
|
|
@@ -2841,23 +2900,23 @@ function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
|
2841
2900
|
function sourceTaskConfigCandidates(projectRoot) {
|
|
2842
2901
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
2843
2902
|
return [
|
|
2844
|
-
runtimeContext?.monorepoMainRoot ?
|
|
2845
|
-
process.env.MONOREPO_MAIN_ROOT?.trim() ?
|
|
2846
|
-
|
|
2903
|
+
runtimeContext?.monorepoMainRoot ? resolve18(runtimeContext.monorepoMainRoot, ".rig", "task-config.json") : "",
|
|
2904
|
+
process.env.MONOREPO_MAIN_ROOT?.trim() ? resolve18(process.env.MONOREPO_MAIN_ROOT.trim(), ".rig", "task-config.json") : "",
|
|
2905
|
+
resolve18(resolveMonorepoRoot2(projectRoot), ".rig", "task-config.json")
|
|
2847
2906
|
].filter(Boolean);
|
|
2848
2907
|
}
|
|
2849
2908
|
|
|
2850
2909
|
// packages/runtime/src/control-plane/native/validator.ts
|
|
2851
|
-
import { existsSync as
|
|
2852
|
-
import { resolve as
|
|
2910
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
2911
|
+
import { resolve as resolve23 } from "path";
|
|
2853
2912
|
|
|
2854
2913
|
// packages/runtime/src/control-plane/native/validator-binaries.ts
|
|
2855
|
-
import { existsSync as
|
|
2856
|
-
import { dirname as
|
|
2914
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync8, rmSync as rmSync5, statSync as statSync4 } from "fs";
|
|
2915
|
+
import { dirname as dirname11, resolve as resolve22 } from "path";
|
|
2857
2916
|
|
|
2858
2917
|
// packages/runtime/src/binary-run.ts
|
|
2859
|
-
import { chmodSync as chmodSync2, cpSync, existsSync as
|
|
2860
|
-
import { basename as basename7, dirname as
|
|
2918
|
+
import { chmodSync as chmodSync2, cpSync, existsSync as existsSync18, mkdirSync as mkdirSync7, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
2919
|
+
import { basename as basename7, dirname as dirname10, resolve as resolve19 } from "path";
|
|
2861
2920
|
import { fileURLToPath } from "url";
|
|
2862
2921
|
import { drainMicrotasks, gcAndSweep } from "bun:jsc";
|
|
2863
2922
|
var runtimeBinaryBuildQueue = Promise.resolve();
|
|
@@ -2883,9 +2942,9 @@ async function buildRuntimeBinary(options) {
|
|
|
2883
2942
|
});
|
|
2884
2943
|
}
|
|
2885
2944
|
async function buildRuntimeBinaryInProcess(options, manifest) {
|
|
2886
|
-
const tempBuildDir =
|
|
2887
|
-
const tempOutputPath =
|
|
2888
|
-
|
|
2945
|
+
const tempBuildDir = resolve19(dirname10(options.outputPath), `.bun-build-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
2946
|
+
const tempOutputPath = resolve19(tempBuildDir, basename7(options.outputPath));
|
|
2947
|
+
mkdirSync7(tempBuildDir, { recursive: true });
|
|
2889
2948
|
await withTemporaryEnv({
|
|
2890
2949
|
...options.env,
|
|
2891
2950
|
...options.define ? { RIG_BUILD_CONFIG_JSON: JSON.stringify(options.define) } : {}
|
|
@@ -2910,7 +2969,7 @@ async function buildRuntimeBinaryInProcess(options, manifest) {
|
|
|
2910
2969
|
`);
|
|
2911
2970
|
throw new Error(`Failed to build ${options.entrypoint}: ${details || "Bun.build() returned errors"}`);
|
|
2912
2971
|
}
|
|
2913
|
-
if (!
|
|
2972
|
+
if (!existsSync18(tempOutputPath)) {
|
|
2914
2973
|
const emitted = buildResult.outputs.map((output) => output.path).join(", ") || "(none)";
|
|
2915
2974
|
throw new Error(`Failed to build ${options.entrypoint}: Bun.build() did not emit ${tempOutputPath}. Emitted: ${emitted}`);
|
|
2916
2975
|
}
|
|
@@ -2942,8 +3001,8 @@ function runtimeBinaryCacheManifestPath(outputPath) {
|
|
|
2942
3001
|
function resolveRuntimeBinaryBuildOptions(options) {
|
|
2943
3002
|
return {
|
|
2944
3003
|
...options,
|
|
2945
|
-
entrypoint:
|
|
2946
|
-
outputPath:
|
|
3004
|
+
entrypoint: resolve19(options.cwd, options.sourcePath),
|
|
3005
|
+
outputPath: resolve19(options.outputPath)
|
|
2947
3006
|
};
|
|
2948
3007
|
}
|
|
2949
3008
|
function shouldUseRuntimeBinaryBuildWorker() {
|
|
@@ -2957,7 +3016,7 @@ function shouldUseRuntimeBinaryBuildWorker() {
|
|
|
2957
3016
|
}
|
|
2958
3017
|
async function buildRuntimeBinaryViaWorker(options) {
|
|
2959
3018
|
const workerSourcePath = resolveRuntimeBinaryBuildWorkerSourcePath(options);
|
|
2960
|
-
if (!workerSourcePath || !
|
|
3019
|
+
if (!workerSourcePath || !existsSync18(workerSourcePath)) {
|
|
2961
3020
|
await buildRuntimeBinaryInProcess(options, {
|
|
2962
3021
|
manifestPath: runtimeBinaryCacheManifestPath(options.outputPath),
|
|
2963
3022
|
buildKey: createRuntimeBinaryBuildKey({
|
|
@@ -2994,7 +3053,7 @@ async function buildRuntimeBinaryViaWorker(options) {
|
|
|
2994
3053
|
}
|
|
2995
3054
|
}
|
|
2996
3055
|
function createRuntimeBinaryBuildWorkerPayloadPath(outputPath) {
|
|
2997
|
-
return
|
|
3056
|
+
return resolve19(dirname10(outputPath), `.bun-build-worker-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
2998
3057
|
}
|
|
2999
3058
|
function resolveRuntimeBinaryBuildWorkerSourcePath(options) {
|
|
3000
3059
|
const envRoots = [
|
|
@@ -3003,13 +3062,13 @@ function resolveRuntimeBinaryBuildWorkerSourcePath(options) {
|
|
|
3003
3062
|
process.env.PROJECT_RIG_ROOT?.trim()
|
|
3004
3063
|
].filter(Boolean);
|
|
3005
3064
|
for (const root of envRoots) {
|
|
3006
|
-
const candidate =
|
|
3007
|
-
if (
|
|
3065
|
+
const candidate = resolve19(root, "packages/runtime/src/binary-build-worker.ts");
|
|
3066
|
+
if (existsSync18(candidate)) {
|
|
3008
3067
|
return candidate;
|
|
3009
3068
|
}
|
|
3010
3069
|
}
|
|
3011
|
-
const localCandidate =
|
|
3012
|
-
return
|
|
3070
|
+
const localCandidate = resolve19(import.meta.dir, "binary-build-worker.ts");
|
|
3071
|
+
return existsSync18(localCandidate) ? localCandidate : null;
|
|
3013
3072
|
}
|
|
3014
3073
|
function resolveRuntimeBinaryBuildWorkerInvocation() {
|
|
3015
3074
|
const bunPath = Bun.which("bun");
|
|
@@ -3045,7 +3104,7 @@ function createRuntimeBinaryBuildKey(input) {
|
|
|
3045
3104
|
});
|
|
3046
3105
|
}
|
|
3047
3106
|
async function isRuntimeBinaryBuildFresh(input) {
|
|
3048
|
-
if (!
|
|
3107
|
+
if (!existsSync18(input.outputPath) || !existsSync18(input.manifestPath)) {
|
|
3049
3108
|
return false;
|
|
3050
3109
|
}
|
|
3051
3110
|
let manifest = null;
|
|
@@ -3058,7 +3117,7 @@ async function isRuntimeBinaryBuildFresh(input) {
|
|
|
3058
3117
|
return false;
|
|
3059
3118
|
}
|
|
3060
3119
|
for (const [filePath, expectedDigest] of Object.entries(manifest.inputs || {})) {
|
|
3061
|
-
if (!
|
|
3120
|
+
if (!existsSync18(filePath)) {
|
|
3062
3121
|
return false;
|
|
3063
3122
|
}
|
|
3064
3123
|
if (await sha256File(filePath) !== expectedDigest) {
|
|
@@ -3071,7 +3130,7 @@ async function writeRuntimeBinaryCacheManifest(input) {
|
|
|
3071
3130
|
const inputs = {};
|
|
3072
3131
|
for (const inputPath of Object.keys(input.metafile?.inputs || {}).sort()) {
|
|
3073
3132
|
const normalized = normalizeBuildInputPath(input.cwd, inputPath);
|
|
3074
|
-
if (!normalized || !
|
|
3133
|
+
if (!normalized || !existsSync18(normalized)) {
|
|
3075
3134
|
continue;
|
|
3076
3135
|
}
|
|
3077
3136
|
inputs[normalized] = await sha256File(normalized);
|
|
@@ -3094,7 +3153,7 @@ function normalizeBuildInputPath(cwd, inputPath) {
|
|
|
3094
3153
|
if (inputPath.startsWith("<")) {
|
|
3095
3154
|
return null;
|
|
3096
3155
|
}
|
|
3097
|
-
return
|
|
3156
|
+
return resolve19(cwd, inputPath);
|
|
3098
3157
|
}
|
|
3099
3158
|
async function sha256File(path) {
|
|
3100
3159
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
@@ -3110,8 +3169,8 @@ function sortRecord(value) {
|
|
|
3110
3169
|
async function runSerializedRuntimeBinaryBuild(action) {
|
|
3111
3170
|
const previous = runtimeBinaryBuildQueue;
|
|
3112
3171
|
let release;
|
|
3113
|
-
runtimeBinaryBuildQueue = new Promise((
|
|
3114
|
-
release =
|
|
3172
|
+
runtimeBinaryBuildQueue = new Promise((resolve20) => {
|
|
3173
|
+
release = resolve20;
|
|
3115
3174
|
});
|
|
3116
3175
|
await previous;
|
|
3117
3176
|
try {
|
|
@@ -3156,11 +3215,11 @@ async function withTemporaryCwd(cwd, action) {
|
|
|
3156
3215
|
}
|
|
3157
3216
|
|
|
3158
3217
|
// packages/runtime/src/control-plane/runtime/provisioning-env.ts
|
|
3159
|
-
import { delimiter, resolve as
|
|
3218
|
+
import { delimiter, resolve as resolve21 } from "path";
|
|
3160
3219
|
|
|
3161
3220
|
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
3162
|
-
import { existsSync as
|
|
3163
|
-
import { resolve as
|
|
3221
|
+
import { existsSync as existsSync19, readdirSync as readdirSync4, realpathSync } from "fs";
|
|
3222
|
+
import { resolve as resolve20 } from "path";
|
|
3164
3223
|
|
|
3165
3224
|
// packages/runtime/src/control-plane/runtime/sandbox/utils.ts
|
|
3166
3225
|
function uniq(values) {
|
|
@@ -3178,7 +3237,7 @@ function resolveBunBinaryPath() {
|
|
|
3178
3237
|
}
|
|
3179
3238
|
const home = process.env.HOME?.trim();
|
|
3180
3239
|
const fallbackCandidates = [
|
|
3181
|
-
home ?
|
|
3240
|
+
home ? resolve20(home, ".bun/bin/bun") : "",
|
|
3182
3241
|
"/opt/homebrew/bin/bun",
|
|
3183
3242
|
"/usr/local/bin/bun",
|
|
3184
3243
|
"/usr/bin/bun"
|
|
@@ -3206,8 +3265,8 @@ function resolveClaudeBinaryPath() {
|
|
|
3206
3265
|
}
|
|
3207
3266
|
const home = process.env.HOME?.trim();
|
|
3208
3267
|
const fallbackCandidates = [
|
|
3209
|
-
home ?
|
|
3210
|
-
home ?
|
|
3268
|
+
home ? resolve20(home, ".local/bin/claude") : "",
|
|
3269
|
+
home ? resolve20(home, ".local/share/claude/local/claude") : "",
|
|
3211
3270
|
"/opt/homebrew/bin/claude",
|
|
3212
3271
|
"/usr/local/bin/claude",
|
|
3213
3272
|
"/usr/bin/claude"
|
|
@@ -3221,51 +3280,51 @@ function resolveClaudeBinaryPath() {
|
|
|
3221
3280
|
throw new Error("claude not found in PATH");
|
|
3222
3281
|
}
|
|
3223
3282
|
function resolveBunInstallDir(bunBinaryPath = resolveBunBinaryPath()) {
|
|
3224
|
-
return
|
|
3283
|
+
return resolve20(bunBinaryPath, "../..");
|
|
3225
3284
|
}
|
|
3226
3285
|
function resolveClaudeInstallDir() {
|
|
3227
3286
|
const realPath = resolveClaudeBinaryPath();
|
|
3228
|
-
return
|
|
3287
|
+
return resolve20(realPath, "..");
|
|
3229
3288
|
}
|
|
3230
3289
|
function resolveNodeInstallDir() {
|
|
3231
3290
|
const preferredNode = resolvePreferredNodeBinary();
|
|
3232
3291
|
if (!preferredNode)
|
|
3233
3292
|
return null;
|
|
3234
3293
|
const explicitNode = process.env.RIG_NODE_BIN?.trim();
|
|
3235
|
-
if (explicitNode &&
|
|
3236
|
-
return preferredNode.endsWith("/bin/node") ?
|
|
3294
|
+
if (explicitNode && resolve20(explicitNode) === resolve20(preferredNode)) {
|
|
3295
|
+
return preferredNode.endsWith("/bin/node") ? resolve20(preferredNode, "../..") : resolve20(preferredNode, "..");
|
|
3237
3296
|
}
|
|
3238
3297
|
try {
|
|
3239
3298
|
const realPath = realpathSync(preferredNode);
|
|
3240
3299
|
if (realPath.endsWith("/bin/node")) {
|
|
3241
|
-
return
|
|
3300
|
+
return resolve20(realPath, "../..");
|
|
3242
3301
|
}
|
|
3243
|
-
return
|
|
3302
|
+
return resolve20(realPath, "..");
|
|
3244
3303
|
} catch {
|
|
3245
|
-
return
|
|
3304
|
+
return resolve20(preferredNode, "..");
|
|
3246
3305
|
}
|
|
3247
3306
|
}
|
|
3248
3307
|
function resolvePreferredNodeBinary() {
|
|
3249
3308
|
const candidates = [];
|
|
3250
3309
|
const envNode = process.env.RIG_NODE_BIN?.trim();
|
|
3251
3310
|
if (envNode) {
|
|
3252
|
-
const explicit =
|
|
3253
|
-
if (
|
|
3311
|
+
const explicit = resolve20(envNode);
|
|
3312
|
+
if (existsSync19(explicit)) {
|
|
3254
3313
|
return explicit;
|
|
3255
3314
|
}
|
|
3256
3315
|
}
|
|
3257
3316
|
const nvmBin = process.env.NVM_BIN?.trim();
|
|
3258
3317
|
if (nvmBin) {
|
|
3259
|
-
candidates.push(
|
|
3318
|
+
candidates.push(resolve20(nvmBin, "node"));
|
|
3260
3319
|
}
|
|
3261
3320
|
const home = process.env.HOME?.trim();
|
|
3262
3321
|
if (home) {
|
|
3263
|
-
const nvmVersionsDir =
|
|
3264
|
-
if (
|
|
3322
|
+
const nvmVersionsDir = resolve20(home, ".nvm/versions/node");
|
|
3323
|
+
if (existsSync19(nvmVersionsDir)) {
|
|
3265
3324
|
try {
|
|
3266
3325
|
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/, "")));
|
|
3267
3326
|
for (const versionDir of versionDirs) {
|
|
3268
|
-
candidates.push(
|
|
3327
|
+
candidates.push(resolve20(nvmVersionsDir, versionDir, "bin/node"));
|
|
3269
3328
|
}
|
|
3270
3329
|
} catch {}
|
|
3271
3330
|
}
|
|
@@ -3274,8 +3333,8 @@ function resolvePreferredNodeBinary() {
|
|
|
3274
3333
|
if (whichNode) {
|
|
3275
3334
|
candidates.push(whichNode);
|
|
3276
3335
|
}
|
|
3277
|
-
const deduped = uniq(candidates.map((candidate) =>
|
|
3278
|
-
const existing = deduped.filter((candidate) =>
|
|
3336
|
+
const deduped = uniq(candidates.map((candidate) => resolve20(candidate)));
|
|
3337
|
+
const existing = deduped.filter((candidate) => existsSync19(candidate));
|
|
3279
3338
|
if (existing.length === 0) {
|
|
3280
3339
|
return null;
|
|
3281
3340
|
}
|
|
@@ -3289,7 +3348,7 @@ function resolvePreferredNodeBinary() {
|
|
|
3289
3348
|
return existing[0] ?? null;
|
|
3290
3349
|
}
|
|
3291
3350
|
function inferNodeMajor(nodeBinaryPath) {
|
|
3292
|
-
const normalized =
|
|
3351
|
+
const normalized = resolve20(nodeBinaryPath).replace(/\\/g, "/");
|
|
3293
3352
|
const match = normalized.match(/(?:^|\/)(?:node-)?v?(\d+)\.\d+\.\d+(?:\/|$)/);
|
|
3294
3353
|
if (!match) {
|
|
3295
3354
|
return null;
|
|
@@ -3301,8 +3360,8 @@ function normalizeExecutablePath(candidate) {
|
|
|
3301
3360
|
if (!candidate) {
|
|
3302
3361
|
return "";
|
|
3303
3362
|
}
|
|
3304
|
-
const normalized =
|
|
3305
|
-
if (!
|
|
3363
|
+
const normalized = resolve20(candidate);
|
|
3364
|
+
if (!existsSync19(normalized)) {
|
|
3306
3365
|
return "";
|
|
3307
3366
|
}
|
|
3308
3367
|
try {
|
|
@@ -3312,7 +3371,7 @@ function normalizeExecutablePath(candidate) {
|
|
|
3312
3371
|
}
|
|
3313
3372
|
}
|
|
3314
3373
|
function looksLikeRuntimeGateway(candidate) {
|
|
3315
|
-
const normalized =
|
|
3374
|
+
const normalized = resolve20(candidate).replace(/\\/g, "/");
|
|
3316
3375
|
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
3317
3376
|
}
|
|
3318
3377
|
|
|
@@ -3333,7 +3392,7 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3333
3392
|
try {
|
|
3334
3393
|
return resolveClaudeInstallDir();
|
|
3335
3394
|
} catch {
|
|
3336
|
-
return
|
|
3395
|
+
return resolve21(claudeBinary, "..");
|
|
3337
3396
|
}
|
|
3338
3397
|
})() : "";
|
|
3339
3398
|
const nodeDir = resolveNodeInstallDir();
|
|
@@ -3343,8 +3402,8 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3343
3402
|
`${bunDir}/bin`,
|
|
3344
3403
|
claudeDir,
|
|
3345
3404
|
nodeDir ? `${nodeDir}/bin` : "",
|
|
3346
|
-
realHome ?
|
|
3347
|
-
realHome ?
|
|
3405
|
+
realHome ? resolve21(realHome, ".local/bin") : "",
|
|
3406
|
+
realHome ? resolve21(realHome, ".cargo/bin") : "",
|
|
3348
3407
|
...inheritedPath,
|
|
3349
3408
|
"/usr/local/bin",
|
|
3350
3409
|
"/usr/local/sbin",
|
|
@@ -3375,9 +3434,9 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3375
3434
|
// packages/runtime/src/control-plane/native/validator-binaries.ts
|
|
3376
3435
|
function resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext) {
|
|
3377
3436
|
if (runtimeContext) {
|
|
3378
|
-
return
|
|
3437
|
+
return resolve22(runtimeContext.binDir, "validators", binaryName);
|
|
3379
3438
|
}
|
|
3380
|
-
return
|
|
3439
|
+
return resolve22(resolveHarnessPaths(projectRoot).binDir, "validators", binaryName);
|
|
3381
3440
|
}
|
|
3382
3441
|
async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
3383
3442
|
const match = checkId.match(/^([a-z][\w-]*):([a-z][\w-]*)$/);
|
|
@@ -3392,19 +3451,19 @@ async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
|
3392
3451
|
const binaryName = `${category}-${check}`;
|
|
3393
3452
|
const binaryPath = resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext);
|
|
3394
3453
|
const hostProjectRoot = runtimeContext?.hostProjectRoot?.trim() || projectRoot;
|
|
3395
|
-
const sourcePath =
|
|
3396
|
-
if (!
|
|
3454
|
+
const sourcePath = resolve22(hostProjectRoot, "packages/runtime/src/control-plane/validators", category, `${check}.ts`);
|
|
3455
|
+
if (!existsSync20(sourcePath)) {
|
|
3397
3456
|
return null;
|
|
3398
3457
|
}
|
|
3399
3458
|
const sourceMtime = statSync4(sourcePath).mtimeMs;
|
|
3400
|
-
const binaryExists =
|
|
3459
|
+
const binaryExists = existsSync20(binaryPath);
|
|
3401
3460
|
const binaryMtime = binaryExists ? statSync4(binaryPath).mtimeMs : 0;
|
|
3402
3461
|
if (!binaryExists || sourceMtime > binaryMtime) {
|
|
3403
3462
|
if (binaryExists) {
|
|
3404
3463
|
rmSync5(binaryPath, { force: true });
|
|
3405
3464
|
rmSync5(`${binaryPath}.build-manifest.json`, { force: true });
|
|
3406
3465
|
}
|
|
3407
|
-
|
|
3466
|
+
mkdirSync8(dirname11(binaryPath), { recursive: true });
|
|
3408
3467
|
await buildRuntimeBinary({
|
|
3409
3468
|
sourcePath: `packages/runtime/src/control-plane/validators/${category}/${check}.ts`,
|
|
3410
3469
|
outputPath: binaryPath,
|
|
@@ -3413,7 +3472,7 @@ async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
|
3413
3472
|
env: runtimeProvisioningEnv()
|
|
3414
3473
|
});
|
|
3415
3474
|
}
|
|
3416
|
-
return
|
|
3475
|
+
return existsSync20(binaryPath) ? binaryPath : null;
|
|
3417
3476
|
}
|
|
3418
3477
|
|
|
3419
3478
|
// packages/runtime/src/control-plane/native/validator.ts
|
|
@@ -3450,20 +3509,20 @@ async function readTaskSourceValidation(projectRoot, taskId) {
|
|
|
3450
3509
|
function resolveValidationPaths(projectRoot, taskId, runtimeContext) {
|
|
3451
3510
|
if (runtimeContext) {
|
|
3452
3511
|
return {
|
|
3453
|
-
taskLogDir:
|
|
3454
|
-
artifactDir:
|
|
3512
|
+
taskLogDir: resolve23(runtimeContext.logsDir, taskId),
|
|
3513
|
+
artifactDir: resolve23(runtimeContext.workspaceDir, "artifacts", taskId)
|
|
3455
3514
|
};
|
|
3456
3515
|
}
|
|
3457
3516
|
const paths = resolveHarnessPaths(projectRoot);
|
|
3458
3517
|
return {
|
|
3459
|
-
taskLogDir:
|
|
3460
|
-
artifactDir:
|
|
3518
|
+
taskLogDir: resolve23(paths.logsDir, taskId),
|
|
3519
|
+
artifactDir: resolve23(paths.artifactsDir, taskId)
|
|
3461
3520
|
};
|
|
3462
3521
|
}
|
|
3463
3522
|
async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext) {
|
|
3464
3523
|
const binaryName = checkId.replace(":", "-");
|
|
3465
3524
|
const binaryPath = await ensureValidatorBinary(projectRoot, checkId, runtimeContext) ?? resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext);
|
|
3466
|
-
if (!
|
|
3525
|
+
if (!existsSync21(binaryPath)) {
|
|
3467
3526
|
return {
|
|
3468
3527
|
result: {
|
|
3469
3528
|
id: checkId,
|
|
@@ -3474,7 +3533,7 @@ async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext)
|
|
|
3474
3533
|
};
|
|
3475
3534
|
}
|
|
3476
3535
|
const validatorCwd = runtimeContext?.workspaceDir || resolveMonorepoRoot(projectRoot);
|
|
3477
|
-
const runtimeShellPath = runtimeContext ?
|
|
3536
|
+
const runtimeShellPath = runtimeContext ? resolve23(runtimeContext.binDir, "rig-shell") : "";
|
|
3478
3537
|
const monorepoMainRoot = runtimeContext?.monorepoMainRoot || process.env.MONOREPO_MAIN_ROOT?.trim() || resolveMonorepoRoot(projectRoot);
|
|
3479
3538
|
const validatorEnv = {
|
|
3480
3539
|
PROJECT_RIG_ROOT: runtimeContext?.hostProjectRoot || projectRoot,
|
|
@@ -3489,7 +3548,7 @@ async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext)
|
|
|
3489
3548
|
validatorEnv.RIG_LOGS_DIR = runtimeContext.logsDir;
|
|
3490
3549
|
validatorEnv.RIG_RUNTIME_BIN_DIR = runtimeContext.binDir;
|
|
3491
3550
|
}
|
|
3492
|
-
const { exitCode, stdout, stderr } = await runCaptureAsync(runtimeShellPath &&
|
|
3551
|
+
const { exitCode, stdout, stderr } = await runCaptureAsync(runtimeShellPath && existsSync21(runtimeShellPath) ? [runtimeShellPath, "run-binary", binaryPath] : [binaryPath], validatorCwd, validatorEnv);
|
|
3493
3552
|
try {
|
|
3494
3553
|
const result = JSON.parse(stdout.trim());
|
|
3495
3554
|
return { result, exitCode };
|
|
@@ -3529,8 +3588,8 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3529
3588
|
const configuredValidation = stringArray(taskConfig[taskId]?.validation);
|
|
3530
3589
|
const commands = resolvedContext?.validation?.length ? resolvedContext.validation : configuredValidation.length > 0 ? configuredValidation : sourceValidation.validation;
|
|
3531
3590
|
const { taskLogDir, artifactDir } = resolveValidationPaths(projectRoot, taskId, resolvedContext);
|
|
3532
|
-
|
|
3533
|
-
|
|
3591
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3592
|
+
mkdirSync9(artifactDir, { recursive: true });
|
|
3534
3593
|
if (commands.length === 0) {
|
|
3535
3594
|
const skipped = {
|
|
3536
3595
|
status: "skipped",
|
|
@@ -3539,7 +3598,7 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3539
3598
|
failed: 0,
|
|
3540
3599
|
categories: []
|
|
3541
3600
|
};
|
|
3542
|
-
|
|
3601
|
+
writeFileSync9(resolve23(artifactDir, "validation-summary.json"), `${JSON.stringify(skipped, null, 2)}
|
|
3543
3602
|
`, "utf-8");
|
|
3544
3603
|
return skipped;
|
|
3545
3604
|
}
|
|
@@ -3574,18 +3633,18 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3574
3633
|
exit_code: 2,
|
|
3575
3634
|
duration_seconds: 0
|
|
3576
3635
|
});
|
|
3577
|
-
const logFile2 =
|
|
3578
|
-
|
|
3579
|
-
|
|
3636
|
+
const logFile2 = resolve23(taskLogDir, `invalid-entry-validation.log`);
|
|
3637
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3638
|
+
writeFileSync9(logFile2, `=== ${nowIso()} :: ${cmd} ===
|
|
3580
3639
|
Invalid validation entry: not a check-ID. All entries must use format "category:check-name".
|
|
3581
3640
|
`, "utf-8");
|
|
3582
3641
|
continue;
|
|
3583
3642
|
}
|
|
3584
3643
|
const { result, exitCode } = await dispatchValidator(cmd, effectiveRegistry, validatorCtx, (id) => runValidatorBinary(projectRoot, taskId, id, resolvedContext));
|
|
3585
3644
|
const durationSeconds = Math.max(0, Math.round((Date.now() - startedAt) / 1000));
|
|
3586
|
-
const logFile =
|
|
3587
|
-
|
|
3588
|
-
|
|
3645
|
+
const logFile = resolve23(taskLogDir, `${cmd.replace(":", "-")}-validation.log`);
|
|
3646
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3647
|
+
writeFileSync9(logFile, `=== ${nowIso()} :: ${cmd} ===
|
|
3589
3648
|
${JSON.stringify(result, null, 2)}
|
|
3590
3649
|
`, "utf-8");
|
|
3591
3650
|
if (result.passed) {
|
|
@@ -3607,15 +3666,15 @@ ${JSON.stringify(result, null, 2)}
|
|
|
3607
3666
|
failed,
|
|
3608
3667
|
categories
|
|
3609
3668
|
};
|
|
3610
|
-
|
|
3611
|
-
|
|
3669
|
+
mkdirSync9(artifactDir, { recursive: true });
|
|
3670
|
+
writeFileSync9(resolve23(artifactDir, "validation-summary.json"), `${JSON.stringify(summary, null, 2)}
|
|
3612
3671
|
`, "utf-8");
|
|
3613
3672
|
return summary;
|
|
3614
3673
|
}
|
|
3615
3674
|
|
|
3616
3675
|
// packages/runtime/src/control-plane/native/verifier.ts
|
|
3617
|
-
import { existsSync as
|
|
3618
|
-
import { resolve as
|
|
3676
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
3677
|
+
import { resolve as resolve24 } from "path";
|
|
3619
3678
|
|
|
3620
3679
|
// packages/runtime/src/control-plane/native/pr-review-gate.ts
|
|
3621
3680
|
function parseJsonObject(value) {
|
|
@@ -4866,11 +4925,11 @@ async function verifyTask(options) {
|
|
|
4866
4925
|
const taskId = options.taskId;
|
|
4867
4926
|
const normalizedTaskId = lookupTask(options.projectRoot, taskId);
|
|
4868
4927
|
const artifactDir = artifactDirForId(options.projectRoot, taskId);
|
|
4869
|
-
|
|
4870
|
-
const validationSummaryPath =
|
|
4871
|
-
const reviewFeedbackPath =
|
|
4872
|
-
const reviewStatePath =
|
|
4873
|
-
const greptileRawPath =
|
|
4928
|
+
mkdirSync10(artifactDir, { recursive: true });
|
|
4929
|
+
const validationSummaryPath = resolve24(artifactDir, "validation-summary.json");
|
|
4930
|
+
const reviewFeedbackPath = resolve24(artifactDir, "review-feedback.md");
|
|
4931
|
+
const reviewStatePath = resolve24(artifactDir, "review-state.json");
|
|
4932
|
+
const greptileRawPath = resolve24(artifactDir, "review-greptile-raw.json");
|
|
4874
4933
|
const prStates = readPrMetadata(options.projectRoot, taskId);
|
|
4875
4934
|
const prState = prStates[0] || null;
|
|
4876
4935
|
const localReasons = [];
|
|
@@ -4882,7 +4941,7 @@ async function verifyTask(options) {
|
|
|
4882
4941
|
if (!normalizedTaskId && !await hasConfiguredSourceTask(options.projectRoot, taskId)) {
|
|
4883
4942
|
localReasons.push(`[Task Config] Unknown task id '${taskId}' in task-config or configured task source.`);
|
|
4884
4943
|
}
|
|
4885
|
-
if (!
|
|
4944
|
+
if (!existsSync22(validationSummaryPath)) {
|
|
4886
4945
|
localReasons.push(`[Artifact Quality] validation-summary.json not found at ${validationSummaryPath}.`);
|
|
4887
4946
|
} else {
|
|
4888
4947
|
const summary = await parseValidationSummary(validationSummaryPath);
|
|
@@ -4891,13 +4950,13 @@ async function verifyTask(options) {
|
|
|
4891
4950
|
}
|
|
4892
4951
|
}
|
|
4893
4952
|
for (const file of ["task-result.json", "decision-log.md", "next-actions.md", "changed-files.txt"]) {
|
|
4894
|
-
const requiredPath =
|
|
4895
|
-
if (!
|
|
4953
|
+
const requiredPath = resolve24(artifactDir, file);
|
|
4954
|
+
if (!existsSync22(requiredPath)) {
|
|
4896
4955
|
localReasons.push(`[Artifact Quality] Missing required artifact file: ${requiredPath}`);
|
|
4897
4956
|
}
|
|
4898
4957
|
}
|
|
4899
|
-
const taskResultPath =
|
|
4900
|
-
if (
|
|
4958
|
+
const taskResultPath = resolve24(artifactDir, "task-result.json");
|
|
4959
|
+
if (existsSync22(taskResultPath)) {
|
|
4901
4960
|
const taskResult = await readJsonFile2(taskResultPath);
|
|
4902
4961
|
const artifactStatus = typeof taskResult?.status === "string" ? taskResult.status.trim().toLowerCase() : "";
|
|
4903
4962
|
if (artifactStatus === "partial") {
|
|
@@ -4910,8 +4969,8 @@ async function verifyTask(options) {
|
|
|
4910
4969
|
localReasons.push("[Artifact Quality] task-result.json next actions indicate remaining implementation scope.");
|
|
4911
4970
|
}
|
|
4912
4971
|
}
|
|
4913
|
-
const nextActionsPath =
|
|
4914
|
-
if (
|
|
4972
|
+
const nextActionsPath = resolve24(artifactDir, "next-actions.md");
|
|
4973
|
+
if (existsSync22(nextActionsPath)) {
|
|
4915
4974
|
const nextActionsContent = await Bun.file(nextActionsPath).text();
|
|
4916
4975
|
if (nextActionsContent.includes("TODO: Replace this scaffold") || nextActionsContent.includes("bd-<downstream-task-id>")) {
|
|
4917
4976
|
localReasons.push("[Artifact Quality] next-actions.md still contains scaffold placeholder text. Replace with real recommendations.");
|
|
@@ -4942,7 +5001,7 @@ async function verifyTask(options) {
|
|
|
4942
5001
|
aiReasons.push(`[AI Review] Required mode needs a completed Greptile approval; current verdict is ${ai.verdict}.`);
|
|
4943
5002
|
}
|
|
4944
5003
|
if (persistArtifacts && ai.rawResponse) {
|
|
4945
|
-
|
|
5004
|
+
writeFileSync10(greptileRawPath, `${ai.rawResponse}
|
|
4946
5005
|
`, "utf-8");
|
|
4947
5006
|
}
|
|
4948
5007
|
} else if (!options.skipAiReview && reviewMode === "off") {
|
|
@@ -5185,7 +5244,7 @@ function evaluateSourceCloseoutChecks(prState, prLabel) {
|
|
|
5185
5244
|
if (!Array.isArray(checks) || checks.length === 0) {
|
|
5186
5245
|
return [`[Source Issue] PR ${prLabel} must have green check evidence before completion.`];
|
|
5187
5246
|
}
|
|
5188
|
-
const ciGate = evaluatePullRequestCiChecks(checks, "PR", 0);
|
|
5247
|
+
const ciGate = evaluatePullRequestCiChecks(checks, "PR", 0, { mergeStateStatus: prState.mergeStateStatus });
|
|
5189
5248
|
if (ciGate.verdict === "APPROVE") {
|
|
5190
5249
|
return [];
|
|
5191
5250
|
}
|
|
@@ -5281,7 +5340,7 @@ function isAcceptedValidationSummary(summary) {
|
|
|
5281
5340
|
return summary.status === "skipped" && summary.total === 0 && summary.failed === 0;
|
|
5282
5341
|
}
|
|
5283
5342
|
async function loadReviewMode(reviewProfilePath, fallback) {
|
|
5284
|
-
const parsed =
|
|
5343
|
+
const parsed = existsSync22(reviewProfilePath) ? await readJsonFile2(reviewProfilePath) : null;
|
|
5285
5344
|
const mode = parsed?.mode;
|
|
5286
5345
|
if (mode === "off" || mode === "advisory" || mode === "required") {
|
|
5287
5346
|
return mode;
|
|
@@ -5292,7 +5351,7 @@ async function loadReviewMode(reviewProfilePath, fallback) {
|
|
|
5292
5351
|
return "advisory";
|
|
5293
5352
|
}
|
|
5294
5353
|
async function loadReviewProvider(reviewProfilePath, fallback) {
|
|
5295
|
-
const parsed =
|
|
5354
|
+
const parsed = existsSync22(reviewProfilePath) ? await readJsonFile2(reviewProfilePath) : null;
|
|
5296
5355
|
const provider = parsed?.provider;
|
|
5297
5356
|
if (typeof provider === "string" && provider.trim().length > 0) {
|
|
5298
5357
|
return provider;
|
|
@@ -5451,7 +5510,7 @@ function writeFeedbackFile(options) {
|
|
|
5451
5510
|
if (options.aiRawFeedback) {
|
|
5452
5511
|
lines.push("## Raw Reviewer Feedback", "", "```text", options.aiRawFeedback, "```", "");
|
|
5453
5512
|
}
|
|
5454
|
-
|
|
5513
|
+
writeFileSync10(options.output, `${lines.join(`
|
|
5455
5514
|
`)}
|
|
5456
5515
|
`, "utf-8");
|
|
5457
5516
|
}
|
|
@@ -5468,7 +5527,7 @@ function writeReviewStateFile(options) {
|
|
|
5468
5527
|
ai_warnings: options.aiWarnings,
|
|
5469
5528
|
updated_at: nowIso()
|
|
5470
5529
|
};
|
|
5471
|
-
|
|
5530
|
+
writeFileSync10(options.output, `${JSON.stringify(payload, null, 2)}
|
|
5472
5531
|
`, "utf-8");
|
|
5473
5532
|
}
|
|
5474
5533
|
async function runGreptileReviewForPr(options) {
|
|
@@ -5549,7 +5608,7 @@ async function runGreptileReviewForPr(options) {
|
|
|
5549
5608
|
}
|
|
5550
5609
|
await Bun.sleep(options.pollIntervalMs);
|
|
5551
5610
|
}
|
|
5552
|
-
const ciGate = evaluatePullRequestCiChecks(githubCheckRollup, repoName, prNumber);
|
|
5611
|
+
const ciGate = evaluatePullRequestCiChecks(githubCheckRollup, repoName, prNumber, { mergeStateStatus: options.prState.mergeStateStatus });
|
|
5553
5612
|
if (ciGate.verdict !== "APPROVE") {
|
|
5554
5613
|
return {
|
|
5555
5614
|
verdict: ciGate.verdict,
|
|
@@ -5807,7 +5866,7 @@ async function runGithubGreptileFallbackReviewForPr(options) {
|
|
|
5807
5866
|
}
|
|
5808
5867
|
await Bun.sleep(options.pollIntervalMs);
|
|
5809
5868
|
}
|
|
5810
|
-
const ciGate = evaluatePullRequestCiChecks(checkRollup, repoName, prNumber);
|
|
5869
|
+
const ciGate = evaluatePullRequestCiChecks(checkRollup, repoName, prNumber, { mergeStateStatus: options.prState.mergeStateStatus });
|
|
5811
5870
|
if (ciGate.verdict !== "APPROVE") {
|
|
5812
5871
|
return {
|
|
5813
5872
|
verdict: ciGate.verdict,
|
|
@@ -6162,7 +6221,7 @@ function loadGithubPullRequestCheckRollup(projectRoot, repoName, prNumber) {
|
|
|
6162
6221
|
]);
|
|
6163
6222
|
return response.statusCheckRollup || [];
|
|
6164
6223
|
}
|
|
6165
|
-
function evaluatePullRequestCiChecks(checks, repoName, prNumber) {
|
|
6224
|
+
function evaluatePullRequestCiChecks(checks, repoName, prNumber, options = {}) {
|
|
6166
6225
|
const nonGreptileChecks = checks.filter((check) => {
|
|
6167
6226
|
const label = (check.name || check.context || "").toLowerCase();
|
|
6168
6227
|
return label.length > 0 && !label.includes("greptile");
|
|
@@ -6174,7 +6233,8 @@ function evaluatePullRequestCiChecks(checks, repoName, prNumber) {
|
|
|
6174
6233
|
const state = (check.state || check.status || "").toUpperCase();
|
|
6175
6234
|
return state === "PENDING" || state === "EXPECTED" || state === "QUEUED" || state === "IN_PROGRESS";
|
|
6176
6235
|
});
|
|
6177
|
-
|
|
6236
|
+
const mergeClean = (options.mergeStateStatus || "").toUpperCase() === "CLEAN";
|
|
6237
|
+
if (pending.length > 0 && !mergeClean) {
|
|
6178
6238
|
return {
|
|
6179
6239
|
verdict: "SKIP",
|
|
6180
6240
|
reasons: pending.map((check) => `[CI] ${repoName}#${prNumber} check is still pending: ${check.name || check.context || "unknown"}.`)
|
|
@@ -6255,7 +6315,7 @@ function filterActionableGithubGreptileThreads(threads) {
|
|
|
6255
6315
|
}
|
|
6256
6316
|
function resolvePrRepoRoot(projectRoot, prState) {
|
|
6257
6317
|
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
6258
|
-
if (prState.target === "monorepo" && runtimeWorkspace &&
|
|
6318
|
+
if (prState.target === "monorepo" && runtimeWorkspace && existsSync22(resolve24(runtimeWorkspace, ".git"))) {
|
|
6259
6319
|
return runtimeWorkspace;
|
|
6260
6320
|
}
|
|
6261
6321
|
const paths = resolveHarnessPaths(projectRoot);
|
|
@@ -6627,16 +6687,16 @@ async function taskDeps(projectRoot, taskId) {
|
|
|
6627
6687
|
for (const dep of deps) {
|
|
6628
6688
|
const artifactDir = artifactDirForId(projectRoot, dep);
|
|
6629
6689
|
console.log(`=== ${dep} ===`);
|
|
6630
|
-
if (!
|
|
6690
|
+
if (!existsSync23(artifactDir)) {
|
|
6631
6691
|
console.log(` (no artifacts yet)
|
|
6632
6692
|
`);
|
|
6633
6693
|
continue;
|
|
6634
6694
|
}
|
|
6635
|
-
printArtifactSection(
|
|
6636
|
-
printArtifactSection(
|
|
6637
|
-
const changedFiles =
|
|
6638
|
-
if (
|
|
6639
|
-
const lines =
|
|
6695
|
+
printArtifactSection(resolve25(artifactDir, "decision-log.md"), "--- Decisions ---");
|
|
6696
|
+
printArtifactSection(resolve25(artifactDir, "next-actions.md"), "--- Next Actions (for you) ---");
|
|
6697
|
+
const changedFiles = resolve25(artifactDir, "changed-files.txt");
|
|
6698
|
+
if (existsSync23(changedFiles)) {
|
|
6699
|
+
const lines = readFileSync12(changedFiles, "utf-8").split(/\r?\n/).filter(Boolean);
|
|
6640
6700
|
console.log(`--- Changed Files (${lines.length}) ---`);
|
|
6641
6701
|
for (const line of lines) {
|
|
6642
6702
|
console.log(line);
|
|
@@ -6681,12 +6741,12 @@ function taskRecord(projectRoot, type, text, taskId) {
|
|
|
6681
6741
|
throw new Error("No active task.");
|
|
6682
6742
|
}
|
|
6683
6743
|
const paths = resolveHarnessPaths(projectRoot);
|
|
6684
|
-
|
|
6744
|
+
mkdirSync11(paths.stateDir, { recursive: true });
|
|
6685
6745
|
if (type === "decision") {
|
|
6686
|
-
const artifactDir =
|
|
6687
|
-
|
|
6746
|
+
const artifactDir = resolve25(paths.artifactsDir, activeTask);
|
|
6747
|
+
mkdirSync11(artifactDir, { recursive: true });
|
|
6688
6748
|
const timestamp = nowIso();
|
|
6689
|
-
appendFileSync(
|
|
6749
|
+
appendFileSync(resolve25(artifactDir, "decision-log.md"), `
|
|
6690
6750
|
### ${timestamp}
|
|
6691
6751
|
|
|
6692
6752
|
${text}
|
|
@@ -6696,14 +6756,14 @@ ${text}
|
|
|
6696
6756
|
return;
|
|
6697
6757
|
}
|
|
6698
6758
|
const failedPath = paths.failedApproachesPath;
|
|
6699
|
-
if (!
|
|
6700
|
-
|
|
6759
|
+
if (!existsSync23(failedPath)) {
|
|
6760
|
+
writeFileSync11(failedPath, `# Failed Approaches Log
|
|
6701
6761
|
|
|
6702
6762
|
This file records approaches that did not work.
|
|
6703
6763
|
|
|
6704
6764
|
`, "utf-8");
|
|
6705
6765
|
}
|
|
6706
|
-
const content =
|
|
6766
|
+
const content = readFileSync12(failedPath, "utf-8");
|
|
6707
6767
|
const attempts = (content.match(new RegExp(`^## ${escapeRegExp(activeTask)}\\b`, "gm")) || []).length + 1;
|
|
6708
6768
|
appendFileSync(failedPath, `
|
|
6709
6769
|
## ${activeTask} - Attempt ${attempts} (${nowIso()})
|
|
@@ -6720,40 +6780,40 @@ function taskArtifacts(projectRoot, taskId) {
|
|
|
6720
6780
|
throw new Error("No active task.");
|
|
6721
6781
|
}
|
|
6722
6782
|
const paths = resolveHarnessPaths(projectRoot);
|
|
6723
|
-
const artifactDir =
|
|
6724
|
-
|
|
6783
|
+
const artifactDir = resolve25(paths.artifactsDir, activeTask);
|
|
6784
|
+
mkdirSync11(artifactDir, { recursive: true });
|
|
6725
6785
|
const changed = changedFilesForTask(projectRoot, activeTask, true);
|
|
6726
|
-
|
|
6786
|
+
writeFileSync11(resolve25(artifactDir, "changed-files.txt"), `${changed.join(`
|
|
6727
6787
|
`)}
|
|
6728
6788
|
`, "utf-8");
|
|
6729
6789
|
console.log(`changed-files.txt: ${changed.length} files`);
|
|
6730
|
-
const taskResultPath =
|
|
6731
|
-
if (!
|
|
6790
|
+
const taskResultPath = resolve25(artifactDir, "task-result.json");
|
|
6791
|
+
if (!existsSync23(taskResultPath)) {
|
|
6732
6792
|
const template = {
|
|
6733
6793
|
task_id: activeTask,
|
|
6734
6794
|
status: "completed",
|
|
6735
6795
|
summary: "TODO: Write a one-line summary of what you did",
|
|
6736
6796
|
completed_at: nowIso()
|
|
6737
6797
|
};
|
|
6738
|
-
|
|
6798
|
+
writeFileSync11(taskResultPath, `${JSON.stringify(template, null, 2)}
|
|
6739
6799
|
`, "utf-8");
|
|
6740
6800
|
console.log("task-result.json: created (update the summary!)");
|
|
6741
6801
|
} else {
|
|
6742
6802
|
console.log("task-result.json: already exists");
|
|
6743
6803
|
}
|
|
6744
|
-
const decisionLogPath =
|
|
6745
|
-
if (!
|
|
6804
|
+
const decisionLogPath = resolve25(artifactDir, "decision-log.md");
|
|
6805
|
+
if (!existsSync23(decisionLogPath)) {
|
|
6746
6806
|
const content = `# Decision Log: ${activeTask}
|
|
6747
6807
|
|
|
6748
6808
|
Record key decisions here using: rig-agent record decision "..."
|
|
6749
6809
|
`;
|
|
6750
|
-
|
|
6810
|
+
writeFileSync11(decisionLogPath, content, "utf-8");
|
|
6751
6811
|
console.log("decision-log.md: created (record your decisions!)");
|
|
6752
6812
|
} else {
|
|
6753
6813
|
console.log("decision-log.md: already exists");
|
|
6754
6814
|
}
|
|
6755
|
-
const nextActionsPath =
|
|
6756
|
-
if (!
|
|
6815
|
+
const nextActionsPath = resolve25(artifactDir, "next-actions.md");
|
|
6816
|
+
if (!existsSync23(nextActionsPath)) {
|
|
6757
6817
|
const content = [
|
|
6758
6818
|
`# Next Actions: ${activeTask}`,
|
|
6759
6819
|
"",
|
|
@@ -6770,13 +6830,13 @@ Record key decisions here using: rig-agent record decision "..."
|
|
|
6770
6830
|
""
|
|
6771
6831
|
].join(`
|
|
6772
6832
|
`);
|
|
6773
|
-
|
|
6833
|
+
writeFileSync11(nextActionsPath, content, "utf-8");
|
|
6774
6834
|
console.log("next-actions.md: created (add recommendations for downstream tasks!)");
|
|
6775
6835
|
} else {
|
|
6776
6836
|
console.log("next-actions.md: already exists");
|
|
6777
6837
|
}
|
|
6778
|
-
const validationSummaryPath =
|
|
6779
|
-
if (
|
|
6838
|
+
const validationSummaryPath = resolve25(artifactDir, "validation-summary.json");
|
|
6839
|
+
if (existsSync23(validationSummaryPath)) {
|
|
6780
6840
|
console.log("validation-summary.json: already exists");
|
|
6781
6841
|
} else {
|
|
6782
6842
|
console.log("validation-summary.json: not yet created (run: rig-agent validate)");
|
|
@@ -6867,7 +6927,7 @@ function collectTaskChangedFiles(projectRoot, taskId, includeCommitted) {
|
|
|
6867
6927
|
[projectRoot, ""],
|
|
6868
6928
|
[monorepoRepoRoot, ""]
|
|
6869
6929
|
]) {
|
|
6870
|
-
if (!
|
|
6930
|
+
if (!existsSync23(resolve25(repo, ".git"))) {
|
|
6871
6931
|
continue;
|
|
6872
6932
|
}
|
|
6873
6933
|
if (includeCommitted && repo === monorepoRepoRoot) {
|
|
@@ -6892,12 +6952,22 @@ function filterTaskChangedFiles(projectRoot, taskId, files, scoped) {
|
|
|
6892
6952
|
}
|
|
6893
6953
|
function resolveTaskMonorepoRoot(projectRoot) {
|
|
6894
6954
|
const runtimeWorkspace = loadRuntimeContextFromEnv()?.workspaceDir || process.env.RIG_TASK_WORKSPACE?.trim();
|
|
6895
|
-
if (runtimeWorkspace &&
|
|
6896
|
-
return
|
|
6955
|
+
if (runtimeWorkspace && existsSync23(resolve25(runtimeWorkspace, ".git"))) {
|
|
6956
|
+
return resolve25(runtimeWorkspace);
|
|
6897
6957
|
}
|
|
6898
6958
|
return resolveHarnessPaths(projectRoot).monorepoRoot;
|
|
6899
6959
|
}
|
|
6900
6960
|
function collectCommittedMonorepoFiles(projectRoot, repo) {
|
|
6961
|
+
const defaultRef = runCapture(["git", "-C", repo, "rev-parse", "--abbrev-ref", "origin/HEAD"], projectRoot);
|
|
6962
|
+
if (defaultRef.exitCode === 0 && defaultRef.stdout.trim()) {
|
|
6963
|
+
const mergeBase = runCapture(["git", "-C", repo, "merge-base", "HEAD", defaultRef.stdout.trim()], projectRoot);
|
|
6964
|
+
if (mergeBase.exitCode === 0 && mergeBase.stdout.trim()) {
|
|
6965
|
+
const result = runCapture(["git", "-C", repo, "diff", "--name-only", `${mergeBase.stdout.trim()}..HEAD`], projectRoot);
|
|
6966
|
+
if (result.exitCode === 0) {
|
|
6967
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
6968
|
+
}
|
|
6969
|
+
}
|
|
6970
|
+
}
|
|
6901
6971
|
const initialHeadCommit = resolveRuntimeInitialHeadCommit(projectRoot, repo);
|
|
6902
6972
|
if (initialHeadCommit) {
|
|
6903
6973
|
const result = runCapture(["git", "-C", repo, "diff", "--name-only", `${initialHeadCommit}..HEAD`], projectRoot);
|
|
@@ -6921,7 +6991,7 @@ function resolveRuntimeInitialHeadCommit(projectRoot, repo) {
|
|
|
6921
6991
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
6922
6992
|
if (runtimeContext?.initialHeadCommits?.monorepo?.trim()) {
|
|
6923
6993
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6924
|
-
if (
|
|
6994
|
+
if (resolve25(monorepoRoot) === resolve25(repo)) {
|
|
6925
6995
|
return runtimeContext.initialHeadCommits.monorepo.trim();
|
|
6926
6996
|
}
|
|
6927
6997
|
}
|
|
@@ -6931,7 +7001,7 @@ function resolveMonorepoBaseCommit(projectRoot, repo) {
|
|
|
6931
7001
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
6932
7002
|
if (runtimeContext?.monorepoBaseCommit?.trim()) {
|
|
6933
7003
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6934
|
-
if (
|
|
7004
|
+
if (resolve25(monorepoRoot) === resolve25(repo)) {
|
|
6935
7005
|
return runtimeContext.monorepoBaseCommit.trim();
|
|
6936
7006
|
}
|
|
6937
7007
|
}
|
|
@@ -6976,7 +7046,7 @@ function resolveRuntimeDirtyBaseline(projectRoot, repo) {
|
|
|
6976
7046
|
return new Set;
|
|
6977
7047
|
}
|
|
6978
7048
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6979
|
-
const selected =
|
|
7049
|
+
const selected = resolve25(repo) === resolve25(monorepoRoot) ? dirtyFiles.monorepo : resolve25(repo) === resolve25(projectRoot) ? dirtyFiles.project : undefined;
|
|
6980
7050
|
return new Set((selected || []).map((file) => normalizeChangedFilePath(file)).filter(Boolean));
|
|
6981
7051
|
}
|
|
6982
7052
|
function normalizeChangedFilePath(file) {
|
|
@@ -7076,12 +7146,12 @@ function printIndented(text) {
|
|
|
7076
7146
|
}
|
|
7077
7147
|
}
|
|
7078
7148
|
function readLocalBeadsTasks(projectRoot) {
|
|
7079
|
-
const issuesPath =
|
|
7080
|
-
if (!
|
|
7149
|
+
const issuesPath = resolve25(resolveMonorepoRoot2(projectRoot), ".beads/issues.jsonl");
|
|
7150
|
+
if (!existsSync23(issuesPath)) {
|
|
7081
7151
|
return [];
|
|
7082
7152
|
}
|
|
7083
7153
|
const tasks = [];
|
|
7084
|
-
for (const line of
|
|
7154
|
+
for (const line of readFileSync12(issuesPath, "utf-8").split(/\r?\n/)) {
|
|
7085
7155
|
const trimmed = line.trim();
|
|
7086
7156
|
if (!trimmed) {
|
|
7087
7157
|
continue;
|
|
@@ -7194,11 +7264,11 @@ function taskDependencies(projectRoot, taskId, tracker) {
|
|
|
7194
7264
|
return [...ids].sort();
|
|
7195
7265
|
}
|
|
7196
7266
|
function printArtifactSection(path, header) {
|
|
7197
|
-
if (!
|
|
7267
|
+
if (!existsSync23(path)) {
|
|
7198
7268
|
return;
|
|
7199
7269
|
}
|
|
7200
7270
|
console.log(header);
|
|
7201
|
-
process.stdout.write(
|
|
7271
|
+
process.stdout.write(readFileSync12(path, "utf-8"));
|
|
7202
7272
|
console.log("");
|
|
7203
7273
|
}
|
|
7204
7274
|
function escapeRegExp(value) {
|
|
@@ -7238,8 +7308,8 @@ function isRuntimeGatewayGhPath(candidate) {
|
|
|
7238
7308
|
}
|
|
7239
7309
|
function resolveOptionalMonorepoRoot(projectRoot) {
|
|
7240
7310
|
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
7241
|
-
if (runtimeWorkspace &&
|
|
7242
|
-
return
|
|
7311
|
+
if (runtimeWorkspace && existsSync24(resolve26(runtimeWorkspace, ".git"))) {
|
|
7312
|
+
return resolve26(runtimeWorkspace);
|
|
7243
7313
|
}
|
|
7244
7314
|
try {
|
|
7245
7315
|
return resolveMonorepoRoot2(projectRoot);
|
|
@@ -7264,7 +7334,7 @@ function resolveGitBinary(projectRoot) {
|
|
|
7264
7334
|
if (!candidate || isRuntimeGatewayGitPath(candidate)) {
|
|
7265
7335
|
continue;
|
|
7266
7336
|
}
|
|
7267
|
-
if (
|
|
7337
|
+
if (existsSync24(candidate)) {
|
|
7268
7338
|
return candidate;
|
|
7269
7339
|
}
|
|
7270
7340
|
}
|
|
@@ -7288,7 +7358,7 @@ function shouldScopeGitCommit(args, hasTaskContext) {
|
|
|
7288
7358
|
if (!hasTaskContext) {
|
|
7289
7359
|
return false;
|
|
7290
7360
|
}
|
|
7291
|
-
return
|
|
7361
|
+
return args.includes("--scoped");
|
|
7292
7362
|
}
|
|
7293
7363
|
function gitStatus(projectRoot, taskId) {
|
|
7294
7364
|
const monorepoRoot = resolveOptionalMonorepoRoot(projectRoot);
|
|
@@ -7324,11 +7394,11 @@ function gitPreflight(projectRoot, taskId, strict) {
|
|
|
7324
7394
|
const expected = resolvedTask ? `rig/${resolveTaskBranchId(projectRoot, resolvedTask)}` : "";
|
|
7325
7395
|
console.log("=== Git Flow Preflight ===");
|
|
7326
7396
|
let issues = 0;
|
|
7327
|
-
if (!
|
|
7397
|
+
if (!existsSync24(resolve26(projectRoot, ".git"))) {
|
|
7328
7398
|
console.log(`ERROR: project root is not a git repo (${projectRoot})`);
|
|
7329
7399
|
issues += 1;
|
|
7330
7400
|
}
|
|
7331
|
-
if (monorepoRoot &&
|
|
7401
|
+
if (monorepoRoot && existsSync24(resolve26(monorepoRoot, ".git"))) {
|
|
7332
7402
|
const monoBranch = branchName(projectRoot, monorepoRoot);
|
|
7333
7403
|
if (expected && monoBranch !== expected) {
|
|
7334
7404
|
console.log(`WARN: monorepo branch is ${monoBranch}, expected ${expected} for task ${resolvedTask}`);
|
|
@@ -7362,7 +7432,7 @@ function gitSyncBranch(projectRoot, taskId, targetRepo = "monorepo") {
|
|
|
7362
7432
|
}
|
|
7363
7433
|
const repoRoot = targetRepo === "monorepo" ? resolveOptionalMonorepoRoot(projectRoot) || resolveMonorepoRoot2(projectRoot) : projectRoot;
|
|
7364
7434
|
const repoLabel = targetRepo === "monorepo" ? "Monorepo" : "Project";
|
|
7365
|
-
if (!
|
|
7435
|
+
if (!existsSync24(resolve26(repoRoot, ".git"))) {
|
|
7366
7436
|
throw new Error(`${repoLabel} repo not found at ${repoRoot}`);
|
|
7367
7437
|
}
|
|
7368
7438
|
const branchId = resolveTaskBranchId(projectRoot, resolvedTask);
|
|
@@ -7406,8 +7476,8 @@ function gitCommit(options) {
|
|
|
7406
7476
|
function gitSnapshot(projectRoot, taskId, outputPath) {
|
|
7407
7477
|
const monorepoRoot = resolveOptionalMonorepoRoot(projectRoot);
|
|
7408
7478
|
const resolvedTask = taskId || safeCurrentTaskId(projectRoot);
|
|
7409
|
-
const output = outputPath || (resolvedTask ? resolveArtifactSnapshot(projectRoot, resolvedTask) :
|
|
7410
|
-
|
|
7479
|
+
const output = outputPath || (resolvedTask ? resolveArtifactSnapshot(projectRoot, resolvedTask) : resolve26(resolve26(projectRoot, ".rig", "state"), "git-state.txt"));
|
|
7480
|
+
mkdirSync12(dirname12(output), { recursive: true });
|
|
7411
7481
|
const lines = ["# Git Snapshot", `timestamp: ${nowIso()}`];
|
|
7412
7482
|
if (resolvedTask) {
|
|
7413
7483
|
lines.push(`task: ${resolvedTask}`);
|
|
@@ -7417,7 +7487,7 @@ function gitSnapshot(projectRoot, taskId, outputPath) {
|
|
|
7417
7487
|
if (monorepoRoot && monorepoRoot !== projectRoot) {
|
|
7418
7488
|
lines.push(...snapshotRepo(projectRoot, "monorepo", monorepoRoot));
|
|
7419
7489
|
}
|
|
7420
|
-
|
|
7490
|
+
writeFileSync12(output, `${lines.join(`
|
|
7421
7491
|
`)}
|
|
7422
7492
|
`, "utf-8");
|
|
7423
7493
|
return output;
|
|
@@ -7441,7 +7511,7 @@ function gitOpenPr(options) {
|
|
|
7441
7511
|
} else if (taskId) {
|
|
7442
7512
|
gitSyncBranch(options.projectRoot, taskId, "project");
|
|
7443
7513
|
}
|
|
7444
|
-
if (!
|
|
7514
|
+
if (!existsSync24(resolve26(repoRoot, ".git"))) {
|
|
7445
7515
|
throw new Error(`Repository not available for open-pr target ${target}: ${repoRoot}`);
|
|
7446
7516
|
}
|
|
7447
7517
|
const branch = branchName(options.projectRoot, repoRoot);
|
|
@@ -7654,12 +7724,12 @@ function assertPrHasNoGitConflicts(prState, repoLabel, baseRef) {
|
|
|
7654
7724
|
}
|
|
7655
7725
|
function writePrMetadata(projectRoot, taskId, result) {
|
|
7656
7726
|
const dir = artifactDirForId(projectRoot, taskId);
|
|
7657
|
-
|
|
7658
|
-
const path =
|
|
7727
|
+
mkdirSync12(dir, { recursive: true });
|
|
7728
|
+
const path = resolve26(dir, "pr-state.json");
|
|
7659
7729
|
let prs = {};
|
|
7660
|
-
if (
|
|
7730
|
+
if (existsSync24(path)) {
|
|
7661
7731
|
try {
|
|
7662
|
-
const parsed = JSON.parse(
|
|
7732
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
7663
7733
|
if (parsed && typeof parsed === "object" && parsed.prs && typeof parsed.prs === "object") {
|
|
7664
7734
|
prs = parsed.prs;
|
|
7665
7735
|
}
|
|
@@ -7675,16 +7745,16 @@ function writePrMetadata(projectRoot, taskId, result) {
|
|
|
7675
7745
|
...primary || {},
|
|
7676
7746
|
updated_at: nowIso()
|
|
7677
7747
|
};
|
|
7678
|
-
|
|
7748
|
+
writeFileSync12(path, `${JSON.stringify(artifact, null, 2)}
|
|
7679
7749
|
`, "utf-8");
|
|
7680
7750
|
}
|
|
7681
7751
|
function readPrMetadata(projectRoot, taskId) {
|
|
7682
|
-
const path =
|
|
7683
|
-
if (!
|
|
7752
|
+
const path = resolve26(artifactDirForId(projectRoot, taskId), "pr-state.json");
|
|
7753
|
+
if (!existsSync24(path)) {
|
|
7684
7754
|
return [];
|
|
7685
7755
|
}
|
|
7686
7756
|
try {
|
|
7687
|
-
const parsed = JSON.parse(
|
|
7757
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
7688
7758
|
if (!parsed || typeof parsed !== "object") {
|
|
7689
7759
|
return [];
|
|
7690
7760
|
}
|
|
@@ -7697,8 +7767,8 @@ function readPrMetadata(projectRoot, taskId) {
|
|
|
7697
7767
|
}
|
|
7698
7768
|
}
|
|
7699
7769
|
function resolveArtifactSnapshot(projectRoot, taskId) {
|
|
7700
|
-
const artifactDir =
|
|
7701
|
-
return
|
|
7770
|
+
const artifactDir = resolve26(resolveHarnessPaths(projectRoot).artifactsDir, taskId);
|
|
7771
|
+
return resolve26(artifactDir, "git-state.txt");
|
|
7702
7772
|
}
|
|
7703
7773
|
function isGitOpenPrResult(value) {
|
|
7704
7774
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -7757,14 +7827,14 @@ function resolveGithubCliBinary(projectRoot) {
|
|
|
7757
7827
|
}
|
|
7758
7828
|
const explicitPathEntries = (process.env.PATH || "").split(":").map((entry) => entry.trim()).filter(Boolean);
|
|
7759
7829
|
for (const entry of explicitPathEntries) {
|
|
7760
|
-
candidates.add(
|
|
7830
|
+
candidates.add(resolve26(entry, "gh"));
|
|
7761
7831
|
}
|
|
7762
7832
|
const bunResolved = Bun.which("gh");
|
|
7763
7833
|
if (bunResolved) {
|
|
7764
7834
|
candidates.add(bunResolved);
|
|
7765
7835
|
}
|
|
7766
7836
|
for (const candidate of candidates) {
|
|
7767
|
-
if (candidate &&
|
|
7837
|
+
if (candidate && existsSync24(candidate) && !isRuntimeGatewayGhPath(candidate)) {
|
|
7768
7838
|
return candidate;
|
|
7769
7839
|
}
|
|
7770
7840
|
}
|
|
@@ -7794,7 +7864,7 @@ function resolveRepoNameWithOwner(projectRoot, repoRoot) {
|
|
|
7794
7864
|
return resolveGithubRepoNameWithOwnerFromGitRoot(projectRoot, repoRoot, repoRoot, visited);
|
|
7795
7865
|
}
|
|
7796
7866
|
function resolveGithubRepoNameWithOwnerFromGitRoot(projectRoot, gitRoot, cwd, visited) {
|
|
7797
|
-
const normalizedGitRoot =
|
|
7867
|
+
const normalizedGitRoot = resolve26(gitRoot);
|
|
7798
7868
|
if (visited.has(normalizedGitRoot)) {
|
|
7799
7869
|
return "";
|
|
7800
7870
|
}
|
|
@@ -7866,7 +7936,7 @@ function resolveNetworkRemoteName(projectRoot, repoRoot, repoNameWithOwner) {
|
|
|
7866
7936
|
return remotes.includes("origin") ? "origin" : remotes[0];
|
|
7867
7937
|
}
|
|
7868
7938
|
function gitQuery(projectRoot, gitRoot, cwd, ...args) {
|
|
7869
|
-
const gitArgs =
|
|
7939
|
+
const gitArgs = existsSync24(resolve26(gitRoot, ".git")) ? gitCmd(projectRoot, gitRoot, ...args) : [resolveGitBinary(projectRoot), "--git-dir", gitRoot, ...args];
|
|
7870
7940
|
return runCapture2(gitArgs, cwd, projectRoot);
|
|
7871
7941
|
}
|
|
7872
7942
|
function resolveLocalGitRemoteRoot(remoteUrl, gitRoot) {
|
|
@@ -7884,9 +7954,9 @@ function resolveLocalGitRemoteRoot(remoteUrl, gitRoot) {
|
|
|
7884
7954
|
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(normalized) || /^[^@]+@[^:]+:.+$/.test(normalized)) {
|
|
7885
7955
|
return "";
|
|
7886
7956
|
} else if (!isAbsolute2(normalized)) {
|
|
7887
|
-
candidate =
|
|
7957
|
+
candidate = resolve26(gitRoot, normalized);
|
|
7888
7958
|
}
|
|
7889
|
-
return
|
|
7959
|
+
return existsSync24(candidate) ? candidate : "";
|
|
7890
7960
|
}
|
|
7891
7961
|
function normalizeGithubRepoNameWithOwner(value) {
|
|
7892
7962
|
const normalized = value.trim();
|
|
@@ -8013,7 +8083,7 @@ function inferReviewerFromChangedFiles(projectRoot, repoRoot, baseRef, branchRef
|
|
|
8013
8083
|
return best;
|
|
8014
8084
|
}
|
|
8015
8085
|
function snapshotRepo(projectRoot, label, repo) {
|
|
8016
|
-
if (!
|
|
8086
|
+
if (!existsSync24(resolve26(repo, ".git"))) {
|
|
8017
8087
|
return [`## ${label}`, `repo: ${repo}`, "status: unavailable", ""];
|
|
8018
8088
|
}
|
|
8019
8089
|
const status = runCapture2(gitCmd(projectRoot, repo, "status", "--short"), projectRoot).stdout.trim();
|
|
@@ -8036,7 +8106,7 @@ function snapshotRepo(projectRoot, label, repo) {
|
|
|
8036
8106
|
];
|
|
8037
8107
|
}
|
|
8038
8108
|
function commitRepo(projectRoot, repo, label, message, allowEmpty, scoped, files, changedFilesManifest) {
|
|
8039
|
-
if (!
|
|
8109
|
+
if (!existsSync24(resolve26(repo, ".git"))) {
|
|
8040
8110
|
console.log(`Skipping ${label}: repo not available (${repo})`);
|
|
8041
8111
|
return;
|
|
8042
8112
|
}
|
|
@@ -8068,18 +8138,18 @@ function commitRepo(projectRoot, repo, label, message, allowEmpty, scoped, files
|
|
|
8068
8138
|
console.log(`Committed ${label}: ${message}`);
|
|
8069
8139
|
}
|
|
8070
8140
|
function readChangedFilesManifest(projectRoot, taskId) {
|
|
8071
|
-
const manifestPath =
|
|
8072
|
-
if (!
|
|
8141
|
+
const manifestPath = resolve26(artifactDirForId(projectRoot, taskId), "changed-files.txt");
|
|
8142
|
+
if (!existsSync24(manifestPath)) {
|
|
8073
8143
|
return [];
|
|
8074
8144
|
}
|
|
8075
|
-
const files =
|
|
8145
|
+
const files = readFileSync13(manifestPath, "utf-8").split(/\r?\n/).map((line) => normalizeChangedFilePath2(line)).filter(Boolean);
|
|
8076
8146
|
return [...new Set(files)];
|
|
8077
8147
|
}
|
|
8078
8148
|
function refreshChangedFilesManifest(projectRoot, taskId) {
|
|
8079
|
-
const manifestPath =
|
|
8080
|
-
|
|
8149
|
+
const manifestPath = resolve26(artifactDirForId(projectRoot, taskId), "changed-files.txt");
|
|
8150
|
+
mkdirSync12(dirname12(manifestPath), { recursive: true });
|
|
8081
8151
|
const changedFiles = changedFilesForTask(projectRoot, taskId, true);
|
|
8082
|
-
|
|
8152
|
+
writeFileSync12(manifestPath, `${changedFiles.join(`
|
|
8083
8153
|
`)}
|
|
8084
8154
|
`, "utf-8");
|
|
8085
8155
|
return manifestPath;
|
|
@@ -8192,7 +8262,7 @@ function repoHasPathChange(projectRoot, repoRoot, relativePath) {
|
|
|
8192
8262
|
return result.exitCode === 0 && result.stdout.trim().length > 0;
|
|
8193
8263
|
}
|
|
8194
8264
|
function stageExcludePathspecs(repoRoot) {
|
|
8195
|
-
const patterns =
|
|
8265
|
+
const patterns = existsSync24(resolve26(repoRoot, ".rig", "task-config.json")) ? [...TASK_RUNTIME_STAGE_EXCLUDES, ...GENERATED_STAGE_EXCLUDES] : [".rig/**", ...GENERATED_STAGE_EXCLUDES];
|
|
8196
8266
|
return patterns.map((pattern) => `:(glob,exclude)${pattern}`);
|
|
8197
8267
|
}
|
|
8198
8268
|
function pathResolvesBeyondSymlink(repoRoot, relativePath) {
|
|
@@ -8202,7 +8272,7 @@ function pathResolvesBeyondSymlink(repoRoot, relativePath) {
|
|
|
8202
8272
|
}
|
|
8203
8273
|
let current = repoRoot;
|
|
8204
8274
|
for (let index = 0;index < parts.length - 1; index += 1) {
|
|
8205
|
-
current =
|
|
8275
|
+
current = resolve26(current, parts[index]);
|
|
8206
8276
|
try {
|
|
8207
8277
|
if (lstatSync(current).isSymbolicLink()) {
|
|
8208
8278
|
return true;
|
|
@@ -8214,7 +8284,7 @@ function pathResolvesBeyondSymlink(repoRoot, relativePath) {
|
|
|
8214
8284
|
return false;
|
|
8215
8285
|
}
|
|
8216
8286
|
function printRepoStatus(projectRoot, label, repo, expectedBranch) {
|
|
8217
|
-
if (!
|
|
8287
|
+
if (!existsSync24(resolve26(repo, ".git"))) {
|
|
8218
8288
|
console.log(`${label}: unavailable (${repo})`);
|
|
8219
8289
|
return;
|
|
8220
8290
|
}
|
|
@@ -8250,7 +8320,7 @@ function resolveTaskBranchId(projectRoot, taskId) {
|
|
|
8250
8320
|
}
|
|
8251
8321
|
} catch {}
|
|
8252
8322
|
const artifactDir = artifactDirForId(projectRoot, taskId);
|
|
8253
|
-
if (
|
|
8323
|
+
if (existsSync24(artifactDir)) {
|
|
8254
8324
|
return taskId;
|
|
8255
8325
|
}
|
|
8256
8326
|
throw new Error(`Unknown task id: ${taskId}`);
|
|
@@ -8286,11 +8356,11 @@ function runCapture2(command, cwd, projectRoot = cwd) {
|
|
|
8286
8356
|
}
|
|
8287
8357
|
function runtimeGitEnv(projectRoot) {
|
|
8288
8358
|
const { ctx, runtimeRoot } = resolveRuntimeMetadata(projectRoot);
|
|
8289
|
-
const runtimeHome = runtimeRoot ?
|
|
8290
|
-
const runtimeTmp = runtimeRoot ?
|
|
8291
|
-
const runtimeCache = runtimeRoot ?
|
|
8292
|
-
const runtimeKnownHosts = runtimeHome ?
|
|
8293
|
-
const runtimeKey = runtimeHome ?
|
|
8359
|
+
const runtimeHome = runtimeRoot ? resolve26(runtimeRoot, "home") : "";
|
|
8360
|
+
const runtimeTmp = runtimeRoot ? resolve26(runtimeRoot, "tmp") : "";
|
|
8361
|
+
const runtimeCache = runtimeRoot ? resolve26(runtimeRoot, "cache") : "";
|
|
8362
|
+
const runtimeKnownHosts = runtimeHome ? resolve26(runtimeHome, ".ssh", "known_hosts") : "";
|
|
8363
|
+
const runtimeKey = runtimeHome ? resolve26(runtimeHome, ".ssh", "rig-agent-key") : "";
|
|
8294
8364
|
const env = {};
|
|
8295
8365
|
if (ctx?.workspaceDir) {
|
|
8296
8366
|
env.PROJECT_RIG_ROOT = projectRoot;
|
|
@@ -8303,14 +8373,14 @@ function runtimeGitEnv(projectRoot) {
|
|
|
8303
8373
|
if (runtimeRoot) {
|
|
8304
8374
|
env.RIG_RUNTIME_HOME = runtimeRoot;
|
|
8305
8375
|
}
|
|
8306
|
-
if (runtimeHome &&
|
|
8376
|
+
if (runtimeHome && existsSync24(runtimeHome)) {
|
|
8307
8377
|
env.HOME = runtimeHome;
|
|
8308
8378
|
env.OPENSSL_CONF = ensureRuntimeOpenSslConfig(runtimeHome);
|
|
8309
8379
|
}
|
|
8310
|
-
if (runtimeTmp &&
|
|
8380
|
+
if (runtimeTmp && existsSync24(runtimeTmp)) {
|
|
8311
8381
|
env.TMPDIR = runtimeTmp;
|
|
8312
8382
|
}
|
|
8313
|
-
if (runtimeCache &&
|
|
8383
|
+
if (runtimeCache && existsSync24(runtimeCache)) {
|
|
8314
8384
|
env.XDG_CACHE_HOME = runtimeCache;
|
|
8315
8385
|
}
|
|
8316
8386
|
const workspaceSecrets = loadDotEnvSecrets(ctx?.workspaceDir || projectRoot, process.env);
|
|
@@ -8354,14 +8424,14 @@ function runtimeGitEnv(projectRoot) {
|
|
|
8354
8424
|
env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
|
|
8355
8425
|
applyGitHubCredentialHelperEnv(env);
|
|
8356
8426
|
}
|
|
8357
|
-
if (runtimeKnownHosts &&
|
|
8427
|
+
if (runtimeKnownHosts && existsSync24(runtimeKnownHosts)) {
|
|
8358
8428
|
const sshParts = [
|
|
8359
8429
|
"ssh",
|
|
8360
8430
|
`-o UserKnownHostsFile="${runtimeKnownHosts}"`,
|
|
8361
8431
|
"-o StrictHostKeyChecking=yes",
|
|
8362
8432
|
"-F /dev/null"
|
|
8363
8433
|
];
|
|
8364
|
-
if (runtimeKey &&
|
|
8434
|
+
if (runtimeKey && existsSync24(runtimeKey)) {
|
|
8365
8435
|
sshParts.splice(1, 0, `-i "${runtimeKey}"`, "-o IdentitiesOnly=yes");
|
|
8366
8436
|
}
|
|
8367
8437
|
env.GIT_SSH_COMMAND = sshParts.join(" ");
|
|
@@ -8382,12 +8452,12 @@ function loadPersistedRuntimeSecrets(runtimeRoot) {
|
|
|
8382
8452
|
if (!runtimeRoot) {
|
|
8383
8453
|
return {};
|
|
8384
8454
|
}
|
|
8385
|
-
const path =
|
|
8386
|
-
if (!
|
|
8455
|
+
const path = resolve26(runtimeRoot, "runtime-secrets.json");
|
|
8456
|
+
if (!existsSync24(path)) {
|
|
8387
8457
|
return {};
|
|
8388
8458
|
}
|
|
8389
8459
|
try {
|
|
8390
|
-
const parsed = JSON.parse(
|
|
8460
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
8391
8461
|
const entries = Object.entries(parsed).filter((entry) => typeof entry[1] === "string");
|
|
8392
8462
|
return Object.fromEntries(entries);
|
|
8393
8463
|
} catch {
|
|
@@ -8395,13 +8465,13 @@ function loadPersistedRuntimeSecrets(runtimeRoot) {
|
|
|
8395
8465
|
}
|
|
8396
8466
|
}
|
|
8397
8467
|
function ensureRuntimeOpenSslConfig(runtimeHome) {
|
|
8398
|
-
const sslDir =
|
|
8399
|
-
const sslConfig =
|
|
8400
|
-
if (!
|
|
8401
|
-
|
|
8468
|
+
const sslDir = resolve26(runtimeHome, ".ssl");
|
|
8469
|
+
const sslConfig = resolve26(sslDir, "openssl.cnf");
|
|
8470
|
+
if (!existsSync24(sslDir)) {
|
|
8471
|
+
mkdirSync12(sslDir, { recursive: true });
|
|
8402
8472
|
}
|
|
8403
|
-
if (!
|
|
8404
|
-
|
|
8473
|
+
if (!existsSync24(sslConfig)) {
|
|
8474
|
+
writeFileSync12(sslConfig, `# Rig runtime OpenSSL config placeholder
|
|
8405
8475
|
`);
|
|
8406
8476
|
}
|
|
8407
8477
|
return sslConfig;
|
|
@@ -8419,29 +8489,29 @@ function resolveRuntimeMetadata(projectRoot) {
|
|
|
8419
8489
|
if (contextFile) {
|
|
8420
8490
|
return {
|
|
8421
8491
|
ctx,
|
|
8422
|
-
runtimeRoot:
|
|
8492
|
+
runtimeRoot: dirname12(resolve26(contextFile))
|
|
8423
8493
|
};
|
|
8424
8494
|
}
|
|
8425
8495
|
const inferredContextFile = findRuntimeContextFile2(projectRoot);
|
|
8426
|
-
if (
|
|
8496
|
+
if (existsSync24(inferredContextFile)) {
|
|
8427
8497
|
try {
|
|
8428
8498
|
ctx = loadRuntimeContext(inferredContextFile);
|
|
8429
8499
|
} catch {}
|
|
8430
8500
|
return {
|
|
8431
8501
|
ctx,
|
|
8432
|
-
runtimeRoot:
|
|
8502
|
+
runtimeRoot: dirname12(inferredContextFile)
|
|
8433
8503
|
};
|
|
8434
8504
|
}
|
|
8435
8505
|
return { ctx, runtimeRoot: "" };
|
|
8436
8506
|
}
|
|
8437
8507
|
function findRuntimeContextFile2(startPath) {
|
|
8438
|
-
let current =
|
|
8508
|
+
let current = resolve26(startPath);
|
|
8439
8509
|
while (true) {
|
|
8440
|
-
const candidate =
|
|
8441
|
-
if (
|
|
8510
|
+
const candidate = resolve26(current, "runtime-context.json");
|
|
8511
|
+
if (existsSync24(candidate)) {
|
|
8442
8512
|
return candidate;
|
|
8443
8513
|
}
|
|
8444
|
-
const parent =
|
|
8514
|
+
const parent = dirname12(current);
|
|
8445
8515
|
if (parent === current) {
|
|
8446
8516
|
return "";
|
|
8447
8517
|
}
|
|
@@ -8450,7 +8520,7 @@ function findRuntimeContextFile2(startPath) {
|
|
|
8450
8520
|
}
|
|
8451
8521
|
|
|
8452
8522
|
// packages/runtime/src/control-plane/native/profile-ops.ts
|
|
8453
|
-
import { existsSync as
|
|
8523
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync13, writeFileSync as writeFileSync13 } from "fs";
|
|
8454
8524
|
var DEFAULTS = {
|
|
8455
8525
|
model: parseEnvOrDefault(process.env.DEFAULT_AGENT_MODEL, ["claude", "gpt-codex", "pi"], "pi"),
|
|
8456
8526
|
runtime: parseEnvOrDefault(process.env.DEFAULT_AGENT_RUNTIME, ["claude-code", "codex-app-server", "pi"], "pi"),
|
|
@@ -8465,7 +8535,7 @@ function parseEnvOrDefault(value, allowed, fallback) {
|
|
|
8465
8535
|
return allowed.includes(value) ? value : fallback;
|
|
8466
8536
|
}
|
|
8467
8537
|
async function readProfileFile(path) {
|
|
8468
|
-
if (!
|
|
8538
|
+
if (!existsSync25(path)) {
|
|
8469
8539
|
return null;
|
|
8470
8540
|
}
|
|
8471
8541
|
try {
|
|
@@ -8518,14 +8588,14 @@ async function setProfile(projectRoot, options) {
|
|
|
8518
8588
|
plugin = model === "gpt-codex" ? "codex" : model === "pi" ? "pi" : "claude";
|
|
8519
8589
|
}
|
|
8520
8590
|
const paths = resolveHarnessPaths(projectRoot);
|
|
8521
|
-
|
|
8591
|
+
mkdirSync13(paths.stateDir, { recursive: true });
|
|
8522
8592
|
const next = {
|
|
8523
8593
|
model,
|
|
8524
8594
|
runtime,
|
|
8525
8595
|
agent_plugin: plugin,
|
|
8526
8596
|
updated_at: new Date().toISOString()
|
|
8527
8597
|
};
|
|
8528
|
-
|
|
8598
|
+
writeFileSync13(paths.agentProfilePath, `${JSON.stringify(next, null, 2)}
|
|
8529
8599
|
`, "utf-8");
|
|
8530
8600
|
await showProfile(projectRoot, false);
|
|
8531
8601
|
}
|
|
@@ -8561,13 +8631,13 @@ async function setReviewProfile(projectRoot, mode, provider) {
|
|
|
8561
8631
|
throw new Error(`Invalid provider: ${resolvedProvider}. Supported: greptile.`);
|
|
8562
8632
|
}
|
|
8563
8633
|
const paths = resolveHarnessPaths(projectRoot);
|
|
8564
|
-
|
|
8634
|
+
mkdirSync13(paths.stateDir, { recursive: true });
|
|
8565
8635
|
const next = {
|
|
8566
8636
|
mode,
|
|
8567
8637
|
provider: resolvedProvider,
|
|
8568
8638
|
updated_at: new Date().toISOString()
|
|
8569
8639
|
};
|
|
8570
|
-
|
|
8640
|
+
writeFileSync13(paths.reviewProfilePath, `${JSON.stringify(next, null, 2)}
|
|
8571
8641
|
`, "utf-8");
|
|
8572
8642
|
await showReviewProfile(projectRoot);
|
|
8573
8643
|
}
|
|
@@ -8605,44 +8675,44 @@ async function loadReviewProfile(path) {
|
|
|
8605
8675
|
}
|
|
8606
8676
|
|
|
8607
8677
|
// packages/runtime/src/control-plane/native/repo-ops.ts
|
|
8608
|
-
import { existsSync as
|
|
8609
|
-
import { basename as basename8, dirname as
|
|
8678
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync17, readFileSync as readFileSync15, readdirSync as readdirSync6, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "fs";
|
|
8679
|
+
import { basename as basename8, dirname as dirname14, resolve as resolve30 } from "path";
|
|
8610
8680
|
// packages/runtime/src/control-plane/repos/mirror/bootstrap.ts
|
|
8611
|
-
import { existsSync as
|
|
8612
|
-
import { resolve as
|
|
8681
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync15, realpathSync as realpathSync2 } from "fs";
|
|
8682
|
+
import { resolve as resolve28 } from "path";
|
|
8613
8683
|
|
|
8614
8684
|
// packages/runtime/src/control-plane/authority-files.ts
|
|
8615
|
-
import { existsSync as
|
|
8616
|
-
import { dirname as
|
|
8685
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync14, appendFileSync as appendFileSync2, copyFileSync as copyFileSync3, statSync as statSync5, readdirSync as readdirSync5, chmodSync as chmodSync3 } from "fs";
|
|
8686
|
+
import { dirname as dirname13, join as join4, relative, resolve as resolve27 } from "path";
|
|
8617
8687
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
8618
8688
|
function resolveAuthorityProjectStateDir(projectRoot) {
|
|
8619
8689
|
const explicit = process.env.RIG_STATE_DIR?.trim();
|
|
8620
8690
|
if (explicit) {
|
|
8621
|
-
return
|
|
8691
|
+
return resolve27(explicit);
|
|
8622
8692
|
}
|
|
8623
|
-
return
|
|
8693
|
+
return resolve27(resolve27(projectRoot), ".rig", "state");
|
|
8624
8694
|
}
|
|
8625
8695
|
function readJsonAtPath(path, fallback) {
|
|
8626
|
-
if (!
|
|
8696
|
+
if (!existsSync26(path)) {
|
|
8627
8697
|
return fallback;
|
|
8628
8698
|
}
|
|
8629
8699
|
try {
|
|
8630
|
-
return JSON.parse(
|
|
8700
|
+
return JSON.parse(readFileSync14(path, "utf-8"));
|
|
8631
8701
|
} catch {
|
|
8632
8702
|
return fallback;
|
|
8633
8703
|
}
|
|
8634
8704
|
}
|
|
8635
8705
|
function writeJsonAtPath(path, value) {
|
|
8636
|
-
|
|
8637
|
-
|
|
8706
|
+
mkdirSync14(dirname13(path), { recursive: true });
|
|
8707
|
+
writeFileSync14(path, `${JSON.stringify(value, null, 2)}
|
|
8638
8708
|
`, "utf8");
|
|
8639
8709
|
return path;
|
|
8640
8710
|
}
|
|
8641
8711
|
function readAuthorityProjectStateJson(projectRoot, relativePath, fallback) {
|
|
8642
|
-
return readJsonAtPath(
|
|
8712
|
+
return readJsonAtPath(resolve27(resolveAuthorityProjectStateDir(projectRoot), relativePath), fallback);
|
|
8643
8713
|
}
|
|
8644
8714
|
function writeAuthorityProjectStateJson(projectRoot, relativePath, value) {
|
|
8645
|
-
return writeJsonAtPath(
|
|
8715
|
+
return writeJsonAtPath(resolve27(resolveAuthorityProjectStateDir(projectRoot), relativePath), value);
|
|
8646
8716
|
}
|
|
8647
8717
|
|
|
8648
8718
|
// packages/runtime/src/control-plane/repos/mirror/state.ts
|
|
@@ -8702,7 +8772,7 @@ function sameExistingPath(left, right) {
|
|
|
8702
8772
|
try {
|
|
8703
8773
|
return realpathSync2(left) === realpathSync2(right);
|
|
8704
8774
|
} catch {
|
|
8705
|
-
return
|
|
8775
|
+
return resolve28(left) === resolve28(right);
|
|
8706
8776
|
}
|
|
8707
8777
|
}
|
|
8708
8778
|
function repoLooksUsable(repoRoot, projectRoot) {
|
|
@@ -8738,7 +8808,7 @@ function resolveMirrorRemoteUrl(layout) {
|
|
|
8738
8808
|
}
|
|
8739
8809
|
}
|
|
8740
8810
|
}
|
|
8741
|
-
if (
|
|
8811
|
+
if (existsSync27(resolve28(layout.checkoutRoot, ".git")) && checkoutLooksUsable(layout)) {
|
|
8742
8812
|
const checkoutOrigin = runGit(["git", "-C", layout.checkoutRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
8743
8813
|
if (checkoutOrigin.exitCode === 0) {
|
|
8744
8814
|
const currentOrigin = checkoutOrigin.stdout.trim();
|
|
@@ -8751,9 +8821,9 @@ function resolveMirrorRemoteUrl(layout) {
|
|
|
8751
8821
|
}
|
|
8752
8822
|
function ensureManagedRepoMirror(projectRoot, repoId) {
|
|
8753
8823
|
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
8754
|
-
|
|
8824
|
+
mkdirSync15(layout.metadataRoot, { recursive: true });
|
|
8755
8825
|
const remoteUrl = resolveMirrorRemoteUrl(layout);
|
|
8756
|
-
if (!
|
|
8826
|
+
if (!existsSync27(resolve28(layout.mirrorRoot, "HEAD"))) {
|
|
8757
8827
|
ensureGitSuccess(runGit(["git", "init", "--bare", layout.mirrorRoot], layout.projectRoot), ["git", "init", "--bare", layout.mirrorRoot]);
|
|
8758
8828
|
}
|
|
8759
8829
|
const getOrigin = runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
@@ -8773,8 +8843,8 @@ function ensureManagedRepoMirror(projectRoot, repoId) {
|
|
|
8773
8843
|
return layout;
|
|
8774
8844
|
}
|
|
8775
8845
|
// packages/runtime/src/control-plane/repos/mirror/refresh.ts
|
|
8776
|
-
import { existsSync as
|
|
8777
|
-
import { resolve as
|
|
8846
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync16, realpathSync as realpathSync3, rmSync as rmSync6 } from "fs";
|
|
8847
|
+
import { resolve as resolve29 } from "path";
|
|
8778
8848
|
function nowIso3() {
|
|
8779
8849
|
return new Date().toISOString();
|
|
8780
8850
|
}
|
|
@@ -8801,7 +8871,7 @@ function sameExistingPath2(left, right) {
|
|
|
8801
8871
|
try {
|
|
8802
8872
|
return realpathSync3(left) === realpathSync3(right);
|
|
8803
8873
|
} catch {
|
|
8804
|
-
return
|
|
8874
|
+
return resolve29(left) === resolve29(right);
|
|
8805
8875
|
}
|
|
8806
8876
|
}
|
|
8807
8877
|
function ensureMirrorHead(layout) {
|
|
@@ -8847,12 +8917,12 @@ function checkoutLooksUsable2(layout) {
|
|
|
8847
8917
|
return probe.exitCode === 0 && sameExistingPath2(probe.stdout.trim(), layout.checkoutRoot);
|
|
8848
8918
|
}
|
|
8849
8919
|
function ensureCheckoutFromMirror(layout) {
|
|
8850
|
-
|
|
8851
|
-
const gitPath =
|
|
8852
|
-
if (
|
|
8920
|
+
mkdirSync16(resolve29(layout.checkoutRoot, ".."), { recursive: true });
|
|
8921
|
+
const gitPath = resolve29(layout.checkoutRoot, ".git");
|
|
8922
|
+
if (existsSync28(layout.checkoutRoot) && (!existsSync28(gitPath) || !checkoutLooksUsable2(layout))) {
|
|
8853
8923
|
rmSync6(layout.checkoutRoot, { recursive: true, force: true });
|
|
8854
8924
|
}
|
|
8855
|
-
if (!
|
|
8925
|
+
if (!existsSync28(gitPath)) {
|
|
8856
8926
|
ensureGitSuccess2(runGit2(["git", "clone", layout.mirrorRoot, layout.checkoutRoot], layout.projectRoot), ["git", "clone", layout.mirrorRoot, layout.checkoutRoot]);
|
|
8857
8927
|
}
|
|
8858
8928
|
const getOrigin = runGit2(["git", "-C", layout.checkoutRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
@@ -8953,7 +9023,7 @@ function repoDiscover(projectRoot, taskId) {
|
|
|
8953
9023
|
}
|
|
8954
9024
|
function repoBaseline(projectRoot, refresh = false) {
|
|
8955
9025
|
const paths = resolveRepoDiscoveryPaths(projectRoot);
|
|
8956
|
-
if (!refresh &&
|
|
9026
|
+
if (!refresh && existsSync29(paths.baseRepoPinsPath)) {
|
|
8957
9027
|
const baseline = readJsonFile(paths.baseRepoPinsPath, { recorded_at: nowIso(), repos: {} });
|
|
8958
9028
|
return baseline.repos || {};
|
|
8959
9029
|
}
|
|
@@ -8977,8 +9047,8 @@ function ensureMonorepoReady(projectRoot) {
|
|
|
8977
9047
|
}
|
|
8978
9048
|
function persistBaselinePins(projectRoot, repos) {
|
|
8979
9049
|
const paths = resolveRepoDiscoveryPaths(projectRoot);
|
|
8980
|
-
|
|
8981
|
-
|
|
9050
|
+
mkdirSync17(resolve30(paths.baseRepoPinsPath, ".."), { recursive: true });
|
|
9051
|
+
writeFileSync15(paths.baseRepoPinsPath, `${JSON.stringify({ recorded_at: nowIso(), repos }, null, 2)}
|
|
8982
9052
|
`, "utf-8");
|
|
8983
9053
|
return repos;
|
|
8984
9054
|
}
|
|
@@ -9057,28 +9127,28 @@ function readRepoDiscoveryTaskConfig(projectRoot) {
|
|
|
9057
9127
|
function resolveRepoDiscoveryPaths(projectRoot) {
|
|
9058
9128
|
const monorepoRoot = resolveMonorepoRepoLayout(projectRoot).checkoutRoot;
|
|
9059
9129
|
const explicitHostProjectRoot = (process.env.RIG_HOST_PROJECT_ROOT || "").trim();
|
|
9060
|
-
const normalizedProjectRoot =
|
|
9130
|
+
const normalizedProjectRoot = resolve30(projectRoot);
|
|
9061
9131
|
const hostProjectRoot = explicitHostProjectRoot && shouldUseHostProjectStateRoot(normalizedProjectRoot) ? explicitHostProjectRoot : normalizedProjectRoot;
|
|
9062
|
-
const stateDir =
|
|
9132
|
+
const stateDir = resolve30(hostProjectRoot, ".rig", "state");
|
|
9063
9133
|
return {
|
|
9064
9134
|
monorepoRoot,
|
|
9065
|
-
taskRepoCommitsPath:
|
|
9066
|
-
baseRepoPinsPath:
|
|
9135
|
+
taskRepoCommitsPath: resolve30(stateDir, "task-repo-commits.json"),
|
|
9136
|
+
baseRepoPinsPath: resolve30(stateDir, "base-repo-pins.json")
|
|
9067
9137
|
};
|
|
9068
9138
|
}
|
|
9069
9139
|
function shouldUseHostProjectStateRoot(projectRoot) {
|
|
9070
9140
|
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
9071
|
-
if (runtimeWorkspace &&
|
|
9141
|
+
if (runtimeWorkspace && resolve30(runtimeWorkspace) === projectRoot) {
|
|
9072
9142
|
return true;
|
|
9073
9143
|
}
|
|
9074
|
-
return basename8(
|
|
9144
|
+
return basename8(dirname14(projectRoot)) === ".worktrees";
|
|
9075
9145
|
}
|
|
9076
9146
|
function readPinFromArtifact(projectRoot, depTask, repoKey) {
|
|
9077
|
-
const snapshot =
|
|
9078
|
-
if (!
|
|
9147
|
+
const snapshot = resolve30(artifactDirForId(projectRoot, depTask), "git-state.txt");
|
|
9148
|
+
if (!existsSync29(snapshot)) {
|
|
9079
9149
|
return "";
|
|
9080
9150
|
}
|
|
9081
|
-
const content =
|
|
9151
|
+
const content = readFileSync15(snapshot, "utf-8");
|
|
9082
9152
|
const chunk = content.split(/\r?\n/);
|
|
9083
9153
|
let inSection = false;
|
|
9084
9154
|
for (const line of chunk) {
|
|
@@ -9100,12 +9170,12 @@ function repoPath(projectRoot, key) {
|
|
|
9100
9170
|
if (managed) {
|
|
9101
9171
|
return managed.checkoutRoot;
|
|
9102
9172
|
}
|
|
9103
|
-
return key.startsWith("/") ? key :
|
|
9173
|
+
return key.startsWith("/") ? key : resolve30(projectRoot, key);
|
|
9104
9174
|
}
|
|
9105
9175
|
function applyPins(projectRoot, pins) {
|
|
9106
9176
|
for (const [key, commit] of Object.entries(pins)) {
|
|
9107
9177
|
const path = repoPath(projectRoot, key);
|
|
9108
|
-
if (!
|
|
9178
|
+
if (!existsSync29(resolve30(path, ".git"))) {
|
|
9109
9179
|
throw new Error(`Repo for pin not available: ${key} (${path})`);
|
|
9110
9180
|
}
|
|
9111
9181
|
let hasCommit = runGitCapture(["git", "-C", path, "cat-file", "-e", `${commit}^{commit}`], projectRoot).exitCode === 0;
|
|
@@ -9134,7 +9204,7 @@ function verifyPins(projectRoot, pins) {
|
|
|
9134
9204
|
let ok = true;
|
|
9135
9205
|
for (const [key, expected] of Object.entries(pins)) {
|
|
9136
9206
|
const path = repoPath(projectRoot, key);
|
|
9137
|
-
if (!
|
|
9207
|
+
if (!existsSync29(resolve30(path, ".git"))) {
|
|
9138
9208
|
console.error(`ERROR: repo missing during pin verification: ${key}`);
|
|
9139
9209
|
ok = false;
|
|
9140
9210
|
continue;
|
|
@@ -9159,23 +9229,23 @@ function resolveRuntimeGitEnv() {
|
|
|
9159
9229
|
GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND
|
|
9160
9230
|
};
|
|
9161
9231
|
}
|
|
9162
|
-
const runtimeRoot = process.env.RIG_RUNTIME_HOME?.trim() || (process.env.RIG_RUNTIME_CONTEXT_FILE?.trim() ?
|
|
9163
|
-
const runtimeHome = runtimeRoot ?
|
|
9232
|
+
const runtimeRoot = process.env.RIG_RUNTIME_HOME?.trim() || (process.env.RIG_RUNTIME_CONTEXT_FILE?.trim() ? resolve30(process.env.RIG_RUNTIME_CONTEXT_FILE, "..") : inferRuntimeRootFromWorkspace(process.cwd()));
|
|
9233
|
+
const runtimeHome = runtimeRoot ? resolve30(runtimeRoot, "home") : process.env.HOME?.trim() || "";
|
|
9164
9234
|
if (!runtimeHome) {
|
|
9165
9235
|
return;
|
|
9166
9236
|
}
|
|
9167
|
-
const knownHostsPath =
|
|
9168
|
-
if (!
|
|
9237
|
+
const knownHostsPath = resolve30(runtimeHome, ".ssh", "known_hosts");
|
|
9238
|
+
if (!existsSync29(knownHostsPath)) {
|
|
9169
9239
|
return { HOME: runtimeHome };
|
|
9170
9240
|
}
|
|
9171
|
-
const agentSshKey =
|
|
9241
|
+
const agentSshKey = resolve30(runtimeHome, ".ssh", "rig-agent-key");
|
|
9172
9242
|
const sshParts = [
|
|
9173
9243
|
"ssh",
|
|
9174
9244
|
`-o UserKnownHostsFile="${knownHostsPath}"`,
|
|
9175
9245
|
"-o StrictHostKeyChecking=yes",
|
|
9176
9246
|
"-F /dev/null"
|
|
9177
9247
|
];
|
|
9178
|
-
if (
|
|
9248
|
+
if (existsSync29(agentSshKey)) {
|
|
9179
9249
|
sshParts.splice(1, 0, `-i "${agentSshKey}"`, "-o IdentitiesOnly=yes");
|
|
9180
9250
|
}
|
|
9181
9251
|
return {
|
|
@@ -9185,24 +9255,24 @@ function resolveRuntimeGitEnv() {
|
|
|
9185
9255
|
}
|
|
9186
9256
|
function inferRuntimeRootFromWorkspace(cwd) {
|
|
9187
9257
|
const contextPath = findRuntimeContextFile3(cwd);
|
|
9188
|
-
if (!contextPath || !
|
|
9258
|
+
if (!contextPath || !existsSync29(contextPath)) {
|
|
9189
9259
|
return "";
|
|
9190
9260
|
}
|
|
9191
9261
|
try {
|
|
9192
9262
|
loadRuntimeContext(contextPath);
|
|
9193
|
-
return
|
|
9263
|
+
return resolve30(contextPath, "..");
|
|
9194
9264
|
} catch {
|
|
9195
9265
|
return "";
|
|
9196
9266
|
}
|
|
9197
9267
|
}
|
|
9198
9268
|
function findRuntimeContextFile3(startPath) {
|
|
9199
|
-
let current =
|
|
9269
|
+
let current = resolve30(startPath);
|
|
9200
9270
|
while (true) {
|
|
9201
|
-
const candidate =
|
|
9202
|
-
if (
|
|
9271
|
+
const candidate = resolve30(current, "runtime-context.json");
|
|
9272
|
+
if (existsSync29(candidate)) {
|
|
9203
9273
|
return candidate;
|
|
9204
9274
|
}
|
|
9205
|
-
const parent =
|
|
9275
|
+
const parent = resolve30(current, "..");
|
|
9206
9276
|
if (parent === current) {
|
|
9207
9277
|
return "";
|
|
9208
9278
|
}
|
|
@@ -9211,13 +9281,13 @@ function findRuntimeContextFile3(startPath) {
|
|
|
9211
9281
|
}
|
|
9212
9282
|
|
|
9213
9283
|
// packages/runtime/src/control-plane/memory-sync/cli.ts
|
|
9214
|
-
import { existsSync as
|
|
9284
|
+
import { existsSync as existsSync30 } from "fs";
|
|
9215
9285
|
import { randomUUID } from "crypto";
|
|
9216
9286
|
|
|
9217
9287
|
// packages/runtime/src/control-plane/memory-sync/db.ts
|
|
9218
9288
|
import { Database } from "bun:sqlite";
|
|
9219
|
-
import { mkdirSync as
|
|
9220
|
-
import { dirname as
|
|
9289
|
+
import { mkdirSync as mkdirSync18 } from "fs";
|
|
9290
|
+
import { dirname as dirname15 } from "path";
|
|
9221
9291
|
|
|
9222
9292
|
// packages/runtime/src/control-plane/memory-sync/types.ts
|
|
9223
9293
|
var NO_MATCH_RETRIEVAL_CANONICAL_KEY = "__memory_recall__:none";
|
|
@@ -9813,7 +9883,7 @@ async function validateEventTargets(executor, event) {
|
|
|
9813
9883
|
}
|
|
9814
9884
|
}
|
|
9815
9885
|
async function openMemoryDb(dbPath) {
|
|
9816
|
-
|
|
9886
|
+
mkdirSync18(dirname15(dbPath), { recursive: true });
|
|
9817
9887
|
const sqlite = new Database(dbPath, { create: true, strict: true });
|
|
9818
9888
|
const client = createMemoryDbClient(sqlite);
|
|
9819
9889
|
const db = {
|
|
@@ -10232,7 +10302,7 @@ function formatMemoryQueryResults(results) {
|
|
|
10232
10302
|
}
|
|
10233
10303
|
|
|
10234
10304
|
// packages/runtime/src/control-plane/memory-sync/read.ts
|
|
10235
|
-
import { mkdtempSync, rmSync as rmSync8, writeFileSync as
|
|
10305
|
+
import { mkdtempSync, rmSync as rmSync8, writeFileSync as writeFileSync16 } from "fs";
|
|
10236
10306
|
import { tmpdir as tmpdir5 } from "os";
|
|
10237
10307
|
import { join as join5 } from "path";
|
|
10238
10308
|
var CANONICAL_MEMORY_DB_PATH = "rig/memory/project-memory.db";
|
|
@@ -10261,7 +10331,7 @@ async function readCanonicalMemoryDb(projectRoot, deps = {}) {
|
|
|
10261
10331
|
try {
|
|
10262
10332
|
try {
|
|
10263
10333
|
const bytes = readDeps.readBlobBytesAtRef(repoPath2, baseOid, CANONICAL_MEMORY_DB_PATH);
|
|
10264
|
-
|
|
10334
|
+
writeFileSync16(dbPath, bytes);
|
|
10265
10335
|
} catch (error) {
|
|
10266
10336
|
if (!isMissingCanonicalMemoryBlobError(error)) {
|
|
10267
10337
|
throw error;
|
|
@@ -10419,7 +10489,7 @@ function requireRuntimeMemoryContext(runtimeContext) {
|
|
|
10419
10489
|
}
|
|
10420
10490
|
async function ensureRuntimeMemoryUsable(runtimeContext) {
|
|
10421
10491
|
const memory = requireRuntimeMemoryContext(runtimeContext);
|
|
10422
|
-
if (!
|
|
10492
|
+
if (!existsSync30(memory.hydratedPath)) {
|
|
10423
10493
|
throw new Error(`Shared memory database is missing: ${memory.hydratedPath}`);
|
|
10424
10494
|
}
|
|
10425
10495
|
const db = await openMemoryDb(memory.hydratedPath);
|
|
@@ -10720,8 +10790,8 @@ async function executeHarnessCommand(projectRoot, args, eventBus, pluginHostCtx)
|
|
|
10720
10790
|
}
|
|
10721
10791
|
case "completion-verification":
|
|
10722
10792
|
case "completition-verification": {
|
|
10723
|
-
const hookPath =
|
|
10724
|
-
if (!
|
|
10793
|
+
const hookPath = resolve31(projectRoot, ".rig/bin/hooks/completion-verification");
|
|
10794
|
+
if (!existsSync31(hookPath)) {
|
|
10725
10795
|
throw new Error(`completion-verification hook binary not found: ${hookPath}`);
|
|
10726
10796
|
}
|
|
10727
10797
|
const proc = Bun.spawn([hookPath], {
|