@h-rig/runtime 0.0.6-alpha.32 → 0.0.6-alpha.34

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.
@@ -1361,16 +1361,297 @@ function readBuildConfig() {
1361
1361
  }
1362
1362
  }
1363
1363
 
1364
- // packages/runtime/src/control-plane/runtime/tooling/shell.ts
1364
+ // packages/runtime/src/control-plane/native/git-native.ts
1365
+ import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
1365
1366
  import { tmpdir as tmpdir2 } from "os";
1366
- import { basename as basename2, dirname as dirname4, resolve as resolve7 } from "path";
1367
- var sharedNativeShellOutputDir = resolve7(tmpdir2(), "rig-native");
1368
- var sharedNativeShellOutputPath = resolve7(sharedNativeShellOutputDir, `rig-shell-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1369
- // packages/runtime/src/control-plane/runtime/tooling/file-tools.ts
1367
+ import { dirname as dirname4, isAbsolute, resolve as resolve7 } from "path";
1368
+ import { createHash } from "crypto";
1369
+ var sharedGitNativeOutputDir = resolve7(tmpdir2(), "rig-native");
1370
+ var sharedGitNativeOutputPath = resolve7(sharedGitNativeOutputDir, `rig-git-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1371
+ var trackerCommandUsageProbe = "usage: rig-git fetch-ref <repo-path> <remote> <branch>";
1372
+ function temporaryGitBinaryOutputPath(outputPath) {
1373
+ const suffix2 = process.platform === "win32" ? ".exe" : "";
1374
+ return resolve7(dirname4(outputPath), `.rig-git-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}${suffix2}`);
1375
+ }
1376
+ function publishGitBinary(tempOutputPath, outputPath) {
1377
+ try {
1378
+ renameSync2(tempOutputPath, outputPath);
1379
+ } catch (error) {
1380
+ if (process.platform === "win32" && existsSync7(outputPath)) {
1381
+ rmSync2(outputPath, { force: true });
1382
+ renameSync2(tempOutputPath, outputPath);
1383
+ return;
1384
+ }
1385
+ throw error;
1386
+ }
1387
+ }
1388
+ function runtimeRigGitFileName() {
1389
+ return `rig-git${process.platform === "win32" ? ".exe" : ""}`;
1390
+ }
1391
+ function rigGitSourceCandidates() {
1392
+ const execDir = process.execPath?.trim() ? dirname4(process.execPath.trim()) : "";
1393
+ const cwd = process.cwd()?.trim() || "";
1394
+ const projectRoot = process.env.PROJECT_RIG_ROOT?.trim() || "";
1395
+ const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || "";
1396
+ const moduleRelativeSource = resolve7(import.meta.dir, "../../../native/rig-git.zig");
1397
+ return [...new Set([
1398
+ process.env.RIG_NATIVE_GIT_SOURCE?.trim() || "",
1399
+ moduleRelativeSource,
1400
+ projectRoot ? resolve7(projectRoot, "packages/runtime/native/rig-git.zig") : "",
1401
+ hostProjectRoot ? resolve7(hostProjectRoot, "packages/runtime/native/rig-git.zig") : "",
1402
+ cwd ? resolve7(cwd, "packages/runtime/native/rig-git.zig") : "",
1403
+ execDir ? resolve7(execDir, "..", "..", "packages/runtime/native/rig-git.zig") : "",
1404
+ execDir ? resolve7(execDir, "..", "native", "rig-git.zig") : ""
1405
+ ].filter(Boolean))];
1406
+ }
1407
+ function nativePackageBinaryCandidates(fromDir, fileName) {
1408
+ const candidates = [];
1409
+ let cursor = resolve7(fromDir);
1410
+ for (let index = 0;index < 8; index += 1) {
1411
+ candidates.push(resolve7(cursor, "native", `${process.platform}-${process.arch}`, fileName), resolve7(cursor, "native", `${process.platform}-${process.arch}`, "bin", fileName), resolve7(cursor, "native", fileName), resolve7(cursor, "native", "bin", fileName));
1412
+ const parent = dirname4(cursor);
1413
+ if (parent === cursor)
1414
+ break;
1415
+ cursor = parent;
1416
+ }
1417
+ return candidates;
1418
+ }
1419
+ function rigGitBinaryCandidates() {
1420
+ const execDir = process.execPath?.trim() ? dirname4(process.execPath.trim()) : "";
1421
+ const fileName = runtimeRigGitFileName();
1422
+ const explicit = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
1423
+ return [...new Set([
1424
+ explicit,
1425
+ ...nativePackageBinaryCandidates(import.meta.dir, fileName),
1426
+ execDir ? resolve7(execDir, fileName) : "",
1427
+ execDir ? resolve7(execDir, "..", fileName) : "",
1428
+ execDir ? resolve7(execDir, "..", "bin", fileName) : "",
1429
+ sharedGitNativeOutputPath
1430
+ ].filter(Boolean))];
1431
+ }
1432
+ function resolveGitSourcePath() {
1433
+ for (const candidate of rigGitSourceCandidates()) {
1434
+ if (candidate && existsSync7(candidate)) {
1435
+ return candidate;
1436
+ }
1437
+ }
1438
+ return null;
1439
+ }
1440
+ function resolveGitBinaryPath() {
1441
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
1442
+ return null;
1443
+ }
1444
+ for (const candidate of rigGitBinaryCandidates()) {
1445
+ if (candidate && existsSync7(candidate)) {
1446
+ return candidate;
1447
+ }
1448
+ }
1449
+ return null;
1450
+ }
1451
+ function preferredGitBinaryOutputPath() {
1452
+ const explicit = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
1453
+ return explicit || sharedGitNativeOutputPath;
1454
+ }
1455
+ function binarySupportsTrackerCommandsSync(binaryPath) {
1456
+ try {
1457
+ const probe = Bun.spawnSync([binaryPath, "fetch-ref", "."], {
1458
+ stdout: "pipe",
1459
+ stderr: "pipe"
1460
+ });
1461
+ const stdout = probe.stdout.toString().trim();
1462
+ const stderr = probe.stderr.toString().trim();
1463
+ if (stdout.includes('"error":"unknown command"')) {
1464
+ return false;
1465
+ }
1466
+ return probe.exitCode === 2 && stderr.includes(trackerCommandUsageProbe);
1467
+ } catch {
1468
+ return false;
1469
+ }
1470
+ }
1471
+ function nativeBuildManifestPath(outputPath) {
1472
+ return `${outputPath}.build-manifest.json`;
1473
+ }
1474
+ function hasMatchingNativeBuildManifestSync(manifestPath, buildKey) {
1475
+ if (!existsSync7(manifestPath)) {
1476
+ return false;
1477
+ }
1478
+ try {
1479
+ const manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
1480
+ return manifest.version === 1 && manifest.buildKey === buildKey;
1481
+ } catch {
1482
+ return false;
1483
+ }
1484
+ }
1485
+ function sha256FileSync(path) {
1486
+ return createHash("sha256").update(readFileSync5(path)).digest("hex");
1487
+ }
1488
+ function ensureRigGitBinaryPathSync(outputPath = preferredGitBinaryOutputPath()) {
1489
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
1490
+ throw new Error("Zig native git is disabled via RIG_DISABLE_ZIG_NATIVE=1");
1491
+ }
1492
+ const sourcePath = resolveGitSourcePath();
1493
+ if (!sourcePath) {
1494
+ const binaryPath = resolveGitBinaryPath();
1495
+ if (binaryPath) {
1496
+ return binaryPath;
1497
+ }
1498
+ throw new Error("rig-git.zig source file not found.");
1499
+ }
1500
+ const zigBinary = Bun.which("zig");
1501
+ if (!zigBinary) {
1502
+ throw new Error("zig is required to build native Rig git tools.");
1503
+ }
1504
+ mkdirSync3(dirname4(outputPath), { recursive: true });
1505
+ const sourceDigest = sha256FileSync(sourcePath);
1506
+ const buildKey = JSON.stringify({
1507
+ version: 1,
1508
+ zigBinary,
1509
+ platform: process.platform,
1510
+ arch: process.arch,
1511
+ sourcePath,
1512
+ sourceDigest
1513
+ });
1514
+ const manifestPath = nativeBuildManifestPath(outputPath);
1515
+ const needsBuild = !existsSync7(outputPath) || !hasMatchingNativeBuildManifestSync(manifestPath, buildKey) || !binarySupportsTrackerCommandsSync(outputPath);
1516
+ if (!needsBuild) {
1517
+ chmodSync(outputPath, 493);
1518
+ return outputPath;
1519
+ }
1520
+ const tempOutputPath = temporaryGitBinaryOutputPath(outputPath);
1521
+ const build = Bun.spawnSync([
1522
+ zigBinary,
1523
+ "build-exe",
1524
+ sourcePath,
1525
+ "-O",
1526
+ "ReleaseFast",
1527
+ `-femit-bin=${tempOutputPath}`
1528
+ ], {
1529
+ cwd: dirname4(sourcePath),
1530
+ stdout: "pipe",
1531
+ stderr: "pipe"
1532
+ });
1533
+ if (build.exitCode !== 0 || !existsSync7(tempOutputPath)) {
1534
+ const stderr = build.stderr.toString().trim();
1535
+ const stdout = build.stdout.toString().trim();
1536
+ const details = [stderr, stdout].filter(Boolean).join(`
1537
+ `);
1538
+ throw new Error(`Failed to build native Rig git tools: ${details || `zig exited with code ${build.exitCode}`}`);
1539
+ }
1540
+ chmodSync(tempOutputPath, 493);
1541
+ if (existsSync7(outputPath) && hasMatchingNativeBuildManifestSync(manifestPath, buildKey)) {
1542
+ rmSync2(tempOutputPath, { force: true });
1543
+ chmodSync(outputPath, 493);
1544
+ return outputPath;
1545
+ }
1546
+ publishGitBinary(tempOutputPath, outputPath);
1547
+ if (!binarySupportsTrackerCommandsSync(outputPath)) {
1548
+ rmSync2(outputPath, { force: true });
1549
+ throw new Error("Failed to build native Rig git tools: tracker command probe failed");
1550
+ }
1551
+ writeFileSync2(manifestPath, `${JSON.stringify({ version: 1, buildKey }, null, 2)}
1552
+ `, "utf8");
1553
+ return outputPath;
1554
+ }
1555
+ function runGitNative(command, args) {
1556
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
1557
+ return { ok: false, error: "rig-git native disabled" };
1558
+ }
1559
+ const trackerCommand = command === "fetch-ref" || command === "read-blob-at-ref" || command === "write-tree-commit" || command === "push-ref-with-lease";
1560
+ let binaryPath = null;
1561
+ if (trackerCommand) {
1562
+ try {
1563
+ binaryPath = ensureRigGitBinaryPathSync(preferredGitBinaryOutputPath());
1564
+ } catch (error) {
1565
+ const message = error instanceof Error ? error.message : String(error);
1566
+ if (message.includes("rig-git.zig source file not found")) {
1567
+ return { ok: false, error: "rig-git binary not found" };
1568
+ }
1569
+ return { ok: false, error: message };
1570
+ }
1571
+ } else {
1572
+ const explicitBinaryPath = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
1573
+ binaryPath = explicitBinaryPath && existsSync7(explicitBinaryPath) ? explicitBinaryPath : !explicitBinaryPath ? resolveGitBinaryPath() : null;
1574
+ if (!binaryPath) {
1575
+ try {
1576
+ binaryPath = ensureRigGitBinaryPathSync(preferredGitBinaryOutputPath());
1577
+ } catch (error) {
1578
+ const message = error instanceof Error ? error.message : String(error);
1579
+ if (message.includes("rig-git.zig source file not found")) {
1580
+ return { ok: false, error: "rig-git binary not found" };
1581
+ }
1582
+ return { ok: false, error: message };
1583
+ }
1584
+ }
1585
+ }
1586
+ try {
1587
+ const proc = Bun.spawnSync([binaryPath, command, ...args], {
1588
+ stdout: "pipe",
1589
+ stderr: "pipe",
1590
+ env: process.env
1591
+ });
1592
+ if (proc.exitCode !== 0) {
1593
+ const stdoutText = proc.stdout.toString().trim();
1594
+ if (stdoutText) {
1595
+ try {
1596
+ const parsed = JSON.parse(stdoutText);
1597
+ if (!parsed.ok) {
1598
+ return parsed;
1599
+ }
1600
+ } catch {}
1601
+ }
1602
+ const errText = proc.stderr.toString().trim() || `exit code ${proc.exitCode}`;
1603
+ return { ok: false, error: errText };
1604
+ }
1605
+ const output = proc.stdout.toString().trim();
1606
+ return JSON.parse(output);
1607
+ } catch (err) {
1608
+ return { ok: false, error: String(err) };
1609
+ }
1610
+ }
1611
+ function nativeBranchName(repoPath) {
1612
+ const result = runGitNative("branch-name", [repoPath]);
1613
+ if (!result.ok)
1614
+ return null;
1615
+ if ("value" in result && typeof result.value === "string")
1616
+ return result.value;
1617
+ return null;
1618
+ }
1619
+ function nativeChangeCount(repoPath) {
1620
+ const result = runGitNative("change-count", [repoPath]);
1621
+ if (!result.ok)
1622
+ return null;
1623
+ if ("count" in result && typeof result.count === "number")
1624
+ return result.count;
1625
+ return null;
1626
+ }
1627
+ function nativePendingFiles(repoPath) {
1628
+ const result = runGitNative("pending-files", [repoPath]);
1629
+ if (!result.ok)
1630
+ return null;
1631
+ if ("files" in result && Array.isArray(result.files)) {
1632
+ return result.files.map((f) => ({ path: f.path, status: f.status }));
1633
+ }
1634
+ return null;
1635
+ }
1636
+ function nativeFileHasChanges(repoPath, filePath) {
1637
+ const result = runGitNative("file-has-changes", [repoPath, filePath]);
1638
+ if (!result.ok)
1639
+ return null;
1640
+ if ("has_changes" in result && typeof result.has_changes === "boolean")
1641
+ return result.has_changes;
1642
+ return null;
1643
+ }
1644
+
1645
+ // packages/runtime/src/control-plane/runtime/tooling/shell.ts
1370
1646
  import { tmpdir as tmpdir3 } from "os";
1371
- import { basename as basename3, dirname as dirname5, resolve as resolve8 } from "path";
1372
- var sharedNativeToolsOutputDir = resolve8(tmpdir3(), "rig-native");
1373
- var sharedNativeToolsOutputPath = resolve8(sharedNativeToolsOutputDir, `rig-tools-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1647
+ import { basename as basename2, dirname as dirname5, resolve as resolve8 } from "path";
1648
+ var sharedNativeShellOutputDir = resolve8(tmpdir3(), "rig-native");
1649
+ var sharedNativeShellOutputPath = resolve8(sharedNativeShellOutputDir, `rig-shell-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1650
+ // packages/runtime/src/control-plane/runtime/tooling/file-tools.ts
1651
+ import { tmpdir as tmpdir4 } from "os";
1652
+ import { basename as basename3, dirname as dirname6, resolve as resolve9 } from "path";
1653
+ var sharedNativeToolsOutputDir = resolve9(tmpdir4(), "rig-native");
1654
+ var sharedNativeToolsOutputPath = resolve9(sharedNativeToolsOutputDir, `rig-tools-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1374
1655
  // packages/runtime/src/control-plane/plugin-host-context.ts
1375
1656
  import { createPluginHost } from "@rig/core";
1376
1657
  import { loadConfig } from "@rig/core/load-config";
@@ -1517,7 +1798,7 @@ function createTaskFieldRegistry(extensions) {
1517
1798
  }
1518
1799
 
1519
1800
  // packages/runtime/src/control-plane/validators/runtime-registration.ts
1520
- import { existsSync as existsSync7 } from "fs";
1801
+ import { existsSync as existsSync8 } from "fs";
1521
1802
  import { join } from "path";
1522
1803
  function createValidatorRegistry() {
1523
1804
  const map = new Map;
@@ -1550,7 +1831,7 @@ function registerBuiltInValidators(registry) {
1550
1831
  }
1551
1832
  async function runStdTypecheck(ctx) {
1552
1833
  const packageJsonPath = join(ctx.workspaceRoot, "package.json");
1553
- if (!existsSync7(packageJsonPath)) {
1834
+ if (!existsSync8(packageJsonPath)) {
1554
1835
  return {
1555
1836
  id: "std:typecheck",
1556
1837
  passed: false,
@@ -1578,8 +1859,8 @@ async function runStdTypecheck(ctx) {
1578
1859
  }
1579
1860
 
1580
1861
  // packages/runtime/src/control-plane/hook-materializer.ts
1581
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
1582
- import { dirname as dirname6, resolve as resolve9 } from "path";
1862
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
1863
+ import { dirname as dirname7, resolve as resolve10 } from "path";
1583
1864
  var MARKER_PLUGIN = "_rigPlugin";
1584
1865
  var MARKER_HOOK_ID = "_rigHookId";
1585
1866
  function matcherToString(matcher) {
@@ -1593,8 +1874,8 @@ function isPluginOwned(cmd) {
1593
1874
  return typeof cmd[MARKER_PLUGIN] === "string";
1594
1875
  }
1595
1876
  function materializeHooks(projectRoot, entries) {
1596
- const settingsPath = resolve9(projectRoot, ".claude", "settings.json");
1597
- const existing = existsSync8(settingsPath) ? safeReadJson(settingsPath) : {};
1877
+ const settingsPath = resolve10(projectRoot, ".claude", "settings.json");
1878
+ const existing = existsSync9(settingsPath) ? safeReadJson(settingsPath) : {};
1598
1879
  const hooks = existing.hooks ?? {};
1599
1880
  for (const event of Object.keys(hooks)) {
1600
1881
  const groups = hooks[event] ?? [];
@@ -1636,56 +1917,56 @@ function materializeHooks(projectRoot, entries) {
1636
1917
  } else {
1637
1918
  delete next.hooks;
1638
1919
  }
1639
- mkdirSync3(dirname6(settingsPath), { recursive: true });
1640
- writeFileSync2(settingsPath, `${JSON.stringify(next, null, 2)}
1920
+ mkdirSync4(dirname7(settingsPath), { recursive: true });
1921
+ writeFileSync3(settingsPath, `${JSON.stringify(next, null, 2)}
1641
1922
  `, "utf-8");
1642
1923
  return settingsPath;
1643
1924
  }
1644
1925
  function safeReadJson(path) {
1645
1926
  try {
1646
- return JSON.parse(readFileSync5(path, "utf-8"));
1927
+ return JSON.parse(readFileSync6(path, "utf-8"));
1647
1928
  } catch {
1648
1929
  return {};
1649
1930
  }
1650
1931
  }
1651
1932
 
1652
1933
  // packages/runtime/src/control-plane/skill-materializer.ts
1653
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
1654
- import { resolve as resolve10 } from "path";
1934
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
1935
+ import { resolve as resolve11 } from "path";
1655
1936
  import { loadSkill } from "@rig/skill-loader";
1656
1937
  var MARKER_FILENAME = ".rig-plugin";
1657
1938
  function skillDirName(id) {
1658
1939
  return id.replace(/[^a-zA-Z0-9._-]+/g, "-");
1659
1940
  }
1660
1941
  async function materializeSkills(projectRoot, entries) {
1661
- const skillsRoot = resolve10(projectRoot, ".pi", "skills");
1662
- if (existsSync9(skillsRoot)) {
1942
+ const skillsRoot = resolve11(projectRoot, ".pi", "skills");
1943
+ if (existsSync10(skillsRoot)) {
1663
1944
  for (const name of readdirSync(skillsRoot)) {
1664
- const dir = resolve10(skillsRoot, name);
1665
- if (existsSync9(resolve10(dir, MARKER_FILENAME))) {
1666
- rmSync2(dir, { recursive: true, force: true });
1945
+ const dir = resolve11(skillsRoot, name);
1946
+ if (existsSync10(resolve11(dir, MARKER_FILENAME))) {
1947
+ rmSync3(dir, { recursive: true, force: true });
1667
1948
  }
1668
1949
  }
1669
1950
  }
1670
1951
  const written = [];
1671
1952
  for (const { pluginName, skill } of entries) {
1672
- const sourcePath = resolve10(projectRoot, skill.path);
1673
- if (!existsSync9(sourcePath)) {
1953
+ const sourcePath = resolve11(projectRoot, skill.path);
1954
+ if (!existsSync10(sourcePath)) {
1674
1955
  console.warn(`[plugin-host] skill "${skill.id}" from plugin "${pluginName}" not materialized: ${sourcePath} does not exist`);
1675
1956
  continue;
1676
1957
  }
1677
1958
  let body;
1678
1959
  try {
1679
1960
  await loadSkill(sourcePath);
1680
- body = readFileSync6(sourcePath, "utf-8");
1961
+ body = readFileSync7(sourcePath, "utf-8");
1681
1962
  } catch (err) {
1682
1963
  console.warn(`[plugin-host] skill "${skill.id}" from plugin "${pluginName}" not materialized: ${err instanceof Error ? err.message : err}`);
1683
1964
  continue;
1684
1965
  }
1685
- const dir = resolve10(skillsRoot, skillDirName(skill.id));
1686
- mkdirSync4(dir, { recursive: true });
1687
- writeFileSync3(resolve10(dir, "SKILL.md"), body, "utf-8");
1688
- writeFileSync3(resolve10(dir, MARKER_FILENAME), `${JSON.stringify({ plugin: pluginName, skillId: skill.id }, null, 2)}
1966
+ const dir = resolve11(skillsRoot, skillDirName(skill.id));
1967
+ mkdirSync5(dir, { recursive: true });
1968
+ writeFileSync4(resolve11(dir, "SKILL.md"), body, "utf-8");
1969
+ writeFileSync4(resolve11(dir, MARKER_FILENAME), `${JSON.stringify({ plugin: pluginName, skillId: skill.id }, null, 2)}
1689
1970
  `, "utf-8");
1690
1971
  written.push({ id: skill.id, pluginName, directory: dir });
1691
1972
  }
@@ -1752,12 +2033,12 @@ async function buildPluginHostContext(projectRoot) {
1752
2033
 
1753
2034
  // packages/runtime/src/control-plane/tasks/source-aware-task-config-source.ts
1754
2035
  import { spawnSync } from "child_process";
1755
- import { existsSync as existsSync11, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync3, writeFileSync as writeFileSync4 } from "fs";
1756
- import { basename as basename4, join as join2, resolve as resolve12 } from "path";
2036
+ import { existsSync as existsSync12, readFileSync as readFileSync9, readdirSync as readdirSync2, statSync as statSync3, writeFileSync as writeFileSync5 } from "fs";
2037
+ import { basename as basename4, join as join2, resolve as resolve13 } from "path";
1757
2038
 
1758
2039
  // packages/runtime/src/control-plane/tasks/legacy-task-config-source.ts
1759
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
1760
- import { resolve as resolve11 } from "path";
2040
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
2041
+ import { resolve as resolve12 } from "path";
1761
2042
 
1762
2043
  // packages/runtime/src/control-plane/tasks/task-record-reader.ts
1763
2044
  async function findTaskById(reader, id) {
@@ -1780,7 +2061,7 @@ class LegacyTaskConfigReadError extends Error {
1780
2061
  }
1781
2062
  }
1782
2063
  function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
1783
- const configPath = options.configPath ?? resolve11(projectRoot, ".rig", "task-config.json");
2064
+ const configPath = options.configPath ?? resolve12(projectRoot, ".rig", "task-config.json");
1784
2065
  const reader = {
1785
2066
  async listTasks() {
1786
2067
  return readLegacyTaskRecords(projectRoot, configPath);
@@ -1791,8 +2072,8 @@ function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
1791
2072
  };
1792
2073
  return reader;
1793
2074
  }
1794
- function readLegacyTaskRecords(projectRoot, configPath = resolve11(projectRoot, ".rig", "task-config.json")) {
1795
- if (!existsSync10(configPath)) {
2075
+ function readLegacyTaskRecords(projectRoot, configPath = resolve12(projectRoot, ".rig", "task-config.json")) {
2076
+ if (!existsSync11(configPath)) {
1796
2077
  return [];
1797
2078
  }
1798
2079
  const rawConfig = readLegacyTaskConfigJson(projectRoot, configPath);
@@ -1800,7 +2081,7 @@ function readLegacyTaskRecords(projectRoot, configPath = resolve11(projectRoot,
1800
2081
  }
1801
2082
  function readLegacyTaskConfigJson(projectRoot, configPath) {
1802
2083
  try {
1803
- const parsed = JSON.parse(readFileSync7(configPath, "utf8"));
2084
+ const parsed = JSON.parse(readFileSync8(configPath, "utf8"));
1804
2085
  if (isPlainRecord(parsed)) {
1805
2086
  return parsed;
1806
2087
  }
@@ -1884,7 +2165,7 @@ function isPlainRecord(candidate) {
1884
2165
  var STATUS_LABELS = new Set(["ready", "blocked", "in-progress", "under-review", "failed", "cancelled"]);
1885
2166
  var FILE_TASK_PATTERN = /\.(task\.)?json$/;
1886
2167
  function createSourceAwareTaskConfigRecordReader(projectRoot, options = {}) {
1887
- const configPath = options.configPath ?? resolve12(projectRoot, ".rig", "task-config.json");
2168
+ const configPath = options.configPath ?? resolve13(projectRoot, ".rig", "task-config.json");
1888
2169
  const legacy = createLegacyTaskConfigRecordReader(projectRoot, { configPath });
1889
2170
  const spawnFn = options.spawn ?? spawnSync;
1890
2171
  const ghBinary = options.ghBinary ?? "gh";
@@ -1950,7 +2231,7 @@ async function readSourceAwareTaskStatus(projectRoot, taskId, options = {}) {
1950
2231
  }
1951
2232
  }
1952
2233
  function updateSourceAwareTaskConfigTask(projectRoot, taskId, update, options = {}) {
1953
- const configPath = options.configPath ?? resolve12(projectRoot, ".rig", "task-config.json");
2234
+ const configPath = options.configPath ?? resolve13(projectRoot, ".rig", "task-config.json");
1954
2235
  const rawEntry = readRawTaskEntry(configPath, taskId);
1955
2236
  if (!rawEntry) {
1956
2237
  const configuredFilesPath = readConfiguredFilesTaskSourcePath(projectRoot);
@@ -2003,10 +2284,10 @@ function readMaterializedTaskMetadata(entry) {
2003
2284
  return metadata;
2004
2285
  }
2005
2286
  function readConfiguredFilesTaskSourcePath(projectRoot) {
2006
- const jsonPath = resolve12(projectRoot, "rig.config.json");
2007
- if (existsSync11(jsonPath)) {
2287
+ const jsonPath = resolve13(projectRoot, "rig.config.json");
2288
+ if (existsSync12(jsonPath)) {
2008
2289
  try {
2009
- const parsed = JSON.parse(readFileSync8(jsonPath, "utf8"));
2290
+ const parsed = JSON.parse(readFileSync9(jsonPath, "utf8"));
2010
2291
  if (isPlainRecord2(parsed) && isPlainRecord2(parsed.taskSource)) {
2011
2292
  const source = parsed.taskSource;
2012
2293
  return source.kind === "files" && typeof source.path === "string" ? source.path : null;
@@ -2015,12 +2296,12 @@ function readConfiguredFilesTaskSourcePath(projectRoot) {
2015
2296
  return null;
2016
2297
  }
2017
2298
  }
2018
- const tsPath = resolve12(projectRoot, "rig.config.ts");
2019
- if (!existsSync11(tsPath)) {
2299
+ const tsPath = resolve13(projectRoot, "rig.config.ts");
2300
+ if (!existsSync12(tsPath)) {
2020
2301
  return null;
2021
2302
  }
2022
2303
  try {
2023
- const source = readFileSync8(tsPath, "utf8");
2304
+ const source = readFileSync9(tsPath, "utf8");
2024
2305
  const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
2025
2306
  const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
2026
2307
  if (kind !== "files") {
@@ -2040,10 +2321,10 @@ function readRawTaskEntry(configPath, taskId) {
2040
2321
  return isPlainRecord2(entry) ? entry : null;
2041
2322
  }
2042
2323
  function readRawTaskConfig(configPath) {
2043
- if (!existsSync11(configPath)) {
2324
+ if (!existsSync12(configPath)) {
2044
2325
  return null;
2045
2326
  }
2046
- const parsed = JSON.parse(readFileSync8(configPath, "utf8"));
2327
+ const parsed = JSON.parse(readFileSync9(configPath, "utf8"));
2047
2328
  return isPlainRecord2(parsed) ? parsed : null;
2048
2329
  }
2049
2330
  function stripLegacyTaskConfigMetadata2(raw) {
@@ -2060,16 +2341,16 @@ function writeLegacyTaskStatus(configPath, taskId, status) {
2060
2341
  return;
2061
2342
  }
2062
2343
  entry.status = status;
2063
- writeFileSync4(configPath, `${JSON.stringify(rawConfig, null, 2)}
2344
+ writeFileSync5(configPath, `${JSON.stringify(rawConfig, null, 2)}
2064
2345
  `, "utf8");
2065
2346
  }
2066
2347
  function updateFileBackedTask(projectRoot, sourcePath, taskId, update) {
2067
- const directory = resolve12(projectRoot, sourcePath);
2348
+ const directory = resolve13(projectRoot, sourcePath);
2068
2349
  const file = findFileBackedTaskFile(directory, taskId);
2069
2350
  if (!file) {
2070
2351
  return false;
2071
2352
  }
2072
- const raw = JSON.parse(readFileSync8(file, "utf8"));
2353
+ const raw = JSON.parse(readFileSync9(file, "utf8"));
2073
2354
  if (!isPlainRecord2(raw)) {
2074
2355
  return false;
2075
2356
  }
@@ -2086,13 +2367,13 @@ function updateFileBackedTask(projectRoot, sourcePath, taskId, update) {
2086
2367
  { body: update.comment, createdAt: new Date().toISOString(), source: "rig" }
2087
2368
  ];
2088
2369
  }
2089
- writeFileSync4(file, `${JSON.stringify(raw, null, 2)}
2370
+ writeFileSync5(file, `${JSON.stringify(raw, null, 2)}
2090
2371
  `, "utf8");
2091
2372
  return true;
2092
2373
  }
2093
2374
  function listFileBackedTasks(projectRoot, sourcePath) {
2094
- const directory = resolve12(projectRoot, sourcePath);
2095
- if (!existsSync11(directory)) {
2375
+ const directory = resolve13(projectRoot, sourcePath);
2376
+ if (!existsSync12(directory)) {
2096
2377
  return [];
2097
2378
  }
2098
2379
  const tasks = [];
@@ -2107,11 +2388,11 @@ function listFileBackedTasks(projectRoot, sourcePath) {
2107
2388
  return tasks;
2108
2389
  }
2109
2390
  function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
2110
- const file = findFileBackedTaskFile(resolve12(projectRoot, sourcePath), taskId);
2391
+ const file = findFileBackedTaskFile(resolve13(projectRoot, sourcePath), taskId);
2111
2392
  if (!file) {
2112
2393
  return null;
2113
2394
  }
2114
- const raw = JSON.parse(readFileSync8(file, "utf8"));
2395
+ const raw = JSON.parse(readFileSync9(file, "utf8"));
2115
2396
  if (!isPlainRecord2(raw)) {
2116
2397
  return null;
2117
2398
  }
@@ -2124,7 +2405,7 @@ function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
2124
2405
  };
2125
2406
  }
2126
2407
  function findFileBackedTaskFile(directory, taskId) {
2127
- if (!existsSync11(directory)) {
2408
+ if (!existsSync12(directory)) {
2128
2409
  return null;
2129
2410
  }
2130
2411
  for (const name of readdirSync2(directory)) {
@@ -2134,7 +2415,7 @@ function findFileBackedTaskFile(directory, taskId) {
2134
2415
  try {
2135
2416
  if (!statSync3(file).isFile())
2136
2417
  continue;
2137
- const raw = JSON.parse(readFileSync8(file, "utf8"));
2418
+ const raw = JSON.parse(readFileSync9(file, "utf8"));
2138
2419
  const inferredId = basename4(file).replace(FILE_TASK_PATTERN, "");
2139
2420
  const id = isPlainRecord2(raw) && typeof raw.id === "string" ? raw.id : inferredId;
2140
2421
  if (id === taskId) {
@@ -2327,457 +2608,177 @@ function assertGhSuccess(args, res) {
2327
2608
  const msg = res.error.message ?? String(res.error);
2328
2609
  throw new Error(`gh CLI not available \u2014 install gh (brew install gh / apt install gh): ${msg}`);
2329
2610
  }
2330
- if (res.status !== 0) {
2331
- throw new Error(`gh ${args.join(" ")} failed (exit ${res.status}): ${res.stderr}`);
2332
- }
2333
- }
2334
- function parseDeps(body) {
2335
- return parseIssueRefs(body, /^depends-on:\s*([^\n]+)/im);
2336
- }
2337
- function parseParents(body) {
2338
- return parseIssueRefs(body, /^parents?:\s*([^\n]+)/im);
2339
- }
2340
- function parseIssueRefs(body, pattern) {
2341
- const match = body.match(pattern);
2342
- if (!match)
2343
- return [];
2344
- return match[1].split(",").map((value) => value.trim()).map((value) => value.replace(/^#/, "").match(/^(\d+)/)?.[1] ?? "").filter((value) => value.length > 0);
2345
- }
2346
- function issueTypeFor(labels) {
2347
- const typed = labels.find((label) => label.startsWith("type:"));
2348
- if (typed)
2349
- return typed.slice("type:".length);
2350
- if (labels.includes("epic"))
2351
- return "epic";
2352
- return "task";
2353
- }
2354
- function isPlainRecord2(candidate) {
2355
- return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
2356
- }
2357
-
2358
- // packages/runtime/src/control-plane/tasks/source-lifecycle.ts
2359
- function hasRunnableTaskSource(source) {
2360
- return Boolean(source && typeof source === "object" && !Array.isArray(source));
2361
- }
2362
- function cleanString(value) {
2363
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
2364
- }
2365
- function taskIdFromSourceIssueId(value) {
2366
- const raw = cleanString(value);
2367
- if (!raw)
2368
- return null;
2369
- const issueNumber = raw.match(/#([^#\s]+)$/)?.[1];
2370
- return issueNumber ?? raw;
2371
- }
2372
- function resolveSourceTaskId(taskId, sourceTask) {
2373
- return cleanString(sourceTask?.id) ?? taskIdFromSourceIssueId(sourceTask?.sourceIssueId) ?? taskIdFromSourceIssueId(sourceTask?.source_issue_id) ?? taskId;
2374
- }
2375
- async function getPluginTask(projectRoot, taskId) {
2376
- const ctx = await buildPluginHostContext(projectRoot);
2377
- const [source] = ctx?.taskSourceRegistry.list() ?? [];
2378
- if (!hasRunnableTaskSource(source)) {
2379
- return ctx ? { configured: false, sourceKind: null, task: null } : null;
2380
- }
2381
- const task = source.get ? await source.get(taskId) ?? null : (await source.list()).find((entry) => entry.id === taskId) ?? null;
2382
- return {
2383
- configured: true,
2384
- sourceKind: source.kind,
2385
- task
2386
- };
2387
- }
2388
- async function readConfiguredTaskSourceTask(projectRoot, taskId) {
2389
- const pluginResult = await getPluginTask(projectRoot, taskId);
2390
- if (pluginResult)
2391
- return pluginResult;
2392
- const task = await createSourceAwareTaskConfigRecordReader(projectRoot).getTask(taskId);
2393
- return {
2394
- configured: false,
2395
- sourceKind: null,
2396
- task
2397
- };
2398
- }
2399
- async function updatePluginTaskSourceTask(projectRoot, taskId, update) {
2400
- const ctx = await buildPluginHostContext(projectRoot);
2401
- const [source] = ctx?.taskSourceRegistry.list() ?? [];
2402
- if (!hasRunnableTaskSource(source)) {
2403
- return ctx ? { taskId, updated: false, source: "none", sourceKind: null, status: null } : null;
2404
- }
2405
- if (source.updateTask) {
2406
- await source.updateTask(taskId, update);
2407
- } else if (update.status && source.updateStatus) {
2408
- await source.updateStatus(taskId, update.status);
2409
- } else {
2410
- return {
2411
- taskId,
2412
- updated: false,
2413
- source: "plugin",
2414
- sourceKind: source.kind,
2415
- status: null
2416
- };
2417
- }
2418
- const status = source.get ? (await source.get(taskId))?.status ?? update.status ?? null : update.status ?? null;
2419
- return {
2420
- taskId,
2421
- updated: true,
2422
- source: "plugin",
2423
- sourceKind: source.kind,
2424
- status
2425
- };
2426
- }
2427
- async function updateConfiguredTaskSourceTask(projectRoot, input) {
2428
- const taskId = resolveSourceTaskId(input.taskId, input.sourceTask);
2429
- let pluginResult = null;
2430
- try {
2431
- pluginResult = await updatePluginTaskSourceTask(projectRoot, taskId, input.update);
2432
- } catch (error) {
2433
- const fallbackUpdated = updateSourceAwareTaskConfigTask(projectRoot, taskId, input.update, {
2434
- allowLocalTaskConfigStatusFallback: false
2435
- });
2436
- if (!fallbackUpdated) {
2437
- throw error;
2438
- }
2439
- return {
2440
- taskId,
2441
- updated: true,
2442
- source: "compat",
2443
- sourceKind: null,
2444
- status: await readSourceAwareTaskStatus(projectRoot, taskId)
2445
- };
2446
- }
2447
- if (pluginResult) {
2448
- return pluginResult;
2449
- }
2450
- const updated = updateSourceAwareTaskConfigTask(projectRoot, taskId, input.update);
2451
- return {
2452
- taskId,
2453
- updated,
2454
- source: updated ? "compat" : "none",
2455
- sourceKind: null,
2456
- status: await readSourceAwareTaskStatus(projectRoot, taskId)
2457
- };
2458
- }
2459
- function buildTaskRunLifecycleComment(input) {
2460
- const lines = [
2461
- "<!-- rig:status-comment -->",
2462
- `### Rig status: ${input.status}`,
2463
- "",
2464
- input.summary,
2465
- "",
2466
- `- Run: ${input.runId}`,
2467
- `- Status: ${input.status}`
2468
- ];
2469
- if (input.errorText?.trim()) {
2470
- lines.push(`- Error: ${input.errorText.trim()}`);
2471
- }
2472
- if (input.runtimeWorkspace?.trim()) {
2473
- lines.push(`- Runtime workspace: ${input.runtimeWorkspace.trim()}`);
2474
- }
2475
- if (input.logsDir?.trim()) {
2476
- lines.push(`- Logs: ${input.logsDir.trim()}`);
2477
- }
2478
- if (input.sessionDir?.trim()) {
2479
- lines.push(`- Session: ${input.sessionDir.trim()}`);
2480
- }
2481
- return lines.join(`
2482
- `);
2483
- }
2484
-
2485
- // packages/runtime/src/control-plane/native/task-state.ts
2486
- import { existsSync as existsSync13, readFileSync as readFileSync10, readdirSync as readdirSync3, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
2487
- import { basename as basename5, resolve as resolve14 } from "path";
2488
-
2489
- // packages/runtime/src/control-plane/state-sync/types.ts
2490
- var CANONICAL_TASK_LIFECYCLE_STATUSES = new Set([
2491
- "draft",
2492
- "open",
2493
- "ready",
2494
- "queued",
2495
- "in_progress",
2496
- "under_review",
2497
- "blocked",
2498
- "completed",
2499
- "cancelled"
2500
- ]);
2501
- // packages/runtime/src/control-plane/native/git-native.ts
2502
- import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync9, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
2503
- import { tmpdir as tmpdir4 } from "os";
2504
- import { dirname as dirname7, isAbsolute, resolve as resolve13 } from "path";
2505
- import { createHash } from "crypto";
2506
- var sharedGitNativeOutputDir = resolve13(tmpdir4(), "rig-native");
2507
- var sharedGitNativeOutputPath = resolve13(sharedGitNativeOutputDir, `rig-git-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
2508
- var trackerCommandUsageProbe = "usage: rig-git fetch-ref <repo-path> <remote> <branch>";
2509
- function temporaryGitBinaryOutputPath(outputPath) {
2510
- const suffix2 = process.platform === "win32" ? ".exe" : "";
2511
- return resolve13(dirname7(outputPath), `.rig-git-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}${suffix2}`);
2512
- }
2513
- function publishGitBinary(tempOutputPath, outputPath) {
2514
- try {
2515
- renameSync2(tempOutputPath, outputPath);
2516
- } catch (error) {
2517
- if (process.platform === "win32" && existsSync12(outputPath)) {
2518
- rmSync3(outputPath, { force: true });
2519
- renameSync2(tempOutputPath, outputPath);
2520
- return;
2521
- }
2522
- throw error;
2523
- }
2524
- }
2525
- function runtimeRigGitFileName() {
2526
- return `rig-git${process.platform === "win32" ? ".exe" : ""}`;
2527
- }
2528
- function rigGitSourceCandidates() {
2529
- const execDir = process.execPath?.trim() ? dirname7(process.execPath.trim()) : "";
2530
- const cwd = process.cwd()?.trim() || "";
2531
- const projectRoot = process.env.PROJECT_RIG_ROOT?.trim() || "";
2532
- const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || "";
2533
- const moduleRelativeSource = resolve13(import.meta.dir, "../../../native/rig-git.zig");
2534
- return [...new Set([
2535
- process.env.RIG_NATIVE_GIT_SOURCE?.trim() || "",
2536
- moduleRelativeSource,
2537
- projectRoot ? resolve13(projectRoot, "packages/runtime/native/rig-git.zig") : "",
2538
- hostProjectRoot ? resolve13(hostProjectRoot, "packages/runtime/native/rig-git.zig") : "",
2539
- cwd ? resolve13(cwd, "packages/runtime/native/rig-git.zig") : "",
2540
- execDir ? resolve13(execDir, "..", "..", "packages/runtime/native/rig-git.zig") : "",
2541
- execDir ? resolve13(execDir, "..", "native", "rig-git.zig") : ""
2542
- ].filter(Boolean))];
2543
- }
2544
- function nativePackageBinaryCandidates(fromDir, fileName) {
2545
- const candidates = [];
2546
- let cursor = resolve13(fromDir);
2547
- for (let index = 0;index < 8; index += 1) {
2548
- candidates.push(resolve13(cursor, "native", `${process.platform}-${process.arch}`, fileName), resolve13(cursor, "native", `${process.platform}-${process.arch}`, "bin", fileName), resolve13(cursor, "native", fileName), resolve13(cursor, "native", "bin", fileName));
2549
- const parent = dirname7(cursor);
2550
- if (parent === cursor)
2551
- break;
2552
- cursor = parent;
2553
- }
2554
- return candidates;
2555
- }
2556
- function rigGitBinaryCandidates() {
2557
- const execDir = process.execPath?.trim() ? dirname7(process.execPath.trim()) : "";
2558
- const fileName = runtimeRigGitFileName();
2559
- const explicit = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
2560
- return [...new Set([
2561
- explicit,
2562
- ...nativePackageBinaryCandidates(import.meta.dir, fileName),
2563
- execDir ? resolve13(execDir, fileName) : "",
2564
- execDir ? resolve13(execDir, "..", fileName) : "",
2565
- execDir ? resolve13(execDir, "..", "bin", fileName) : "",
2566
- sharedGitNativeOutputPath
2567
- ].filter(Boolean))];
2568
- }
2569
- function resolveGitSourcePath() {
2570
- for (const candidate of rigGitSourceCandidates()) {
2571
- if (candidate && existsSync12(candidate)) {
2572
- return candidate;
2573
- }
2574
- }
2575
- return null;
2576
- }
2577
- function resolveGitBinaryPath() {
2578
- if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
2579
- return null;
2580
- }
2581
- for (const candidate of rigGitBinaryCandidates()) {
2582
- if (candidate && existsSync12(candidate)) {
2583
- return candidate;
2584
- }
2585
- }
2586
- return null;
2587
- }
2588
- function preferredGitBinaryOutputPath() {
2589
- const explicit = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
2590
- return explicit || sharedGitNativeOutputPath;
2591
- }
2592
- function binarySupportsTrackerCommandsSync(binaryPath) {
2593
- try {
2594
- const probe = Bun.spawnSync([binaryPath, "fetch-ref", "."], {
2595
- stdout: "pipe",
2596
- stderr: "pipe"
2597
- });
2598
- const stdout = probe.stdout.toString().trim();
2599
- const stderr = probe.stderr.toString().trim();
2600
- if (stdout.includes('"error":"unknown command"')) {
2601
- return false;
2602
- }
2603
- return probe.exitCode === 2 && stderr.includes(trackerCommandUsageProbe);
2604
- } catch {
2605
- return false;
2606
- }
2607
- }
2608
- function nativeBuildManifestPath(outputPath) {
2609
- return `${outputPath}.build-manifest.json`;
2610
- }
2611
- function hasMatchingNativeBuildManifestSync(manifestPath, buildKey) {
2612
- if (!existsSync12(manifestPath)) {
2613
- return false;
2614
- }
2615
- try {
2616
- const manifest = JSON.parse(readFileSync9(manifestPath, "utf8"));
2617
- return manifest.version === 1 && manifest.buildKey === buildKey;
2618
- } catch {
2619
- return false;
2620
- }
2621
- }
2622
- function sha256FileSync(path) {
2623
- return createHash("sha256").update(readFileSync9(path)).digest("hex");
2624
- }
2625
- function ensureRigGitBinaryPathSync(outputPath = preferredGitBinaryOutputPath()) {
2626
- if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
2627
- throw new Error("Zig native git is disabled via RIG_DISABLE_ZIG_NATIVE=1");
2628
- }
2629
- const sourcePath = resolveGitSourcePath();
2630
- if (!sourcePath) {
2631
- const binaryPath = resolveGitBinaryPath();
2632
- if (binaryPath) {
2633
- return binaryPath;
2634
- }
2635
- throw new Error("rig-git.zig source file not found.");
2636
- }
2637
- const zigBinary = Bun.which("zig");
2638
- if (!zigBinary) {
2639
- throw new Error("zig is required to build native Rig git tools.");
2640
- }
2641
- mkdirSync5(dirname7(outputPath), { recursive: true });
2642
- const sourceDigest = sha256FileSync(sourcePath);
2643
- const buildKey = JSON.stringify({
2644
- version: 1,
2645
- zigBinary,
2646
- platform: process.platform,
2647
- arch: process.arch,
2648
- sourcePath,
2649
- sourceDigest
2650
- });
2651
- const manifestPath = nativeBuildManifestPath(outputPath);
2652
- const needsBuild = !existsSync12(outputPath) || !hasMatchingNativeBuildManifestSync(manifestPath, buildKey) || !binarySupportsTrackerCommandsSync(outputPath);
2653
- if (!needsBuild) {
2654
- chmodSync(outputPath, 493);
2655
- return outputPath;
2656
- }
2657
- const tempOutputPath = temporaryGitBinaryOutputPath(outputPath);
2658
- const build = Bun.spawnSync([
2659
- zigBinary,
2660
- "build-exe",
2661
- sourcePath,
2662
- "-O",
2663
- "ReleaseFast",
2664
- `-femit-bin=${tempOutputPath}`
2665
- ], {
2666
- cwd: dirname7(sourcePath),
2667
- stdout: "pipe",
2668
- stderr: "pipe"
2669
- });
2670
- if (build.exitCode !== 0 || !existsSync12(tempOutputPath)) {
2671
- const stderr = build.stderr.toString().trim();
2672
- const stdout = build.stdout.toString().trim();
2673
- const details = [stderr, stdout].filter(Boolean).join(`
2674
- `);
2675
- throw new Error(`Failed to build native Rig git tools: ${details || `zig exited with code ${build.exitCode}`}`);
2676
- }
2677
- chmodSync(tempOutputPath, 493);
2678
- if (existsSync12(outputPath) && hasMatchingNativeBuildManifestSync(manifestPath, buildKey)) {
2679
- rmSync3(tempOutputPath, { force: true });
2680
- chmodSync(outputPath, 493);
2681
- return outputPath;
2611
+ if (res.status !== 0) {
2612
+ throw new Error(`gh ${args.join(" ")} failed (exit ${res.status}): ${res.stderr}`);
2682
2613
  }
2683
- publishGitBinary(tempOutputPath, outputPath);
2684
- if (!binarySupportsTrackerCommandsSync(outputPath)) {
2685
- rmSync3(outputPath, { force: true });
2686
- throw new Error("Failed to build native Rig git tools: tracker command probe failed");
2614
+ }
2615
+ function parseDeps(body) {
2616
+ return parseIssueRefs(body, /^depends-on:\s*([^\n]+)/im);
2617
+ }
2618
+ function parseParents(body) {
2619
+ return parseIssueRefs(body, /^parents?:\s*([^\n]+)/im);
2620
+ }
2621
+ function parseIssueRefs(body, pattern) {
2622
+ const match = body.match(pattern);
2623
+ if (!match)
2624
+ return [];
2625
+ return match[1].split(",").map((value) => value.trim()).map((value) => value.replace(/^#/, "").match(/^(\d+)/)?.[1] ?? "").filter((value) => value.length > 0);
2626
+ }
2627
+ function issueTypeFor(labels) {
2628
+ const typed = labels.find((label) => label.startsWith("type:"));
2629
+ if (typed)
2630
+ return typed.slice("type:".length);
2631
+ if (labels.includes("epic"))
2632
+ return "epic";
2633
+ return "task";
2634
+ }
2635
+ function isPlainRecord2(candidate) {
2636
+ return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
2637
+ }
2638
+
2639
+ // packages/runtime/src/control-plane/tasks/source-lifecycle.ts
2640
+ function hasRunnableTaskSource(source) {
2641
+ return Boolean(source && typeof source === "object" && !Array.isArray(source));
2642
+ }
2643
+ function cleanString(value) {
2644
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
2645
+ }
2646
+ function taskIdFromSourceIssueId(value) {
2647
+ const raw = cleanString(value);
2648
+ if (!raw)
2649
+ return null;
2650
+ const issueNumber = raw.match(/#([^#\s]+)$/)?.[1];
2651
+ return issueNumber ?? raw;
2652
+ }
2653
+ function resolveSourceTaskId(taskId, sourceTask) {
2654
+ return cleanString(sourceTask?.id) ?? taskIdFromSourceIssueId(sourceTask?.sourceIssueId) ?? taskIdFromSourceIssueId(sourceTask?.source_issue_id) ?? taskId;
2655
+ }
2656
+ async function getPluginTask(projectRoot, taskId) {
2657
+ const ctx = await buildPluginHostContext(projectRoot);
2658
+ const [source] = ctx?.taskSourceRegistry.list() ?? [];
2659
+ if (!hasRunnableTaskSource(source)) {
2660
+ return ctx ? { configured: false, sourceKind: null, task: null } : null;
2687
2661
  }
2688
- writeFileSync5(manifestPath, `${JSON.stringify({ version: 1, buildKey }, null, 2)}
2689
- `, "utf8");
2690
- return outputPath;
2662
+ const task = source.get ? await source.get(taskId) ?? null : (await source.list()).find((entry) => entry.id === taskId) ?? null;
2663
+ return {
2664
+ configured: true,
2665
+ sourceKind: source.kind,
2666
+ task
2667
+ };
2691
2668
  }
2692
- function runGitNative(command, args) {
2693
- if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
2694
- return { ok: false, error: "rig-git native disabled" };
2669
+ async function readConfiguredTaskSourceTask(projectRoot, taskId) {
2670
+ const pluginResult = await getPluginTask(projectRoot, taskId);
2671
+ if (pluginResult)
2672
+ return pluginResult;
2673
+ const task = await createSourceAwareTaskConfigRecordReader(projectRoot).getTask(taskId);
2674
+ return {
2675
+ configured: false,
2676
+ sourceKind: null,
2677
+ task
2678
+ };
2679
+ }
2680
+ async function updatePluginTaskSourceTask(projectRoot, taskId, update) {
2681
+ const ctx = await buildPluginHostContext(projectRoot);
2682
+ const [source] = ctx?.taskSourceRegistry.list() ?? [];
2683
+ if (!hasRunnableTaskSource(source)) {
2684
+ return ctx ? { taskId, updated: false, source: "none", sourceKind: null, status: null } : null;
2695
2685
  }
2696
- const trackerCommand = command === "fetch-ref" || command === "read-blob-at-ref" || command === "write-tree-commit" || command === "push-ref-with-lease";
2697
- let binaryPath = null;
2698
- if (trackerCommand) {
2699
- try {
2700
- binaryPath = ensureRigGitBinaryPathSync(preferredGitBinaryOutputPath());
2701
- } catch (error) {
2702
- const message = error instanceof Error ? error.message : String(error);
2703
- if (message.includes("rig-git.zig source file not found")) {
2704
- return { ok: false, error: "rig-git binary not found" };
2705
- }
2706
- return { ok: false, error: message };
2707
- }
2686
+ if (source.updateTask) {
2687
+ await source.updateTask(taskId, update);
2688
+ } else if (update.status && source.updateStatus) {
2689
+ await source.updateStatus(taskId, update.status);
2708
2690
  } else {
2709
- const explicitBinaryPath = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
2710
- binaryPath = explicitBinaryPath && existsSync12(explicitBinaryPath) ? explicitBinaryPath : !explicitBinaryPath ? resolveGitBinaryPath() : null;
2711
- if (!binaryPath) {
2712
- try {
2713
- binaryPath = ensureRigGitBinaryPathSync(preferredGitBinaryOutputPath());
2714
- } catch (error) {
2715
- const message = error instanceof Error ? error.message : String(error);
2716
- if (message.includes("rig-git.zig source file not found")) {
2717
- return { ok: false, error: "rig-git binary not found" };
2718
- }
2719
- return { ok: false, error: message };
2720
- }
2721
- }
2691
+ return {
2692
+ taskId,
2693
+ updated: false,
2694
+ source: "plugin",
2695
+ sourceKind: source.kind,
2696
+ status: null
2697
+ };
2722
2698
  }
2699
+ const status = source.get ? (await source.get(taskId))?.status ?? update.status ?? null : update.status ?? null;
2700
+ return {
2701
+ taskId,
2702
+ updated: true,
2703
+ source: "plugin",
2704
+ sourceKind: source.kind,
2705
+ status
2706
+ };
2707
+ }
2708
+ async function updateConfiguredTaskSourceTask(projectRoot, input) {
2709
+ const taskId = resolveSourceTaskId(input.taskId, input.sourceTask);
2710
+ let pluginResult = null;
2723
2711
  try {
2724
- const proc = Bun.spawnSync([binaryPath, command, ...args], {
2725
- stdout: "pipe",
2726
- stderr: "pipe",
2727
- env: process.env
2712
+ pluginResult = await updatePluginTaskSourceTask(projectRoot, taskId, input.update);
2713
+ } catch (error) {
2714
+ const fallbackUpdated = updateSourceAwareTaskConfigTask(projectRoot, taskId, input.update, {
2715
+ allowLocalTaskConfigStatusFallback: false
2728
2716
  });
2729
- if (proc.exitCode !== 0) {
2730
- const stdoutText = proc.stdout.toString().trim();
2731
- if (stdoutText) {
2732
- try {
2733
- const parsed = JSON.parse(stdoutText);
2734
- if (!parsed.ok) {
2735
- return parsed;
2736
- }
2737
- } catch {}
2738
- }
2739
- const errText = proc.stderr.toString().trim() || `exit code ${proc.exitCode}`;
2740
- return { ok: false, error: errText };
2717
+ if (!fallbackUpdated) {
2718
+ throw error;
2741
2719
  }
2742
- const output = proc.stdout.toString().trim();
2743
- return JSON.parse(output);
2744
- } catch (err) {
2745
- return { ok: false, error: String(err) };
2720
+ return {
2721
+ taskId,
2722
+ updated: true,
2723
+ source: "compat",
2724
+ sourceKind: null,
2725
+ status: await readSourceAwareTaskStatus(projectRoot, taskId)
2726
+ };
2746
2727
  }
2747
- }
2748
- function nativeBranchName(repoPath) {
2749
- const result = runGitNative("branch-name", [repoPath]);
2750
- if (!result.ok)
2751
- return null;
2752
- if ("value" in result && typeof result.value === "string")
2753
- return result.value;
2754
- return null;
2755
- }
2756
- function nativeChangeCount(repoPath) {
2757
- const result = runGitNative("change-count", [repoPath]);
2758
- if (!result.ok)
2759
- return null;
2760
- if ("count" in result && typeof result.count === "number")
2761
- return result.count;
2762
- return null;
2763
- }
2764
- function nativePendingFiles(repoPath) {
2765
- const result = runGitNative("pending-files", [repoPath]);
2766
- if (!result.ok)
2767
- return null;
2768
- if ("files" in result && Array.isArray(result.files)) {
2769
- return result.files.map((f) => ({ path: f.path, status: f.status }));
2728
+ if (pluginResult) {
2729
+ return pluginResult;
2770
2730
  }
2771
- return null;
2731
+ const updated = updateSourceAwareTaskConfigTask(projectRoot, taskId, input.update);
2732
+ return {
2733
+ taskId,
2734
+ updated,
2735
+ source: updated ? "compat" : "none",
2736
+ sourceKind: null,
2737
+ status: await readSourceAwareTaskStatus(projectRoot, taskId)
2738
+ };
2772
2739
  }
2773
- function nativeFileHasChanges(repoPath, filePath) {
2774
- const result = runGitNative("file-has-changes", [repoPath, filePath]);
2775
- if (!result.ok)
2776
- return null;
2777
- if ("has_changes" in result && typeof result.has_changes === "boolean")
2778
- return result.has_changes;
2779
- return null;
2740
+ function buildTaskRunLifecycleComment(input) {
2741
+ const lines = [
2742
+ "<!-- rig:status-comment -->",
2743
+ `### Rig status: ${input.status}`,
2744
+ "",
2745
+ input.summary,
2746
+ "",
2747
+ `- Run: ${input.runId}`,
2748
+ `- Status: ${input.status}`
2749
+ ];
2750
+ if (input.errorText?.trim()) {
2751
+ lines.push(`- Error: ${input.errorText.trim()}`);
2752
+ }
2753
+ if (input.runtimeWorkspace?.trim()) {
2754
+ lines.push(`- Runtime workspace: ${input.runtimeWorkspace.trim()}`);
2755
+ }
2756
+ if (input.logsDir?.trim()) {
2757
+ lines.push(`- Logs: ${input.logsDir.trim()}`);
2758
+ }
2759
+ if (input.sessionDir?.trim()) {
2760
+ lines.push(`- Session: ${input.sessionDir.trim()}`);
2761
+ }
2762
+ return lines.join(`
2763
+ `);
2780
2764
  }
2765
+
2766
+ // packages/runtime/src/control-plane/native/task-state.ts
2767
+ import { existsSync as existsSync13, readFileSync as readFileSync10, readdirSync as readdirSync3, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
2768
+ import { basename as basename5, resolve as resolve14 } from "path";
2769
+
2770
+ // packages/runtime/src/control-plane/state-sync/types.ts
2771
+ var CANONICAL_TASK_LIFECYCLE_STATUSES = new Set([
2772
+ "draft",
2773
+ "open",
2774
+ "ready",
2775
+ "queued",
2776
+ "in_progress",
2777
+ "under_review",
2778
+ "blocked",
2779
+ "completed",
2780
+ "cancelled"
2781
+ ]);
2781
2782
  // packages/runtime/src/control-plane/state-sync/reconcile.ts
2782
2783
  var STALE_CLAIM_MS = 24 * 60 * 60 * 1000;
2783
2784
  // packages/runtime/src/control-plane/native/task-state.ts
@@ -6904,20 +6905,7 @@ function filterTaskChangedFiles(projectRoot, taskId, files, scoped) {
6904
6905
  if (!taskId) {
6905
6906
  return unique(uniqueFiles).sort();
6906
6907
  }
6907
- const filteredFiles = unique(uniqueFiles).filter((file) => !isGeneratedTaskChangePath(taskId, file));
6908
- if (!scoped) {
6909
- return filteredFiles.sort();
6910
- }
6911
- const paths = resolveHarnessPaths(projectRoot);
6912
- const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
6913
- const scopes = readTaskConfigForInvocation(projectRoot)[taskId]?.scope || [];
6914
- if (scopes.length === 0) {
6915
- return [];
6916
- }
6917
- return filteredFiles.filter((file) => {
6918
- const normalized = normalizePathToScope(projectRoot, monorepoRoot || paths.monorepoRoot, file);
6919
- return scopeMatches(file, scopes) || scopeMatches(normalized, scopes);
6920
- }).sort();
6908
+ return unique(uniqueFiles).filter((file) => !isGeneratedTaskChangePath(taskId, file)).sort();
6921
6909
  }
6922
6910
  function resolveTaskMonorepoRoot(projectRoot) {
6923
6911
  const runtimeWorkspace = loadRuntimeContextFromEnv()?.workspaceDir || process.env.RIG_TASK_WORKSPACE?.trim();
@@ -6968,6 +6956,17 @@ function resolveMonorepoBaseCommit(projectRoot, repo) {
6968
6956
  }
6969
6957
  function collectWorkingTreeFiles(projectRoot, repo, baseline) {
6970
6958
  const files = new Set;
6959
+ const nativeFiles = nativePendingFiles(repo);
6960
+ if (nativeFiles !== null) {
6961
+ for (const entry of nativeFiles) {
6962
+ const normalized = normalizeChangedFilePath(entry.path);
6963
+ if (!normalized || baseline.has(normalized)) {
6964
+ continue;
6965
+ }
6966
+ files.add(normalized);
6967
+ }
6968
+ return [...files].sort();
6969
+ }
6971
6970
  for (const args of [
6972
6971
  ["diff", "--name-only"],
6973
6972
  ["diff", "--cached", "--name-only"],