@h-rig/bundle-default-lifecycle 0.0.6-alpha.157 → 0.0.6-alpha.159
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/src/cli.d.ts +1 -7
- package/dist/src/cli.js +5 -2
- package/dist/src/control-plane/completion-verification.js +1591 -118
- package/dist/src/control-plane/hooks/inject-context.d.ts +2 -0
- package/dist/src/control-plane/hooks/inject-context.js +175 -0
- package/dist/src/control-plane/hooks/shared.d.ts +11 -0
- package/dist/src/control-plane/hooks/shared.js +44 -0
- package/dist/src/control-plane/hooks/submodule-branch.d.ts +2 -0
- package/dist/src/control-plane/hooks/submodule-branch.js +432 -0
- package/dist/src/control-plane/hooks/task-runtime-start.d.ts +2 -0
- package/dist/src/control-plane/hooks/task-runtime-start.js +429 -0
- package/dist/src/control-plane/materialize-task-config.d.ts +29 -0
- package/dist/src/control-plane/materialize-task-config.js +95 -0
- package/dist/src/control-plane/native/git-ops.d.ts +67 -0
- package/dist/src/control-plane/native/git-ops.js +1390 -0
- package/dist/src/control-plane/policy.d.ts +3 -0
- package/dist/src/control-plane/policy.js +226 -0
- package/dist/src/control-plane/pr-automation.d.ts +2 -0
- package/dist/src/control-plane/pr-automation.js +26 -16
- package/dist/src/control-plane/pr-merge-gate-cap.d.ts +10 -0
- package/dist/src/control-plane/pr-merge-gate-cap.js +13 -0
- package/dist/src/control-plane/task-data.d.ts +13 -0
- package/dist/src/control-plane/task-data.js +12 -0
- package/dist/src/control-plane/task-verify.js +131 -59
- package/dist/src/control-plane/verifier.d.ts +1 -3
- package/dist/src/control-plane/verifier.js +133 -57
- package/dist/src/defaultPipeline.d.ts +1 -1
- package/dist/src/defaultPipeline.js +5 -2
- package/dist/src/index.d.ts +0 -2
- package/dist/src/index.js +1908 -290
- package/dist/src/native/closeout-runners.js +22 -2
- package/dist/src/native/github-auth-env.d.ts +2 -0
- package/dist/src/native/github-auth-env.js +25 -0
- package/dist/src/native/host-git.d.ts +6 -0
- package/dist/src/native/host-git.js +62 -0
- package/dist/src/native/in-process-closeout.d.ts +1 -3
- package/dist/src/native/in-process-closeout.js +0 -794
- package/dist/src/pipelineCloseout.js +1905 -185
- package/dist/src/plugin.js +2843 -145
- package/dist/src/stages/auto-merge.js +28 -16
- package/dist/src/stages/commit.js +28 -16
- package/dist/src/stages/isolation.d.ts +1 -1
- package/dist/src/stages/isolation.js +5 -3
- package/dist/src/stages/merge-gate.js +35 -3
- package/dist/src/stages/open-pr.js +28 -16
- package/dist/src/stages/push.js +28 -16
- package/dist/src/stages/source-closeout.js +28 -16
- package/package.json +29 -16
- package/dist/src/branch-naming.d.ts +0 -15
- package/dist/src/branch-naming.js +0 -33
- package/dist/src/closeoutEquivalence.d.ts +0 -37
- package/dist/src/closeoutEquivalence.js +0 -78
- package/dist/src/closeoutShadowHarness.d.ts +0 -27
- package/dist/src/closeoutShadowHarness.js +0 -29
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// packages/bundle-default-lifecycle/src/control-plane/hooks/inject-context.ts
|
|
5
|
+
import { existsSync, readFileSync } from "fs";
|
|
6
|
+
import { resolve } from "path";
|
|
7
|
+
import { resolveProjectRoot } from "@rig/hook-kit";
|
|
8
|
+
import { MEMORY, REPO_OPERATIONS_CAPABILITY, RUNTIME_INSTRUCTION } from "@rig/contracts";
|
|
9
|
+
import { defineCapability as defineCapability2 } from "@rig/core/capability";
|
|
10
|
+
import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
11
|
+
import { showProfile, showReviewProfile } from "@rig/core/profile-ops";
|
|
12
|
+
|
|
13
|
+
// packages/bundle-default-lifecycle/src/control-plane/task-data.ts
|
|
14
|
+
import { TASK_DATA_SERVICE_CAPABILITY } from "@rig/contracts";
|
|
15
|
+
import { defineCapability } from "@rig/core/capability";
|
|
16
|
+
import { requireInstalledCapability } from "@rig/core/capability-loaders";
|
|
17
|
+
var TaskDataCap = defineCapability(TASK_DATA_SERVICE_CAPABILITY);
|
|
18
|
+
function taskData() {
|
|
19
|
+
return requireInstalledCapability(TaskDataCap, "task-data capability unavailable: load @rig/task-sources-plugin (default bundle) before running the lifecycle.");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// packages/bundle-default-lifecycle/src/control-plane/hooks/inject-context.ts
|
|
23
|
+
import { runCapture } from "@rig/core/exec";
|
|
24
|
+
import { resolveHarnessPaths } from "@rig/core/harness-paths";
|
|
25
|
+
import { loadRuntimeContextFromEnv } from "@rig/core/runtime-context";
|
|
26
|
+
var RepoOperationsCap = defineCapability2(REPO_OPERATIONS_CAPABILITY);
|
|
27
|
+
function defaultMemoryQuery(taskId, runtimeContext) {
|
|
28
|
+
const role = runtimeContext?.role?.trim();
|
|
29
|
+
const scopeTerms = (runtimeContext?.scopes ?? []).flatMap((scope) => scope.split("/").slice(-3)).map((term) => term.trim()).filter(Boolean);
|
|
30
|
+
const validationTerms = (runtimeContext?.validation ?? []).map((command) => command.split(/\s+/)[0]?.trim() ?? "").filter(Boolean);
|
|
31
|
+
const parts = [...new Set([role, ...scopeTerms, ...validationTerms].filter(Boolean))];
|
|
32
|
+
return parts.join(" ").trim() || taskId;
|
|
33
|
+
}
|
|
34
|
+
async function printSharedMemorySection(projectRoot, taskId) {
|
|
35
|
+
const runtimeContext = loadRuntimeContextFromEnv();
|
|
36
|
+
const memory = runtimeContext?.memory;
|
|
37
|
+
if (!memory?.hydratedPath || !existsSync(memory.hydratedPath)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const memoryService = await loadCapabilityForRoot(projectRoot, defineCapability2(MEMORY));
|
|
41
|
+
if (!memoryService) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const db = await memoryService.openMemoryDb(memory.hydratedPath);
|
|
45
|
+
try {
|
|
46
|
+
const results = await memoryService.queryRelevantMemory(db, {
|
|
47
|
+
query: defaultMemoryQuery(taskId, runtimeContext),
|
|
48
|
+
limit: memory.retrieval.topK,
|
|
49
|
+
retrieval: memory.retrieval
|
|
50
|
+
});
|
|
51
|
+
if (results.length === 0) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
console.log(`
|
|
55
|
+
=== Shared Memory ===
|
|
56
|
+
`);
|
|
57
|
+
console.log(memoryService.formatMemoryQueryResults(results));
|
|
58
|
+
} finally {
|
|
59
|
+
await db.close();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function main() {
|
|
63
|
+
const projectRoot = resolveProjectRoot();
|
|
64
|
+
const taskId = taskData().currentTaskId(projectRoot);
|
|
65
|
+
if (!taskId) {
|
|
66
|
+
console.log("No active task in Rig session. Start a run with: rig run start-serial");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
console.log(`=== Task Assignment: ${taskId} ===
|
|
70
|
+
`);
|
|
71
|
+
await taskData().taskInfo(projectRoot, taskId);
|
|
72
|
+
await printSharedMemorySection(projectRoot, taskId);
|
|
73
|
+
const depIds = taskData().taskDependencyIds(projectRoot, taskId);
|
|
74
|
+
if (depIds.length > 0) {
|
|
75
|
+
console.log(`
|
|
76
|
+
=== Completed Dependency Artifacts ===
|
|
77
|
+
`);
|
|
78
|
+
await taskData().taskDeps(projectRoot, taskId);
|
|
79
|
+
}
|
|
80
|
+
console.log(`
|
|
81
|
+
=== Repo Pin Verification ===
|
|
82
|
+
`);
|
|
83
|
+
const repoOperations = await loadCapabilityForRoot(projectRoot, RepoOperationsCap);
|
|
84
|
+
const pins = repoOperations ? repoOperations.repoDiscover(projectRoot, taskId) : {};
|
|
85
|
+
if (Object.keys(pins).length === 0) {
|
|
86
|
+
console.log("(no explicit/discovered task pins)");
|
|
87
|
+
} else {
|
|
88
|
+
for (const [key, expected] of Object.entries(pins)) {
|
|
89
|
+
const repoPath = key.startsWith("/") ? key : resolve(projectRoot, key);
|
|
90
|
+
let current = "missing";
|
|
91
|
+
if (existsSync(resolve(repoPath, ".git"))) {
|
|
92
|
+
current = runCapture(["git", "-C", repoPath, "rev-parse", "HEAD"], projectRoot).stdout.trim() || "unknown";
|
|
93
|
+
}
|
|
94
|
+
console.log(`${current === expected ? "OK " : "WARN"} ${key} expected ${expected} got ${current}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const failedPath = resolve(resolveHarnessPaths(projectRoot).stateDir, "failed_approaches.md");
|
|
98
|
+
if (existsSync(failedPath)) {
|
|
99
|
+
const lines = readFileSync(failedPath, "utf-8").split(/\r?\n/);
|
|
100
|
+
let printing = false;
|
|
101
|
+
const failureLines = [];
|
|
102
|
+
for (const line of lines) {
|
|
103
|
+
if (line.startsWith("## ")) {
|
|
104
|
+
if (printing) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
printing = line.includes(taskId);
|
|
108
|
+
}
|
|
109
|
+
if (printing) {
|
|
110
|
+
failureLines.push(line);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (failureLines.length > 0) {
|
|
114
|
+
console.log(`
|
|
115
|
+
=== Failed Approaches ===
|
|
116
|
+
`);
|
|
117
|
+
for (const line of failureLines) {
|
|
118
|
+
console.log(line);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
console.log(`
|
|
123
|
+
=== Agent Runtime ===
|
|
124
|
+
`);
|
|
125
|
+
await showProfile(projectRoot, false);
|
|
126
|
+
console.log(`
|
|
127
|
+
=== AI Review Profile ===
|
|
128
|
+
`);
|
|
129
|
+
await showReviewProfile(projectRoot);
|
|
130
|
+
console.log(`
|
|
131
|
+
=== Agent CLI ===
|
|
132
|
+
`);
|
|
133
|
+
console.log("Run: rig-agent help");
|
|
134
|
+
console.log("");
|
|
135
|
+
console.log("Quick reference:");
|
|
136
|
+
console.log(" rig-agent info \u2014 Your task, role, scope, deps");
|
|
137
|
+
console.log(" rig-agent scope \u2014 Scope globs (--files to expand)");
|
|
138
|
+
console.log(" rig-agent deps \u2014 Dependency decisions & next-actions");
|
|
139
|
+
console.log(" rig-agent project-root \u2014 Absolute task worktree root");
|
|
140
|
+
console.log(" rig-agent monorepo-root \u2014 Absolute monorepo root");
|
|
141
|
+
console.log(" rig-agent validate \u2014 Run validation before completing");
|
|
142
|
+
console.log(" rig-agent verify \u2014 Run verifier (local + optional AI review)");
|
|
143
|
+
console.log("");
|
|
144
|
+
console.log("File tool contract:");
|
|
145
|
+
const instructionService = await loadCapabilityForRoot(projectRoot, defineCapability2(RUNTIME_INSTRUCTION));
|
|
146
|
+
const runtimeProvider = instructionService?.normalizeProvider(process.env.RIG_RUNTIME_ADAPTER) ?? "pi";
|
|
147
|
+
for (const line of instructionService?.buildRuntimeContextLines(runtimeProvider) ?? []) {
|
|
148
|
+
console.log(` ${line}`);
|
|
149
|
+
}
|
|
150
|
+
console.log(`
|
|
151
|
+
=== Working Directory ===
|
|
152
|
+
`);
|
|
153
|
+
const taskWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
154
|
+
if (!taskWorkspace) {
|
|
155
|
+
throw new Error("inject-context requires RIG_TASK_WORKSPACE to point at the active runtime workspace.");
|
|
156
|
+
}
|
|
157
|
+
console.log("IMPORTANT: Your working directory is the task worktree:");
|
|
158
|
+
console.log(` ${taskWorkspace}`);
|
|
159
|
+
console.log("");
|
|
160
|
+
console.log(`Do not use absolute root paths under ${projectRoot}/repos.`);
|
|
161
|
+
console.log("");
|
|
162
|
+
console.log("Use the agent CLI from any directory:");
|
|
163
|
+
console.log(" rig-agent <command>");
|
|
164
|
+
console.log("Scope globs are monorepo-relative inside the monorepo worktree.");
|
|
165
|
+
console.log("");
|
|
166
|
+
console.log("Path discipline:");
|
|
167
|
+
console.log(" Use absolute paths for Read/Grep/Bash after any `cd` command.");
|
|
168
|
+
console.log(" The shell session preserves cwd across commands, so relative repos/... paths can drift.");
|
|
169
|
+
console.log(` Project root: ${taskWorkspace}`);
|
|
170
|
+
console.log(` Monorepo root: ${taskWorkspace}`);
|
|
171
|
+
}
|
|
172
|
+
main().catch((error) => {
|
|
173
|
+
console.error(error);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for individual hook entry points.
|
|
3
|
+
*
|
|
4
|
+
* All exports have been moved to @rig/hook-kit in Phase 5 Task 5.3.
|
|
5
|
+
* This file is now a thin re-export shim for backward compatibility.
|
|
6
|
+
* All existing `import { ... } from "./shared"` consumers continue to work.
|
|
7
|
+
*
|
|
8
|
+
* Direct consumers should migrate to `import { ... } from "@rig/hook-kit"`.
|
|
9
|
+
*/
|
|
10
|
+
export type { HookInput, ParsedHookInput } from "@rig/hook-kit";
|
|
11
|
+
export { BAKED_PROJECT_ROOT, BAKED_TASK_ID, BAKED_STATE_DIR, BAKED_BUN_PATH, BAKED_TASK_CONFIG, BAKED_POLICY_CONTENT, BAKED_TASK_SCOPES, readHookInput, resolveProjectRoot, resolveTaskIdForHook, resolveTaskConfig, resolveTaskScopes, resolvePolicyContent, block, extractToolFilePaths, isTestFilePath, escapeRegExp, resolveBunCli, resolveBunCliInvocation, } from "@rig/hook-kit";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/bundle-default-lifecycle/src/control-plane/hooks/shared.ts
|
|
3
|
+
import {
|
|
4
|
+
BAKED_PROJECT_ROOT,
|
|
5
|
+
BAKED_TASK_ID,
|
|
6
|
+
BAKED_STATE_DIR,
|
|
7
|
+
BAKED_BUN_PATH,
|
|
8
|
+
BAKED_TASK_CONFIG,
|
|
9
|
+
BAKED_POLICY_CONTENT,
|
|
10
|
+
BAKED_TASK_SCOPES,
|
|
11
|
+
readHookInput,
|
|
12
|
+
resolveProjectRoot,
|
|
13
|
+
resolveTaskIdForHook,
|
|
14
|
+
resolveTaskConfig,
|
|
15
|
+
resolveTaskScopes,
|
|
16
|
+
resolvePolicyContent,
|
|
17
|
+
block,
|
|
18
|
+
extractToolFilePaths,
|
|
19
|
+
isTestFilePath,
|
|
20
|
+
escapeRegExp,
|
|
21
|
+
resolveBunCli,
|
|
22
|
+
resolveBunCliInvocation
|
|
23
|
+
} from "@rig/hook-kit";
|
|
24
|
+
export {
|
|
25
|
+
resolveTaskScopes,
|
|
26
|
+
resolveTaskIdForHook,
|
|
27
|
+
resolveTaskConfig,
|
|
28
|
+
resolveProjectRoot,
|
|
29
|
+
resolvePolicyContent,
|
|
30
|
+
resolveBunCliInvocation,
|
|
31
|
+
resolveBunCli,
|
|
32
|
+
readHookInput,
|
|
33
|
+
isTestFilePath,
|
|
34
|
+
extractToolFilePaths,
|
|
35
|
+
escapeRegExp,
|
|
36
|
+
block,
|
|
37
|
+
BAKED_TASK_SCOPES,
|
|
38
|
+
BAKED_TASK_ID,
|
|
39
|
+
BAKED_TASK_CONFIG,
|
|
40
|
+
BAKED_STATE_DIR,
|
|
41
|
+
BAKED_PROJECT_ROOT,
|
|
42
|
+
BAKED_POLICY_CONTENT,
|
|
43
|
+
BAKED_BUN_PATH
|
|
44
|
+
};
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// packages/bundle-default-lifecycle/src/control-plane/hooks/task-runtime-start.ts
|
|
5
|
+
import { existsSync as existsSync4 } from "fs";
|
|
6
|
+
import { resolve as resolve2 } from "path";
|
|
7
|
+
import { resolveProjectRoot } from "@rig/hook-kit";
|
|
8
|
+
import { resolveRuntimeWorkspaceLayout } from "@rig/core/layout";
|
|
9
|
+
|
|
10
|
+
// packages/bundle-default-lifecycle/src/control-plane/native/git-ops.ts
|
|
11
|
+
import { existsSync as existsSync3, lstatSync, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
|
|
12
|
+
import { dirname, isAbsolute, resolve } from "path";
|
|
13
|
+
import { loadDotEnvSecrets, resolveRuntimeSecrets } from "@rig/core/baked-secrets";
|
|
14
|
+
import { loadRuntimeContext, loadRuntimeContextFromEnv } from "@rig/core/runtime-context";
|
|
15
|
+
|
|
16
|
+
// packages/bundle-default-lifecycle/src/control-plane/task-data.ts
|
|
17
|
+
import { TASK_DATA_SERVICE_CAPABILITY } from "@rig/contracts";
|
|
18
|
+
import { defineCapability } from "@rig/core/capability";
|
|
19
|
+
import { requireInstalledCapability } from "@rig/core/capability-loaders";
|
|
20
|
+
var TaskDataCap = defineCapability(TASK_DATA_SERVICE_CAPABILITY);
|
|
21
|
+
function taskData() {
|
|
22
|
+
return requireInstalledCapability(TaskDataCap, "task-data capability unavailable: load @rig/task-sources-plugin (default bundle) before running the lifecycle.");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// packages/bundle-default-lifecycle/src/control-plane/native/git-ops.ts
|
|
26
|
+
import { nowIso, runCapture as baseRunCapture } from "@rig/core/exec";
|
|
27
|
+
import { resolveCheckoutRoot as resolveMonorepoRoot } from "@rig/core/checkout-root";
|
|
28
|
+
import { getScopeRules } from "@rig/core/scope-rules";
|
|
29
|
+
|
|
30
|
+
// packages/bundle-default-lifecycle/src/native/github-auth-env.ts
|
|
31
|
+
import { existsSync, readFileSync } from "fs";
|
|
32
|
+
function cleanToken(value) {
|
|
33
|
+
const trimmed = value?.trim() ?? "";
|
|
34
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
35
|
+
}
|
|
36
|
+
function authStateToken(env = process.env) {
|
|
37
|
+
const file = env.RIG_GITHUB_AUTH_STATE_FILE?.trim();
|
|
38
|
+
if (!file || !existsSync(file))
|
|
39
|
+
return null;
|
|
40
|
+
try {
|
|
41
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
42
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// packages/bundle-default-lifecycle/src/control-plane/native/git-ops.ts
|
|
49
|
+
import { safePathSegment } from "@rig/core/safe-identifiers";
|
|
50
|
+
|
|
51
|
+
// packages/bundle-default-lifecycle/src/native/host-git.ts
|
|
52
|
+
import { existsSync as existsSync2 } from "fs";
|
|
53
|
+
function isRuntimeGatewayGitPath(candidate) {
|
|
54
|
+
return /\/\.rig\/bin\/git$/.test(candidate.replace(/\\/g, "/"));
|
|
55
|
+
}
|
|
56
|
+
function resolveHostGitBinary() {
|
|
57
|
+
const candidates = [
|
|
58
|
+
process.env.RIG_GIT_BIN?.trim() || "",
|
|
59
|
+
"/usr/bin/git",
|
|
60
|
+
"/opt/homebrew/bin/git",
|
|
61
|
+
"/usr/local/bin/git"
|
|
62
|
+
];
|
|
63
|
+
const bunResolved = Bun.which("git");
|
|
64
|
+
if (bunResolved && !isRuntimeGatewayGitPath(bunResolved)) {
|
|
65
|
+
candidates.push(bunResolved);
|
|
66
|
+
}
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
if (!candidate || isRuntimeGatewayGitPath(candidate)) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (existsSync2(candidate)) {
|
|
72
|
+
return candidate;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return "git";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// packages/bundle-default-lifecycle/src/control-plane/native/git-ops.ts
|
|
79
|
+
var TASK_ARTIFACT_STAGE_FALLBACK = new Set([
|
|
80
|
+
"changed-files.txt",
|
|
81
|
+
"contract-changes.md",
|
|
82
|
+
"decision-log.md",
|
|
83
|
+
"git-state.txt",
|
|
84
|
+
"next-actions.md",
|
|
85
|
+
"pr-state.json",
|
|
86
|
+
"task-result.json",
|
|
87
|
+
"validation-summary.json"
|
|
88
|
+
]);
|
|
89
|
+
function resolveOptionalMonorepoRoot(projectRoot) {
|
|
90
|
+
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
91
|
+
if (runtimeWorkspace && existsSync3(resolve(runtimeWorkspace, ".git"))) {
|
|
92
|
+
return resolve(runtimeWorkspace);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
return resolveMonorepoRoot(projectRoot);
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function safeCurrentTaskId(projectRoot) {
|
|
101
|
+
try {
|
|
102
|
+
const taskId = taskData().currentTaskId(projectRoot);
|
|
103
|
+
return /^bd-[a-z0-9-]+$/.test(taskId) ? taskId : "";
|
|
104
|
+
} catch {
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function gitCmd(projectRoot, repoRoot, ...args) {
|
|
109
|
+
return [resolveHostGitBinary(), "-C", repoRoot, ...args];
|
|
110
|
+
}
|
|
111
|
+
function gitSyncBranch(projectRoot, taskId, targetRepo = "monorepo") {
|
|
112
|
+
const resolvedTask = taskId || safeCurrentTaskId(projectRoot);
|
|
113
|
+
if (!resolvedTask) {
|
|
114
|
+
throw new Error("No task specified and no active task in session.");
|
|
115
|
+
}
|
|
116
|
+
const repoRoot = targetRepo === "monorepo" ? resolveOptionalMonorepoRoot(projectRoot) || resolveMonorepoRoot(projectRoot) : projectRoot;
|
|
117
|
+
const repoLabel = targetRepo === "monorepo" ? "Monorepo" : "Project";
|
|
118
|
+
if (!existsSync3(resolve(repoRoot, ".git"))) {
|
|
119
|
+
throw new Error(`${repoLabel} repo not found at ${repoRoot}`);
|
|
120
|
+
}
|
|
121
|
+
const branchId = resolveTaskBranchId(projectRoot, resolvedTask);
|
|
122
|
+
const branchTarget = `rig/${branchId}`;
|
|
123
|
+
const current = branchName(projectRoot, repoRoot);
|
|
124
|
+
if (current === branchTarget) {
|
|
125
|
+
console.log(`${repoLabel} branch: already on ${branchTarget}`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const hasBranch = runCapture(gitCmd(projectRoot, repoRoot, "show-ref", "--verify", "--quiet", `refs/heads/${branchTarget}`), projectRoot).exitCode === 0;
|
|
129
|
+
const cmd = hasBranch && current === "HEAD" ? gitCmd(projectRoot, repoRoot, "checkout", "-B", branchTarget) : hasBranch ? gitCmd(projectRoot, repoRoot, "checkout", branchTarget) : gitCmd(projectRoot, repoRoot, "checkout", "-b", branchTarget);
|
|
130
|
+
const checkout = runCapture(cmd, projectRoot);
|
|
131
|
+
if (checkout.exitCode !== 0) {
|
|
132
|
+
throw new Error(`Failed to sync ${repoLabel.toLowerCase()} branch: ${checkout.stderr || checkout.stdout}`);
|
|
133
|
+
}
|
|
134
|
+
const action = hasBranch && current === "HEAD" ? "reset" : hasBranch ? "checked out" : "created";
|
|
135
|
+
console.log(`${repoLabel} branch: ${action} ${branchTarget}`);
|
|
136
|
+
}
|
|
137
|
+
function resolveTaskBranchId(projectRoot, taskId) {
|
|
138
|
+
if (/^bd-[a-z0-9-]+$/.test(taskId)) {
|
|
139
|
+
return taskId;
|
|
140
|
+
}
|
|
141
|
+
const normalizedTaskId = taskData().lookupTask(projectRoot, taskId);
|
|
142
|
+
if (normalizedTaskId) {
|
|
143
|
+
return normalizedTaskId;
|
|
144
|
+
}
|
|
145
|
+
const currentTask = taskData().currentTaskId(projectRoot);
|
|
146
|
+
if (currentTask && currentTask === taskId) {
|
|
147
|
+
return currentTask;
|
|
148
|
+
}
|
|
149
|
+
const runtimeIdFromEnv = (process.env.RIG_TASK_RUNTIME_ID || "").trim();
|
|
150
|
+
if (runtimeIdFromEnv.startsWith("task-") && runtimeIdFromEnv.length > "task-".length) {
|
|
151
|
+
return runtimeIdFromEnv.slice("task-".length);
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const runtimeIdFromContext = loadRuntimeContextFromEnv()?.runtimeId || "";
|
|
155
|
+
if (runtimeIdFromContext.startsWith("task-") && runtimeIdFromContext.length > "task-".length) {
|
|
156
|
+
return runtimeIdFromContext.slice("task-".length);
|
|
157
|
+
}
|
|
158
|
+
} catch {}
|
|
159
|
+
const artifactDir = taskData().artifactDirForId(projectRoot, taskId);
|
|
160
|
+
if (existsSync3(artifactDir)) {
|
|
161
|
+
return taskId;
|
|
162
|
+
}
|
|
163
|
+
throw new Error(`Unknown task id: ${taskId}`);
|
|
164
|
+
}
|
|
165
|
+
function branchName(projectRoot, repo) {
|
|
166
|
+
return runCapture(gitCmd(projectRoot, repo, "rev-parse", "--abbrev-ref", "HEAD"), projectRoot).stdout.trim();
|
|
167
|
+
}
|
|
168
|
+
function runCapture(command, cwd, projectRoot = cwd) {
|
|
169
|
+
return baseRunCapture(command, cwd, runtimeGitEnv(projectRoot));
|
|
170
|
+
}
|
|
171
|
+
function runtimeGitEnv(projectRoot) {
|
|
172
|
+
const { ctx, runtimeRoot } = resolveRuntimeMetadata(projectRoot);
|
|
173
|
+
const runtimeHome = runtimeRoot ? resolve(runtimeRoot, "home") : "";
|
|
174
|
+
const runtimeTmp = runtimeRoot ? resolve(runtimeRoot, "tmp") : "";
|
|
175
|
+
const runtimeCache = runtimeRoot ? resolve(runtimeRoot, "cache") : "";
|
|
176
|
+
const runtimeKnownHosts = runtimeHome ? resolve(runtimeHome, ".ssh", "known_hosts") : "";
|
|
177
|
+
const runtimeKey = runtimeHome ? resolve(runtimeHome, ".ssh", "rig-agent-key") : "";
|
|
178
|
+
const env = {};
|
|
179
|
+
if (ctx?.workspaceDir) {
|
|
180
|
+
env.PROJECT_RIG_ROOT = projectRoot;
|
|
181
|
+
env.RIG_TASK_WORKSPACE = ctx.workspaceDir;
|
|
182
|
+
env.MONOREPO_ROOT = ctx.workspaceDir;
|
|
183
|
+
env.MONOREPO_MAIN_ROOT = resolveMonorepoRoot(projectRoot);
|
|
184
|
+
} else if (projectRoot) {
|
|
185
|
+
env.PROJECT_RIG_ROOT = projectRoot;
|
|
186
|
+
}
|
|
187
|
+
if (runtimeRoot) {
|
|
188
|
+
env.RIG_RUNTIME_HOME = runtimeRoot;
|
|
189
|
+
}
|
|
190
|
+
if (runtimeHome && existsSync3(runtimeHome)) {
|
|
191
|
+
env.HOME = runtimeHome;
|
|
192
|
+
env.OPENSSL_CONF = ensureRuntimeOpenSslConfig(runtimeHome);
|
|
193
|
+
}
|
|
194
|
+
if (runtimeTmp && existsSync3(runtimeTmp)) {
|
|
195
|
+
env.TMPDIR = runtimeTmp;
|
|
196
|
+
}
|
|
197
|
+
if (runtimeCache && existsSync3(runtimeCache)) {
|
|
198
|
+
env.XDG_CACHE_HOME = runtimeCache;
|
|
199
|
+
}
|
|
200
|
+
const workspaceSecrets = loadDotEnvSecrets(ctx?.workspaceDir || projectRoot, process.env);
|
|
201
|
+
for (const [key, value] of Object.entries(resolveRuntimeSecrets(process.env, workspaceSecrets))) {
|
|
202
|
+
if (key === "GITHUB_SSH_KEY" || !value) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
env[key] = value;
|
|
206
|
+
}
|
|
207
|
+
const rigGithubToken = process.env.RIG_GITHUB_TOKEN?.trim() || authStateToken(process.env) || "";
|
|
208
|
+
if (rigGithubToken && !env.GITHUB_TOKEN && !env.GH_TOKEN) {
|
|
209
|
+
env.GITHUB_TOKEN = rigGithubToken;
|
|
210
|
+
}
|
|
211
|
+
if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
|
|
212
|
+
env.GITHUB_TOKEN = env.GH_TOKEN;
|
|
213
|
+
}
|
|
214
|
+
if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
|
|
215
|
+
env.GH_TOKEN = env.GITHUB_TOKEN;
|
|
216
|
+
}
|
|
217
|
+
if (!env.GREPTILE_GITHUB_TOKEN && env.GITHUB_TOKEN) {
|
|
218
|
+
env.GREPTILE_GITHUB_TOKEN = env.GITHUB_TOKEN;
|
|
219
|
+
}
|
|
220
|
+
const persistedSecrets = loadPersistedRuntimeSecrets(runtimeRoot);
|
|
221
|
+
for (const [key, value] of Object.entries(persistedSecrets)) {
|
|
222
|
+
if (!value)
|
|
223
|
+
continue;
|
|
224
|
+
if (!env[key]) {
|
|
225
|
+
env[key] = value;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
|
|
229
|
+
env.GITHUB_TOKEN = env.GH_TOKEN;
|
|
230
|
+
}
|
|
231
|
+
if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
|
|
232
|
+
env.GH_TOKEN = env.GITHUB_TOKEN;
|
|
233
|
+
}
|
|
234
|
+
const gitHubToken = env.GITHUB_TOKEN || env.GH_TOKEN || env.RIG_GITHUB_TOKEN || rigGithubToken;
|
|
235
|
+
if (gitHubToken) {
|
|
236
|
+
env.RIG_GITHUB_TOKEN = gitHubToken;
|
|
237
|
+
env.GITHUB_TOKEN = env.GITHUB_TOKEN || gitHubToken;
|
|
238
|
+
env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
|
|
239
|
+
applyGitHubCredentialHelperEnv(env);
|
|
240
|
+
}
|
|
241
|
+
if (runtimeKnownHosts && existsSync3(runtimeKnownHosts)) {
|
|
242
|
+
const sshParts = [
|
|
243
|
+
"ssh",
|
|
244
|
+
`-o UserKnownHostsFile="${runtimeKnownHosts}"`,
|
|
245
|
+
"-o StrictHostKeyChecking=yes",
|
|
246
|
+
"-F /dev/null"
|
|
247
|
+
];
|
|
248
|
+
if (runtimeKey && existsSync3(runtimeKey)) {
|
|
249
|
+
sshParts.splice(1, 0, `-i "${runtimeKey}"`, "-o IdentitiesOnly=yes");
|
|
250
|
+
}
|
|
251
|
+
env.GIT_SSH_COMMAND = sshParts.join(" ");
|
|
252
|
+
} else if (process.env.GIT_SSH_COMMAND?.trim()) {
|
|
253
|
+
env.GIT_SSH_COMMAND = process.env.GIT_SSH_COMMAND;
|
|
254
|
+
}
|
|
255
|
+
return Object.keys(env).length > 0 ? env : undefined;
|
|
256
|
+
}
|
|
257
|
+
function applyGitHubCredentialHelperEnv(env) {
|
|
258
|
+
env.GIT_TERMINAL_PROMPT = "0";
|
|
259
|
+
env.GIT_CONFIG_COUNT = "2";
|
|
260
|
+
env.GIT_CONFIG_KEY_0 = "credential.helper";
|
|
261
|
+
env.GIT_CONFIG_VALUE_0 = "";
|
|
262
|
+
env.GIT_CONFIG_KEY_1 = "credential.helper";
|
|
263
|
+
env.GIT_CONFIG_VALUE_1 = '!f() { test "$1" = get || exit 0; token="${GITHUB_TOKEN:-${GH_TOKEN:-${RIG_GITHUB_TOKEN:-}}}"; test -n "$token" || exit 0; echo username=x-access-token; echo password="$token"; }; f';
|
|
264
|
+
}
|
|
265
|
+
function loadPersistedRuntimeSecrets(runtimeRoot) {
|
|
266
|
+
if (!runtimeRoot) {
|
|
267
|
+
return {};
|
|
268
|
+
}
|
|
269
|
+
const path = resolve(runtimeRoot, "runtime-secrets.json");
|
|
270
|
+
if (!existsSync3(path)) {
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
275
|
+
const allowed = new Set(["GITHUB_TOKEN", "GH_TOKEN", "RIG_GITHUB_TOKEN"]);
|
|
276
|
+
const entries = Object.entries(parsed).filter((entry) => typeof entry[1] === "string" && allowed.has(entry[0]));
|
|
277
|
+
return Object.fromEntries(entries);
|
|
278
|
+
} catch {
|
|
279
|
+
return {};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function ensureRuntimeOpenSslConfig(runtimeHome) {
|
|
283
|
+
const sslDir = resolve(runtimeHome, ".ssl");
|
|
284
|
+
const sslConfig = resolve(sslDir, "openssl.cnf");
|
|
285
|
+
if (!existsSync3(sslDir)) {
|
|
286
|
+
mkdirSync(sslDir, { recursive: true });
|
|
287
|
+
}
|
|
288
|
+
if (!existsSync3(sslConfig)) {
|
|
289
|
+
writeFileSync(sslConfig, `# Rig runtime OpenSSL config placeholder
|
|
290
|
+
`);
|
|
291
|
+
}
|
|
292
|
+
return sslConfig;
|
|
293
|
+
}
|
|
294
|
+
function resolveRuntimeMetadata(projectRoot) {
|
|
295
|
+
const contextFile = process.env.RIG_RUNTIME_CONTEXT_FILE?.trim();
|
|
296
|
+
const runtimeHome = process.env.RIG_RUNTIME_HOME?.trim();
|
|
297
|
+
let ctx = loadRuntimeContextFromEnv();
|
|
298
|
+
if (runtimeHome) {
|
|
299
|
+
return {
|
|
300
|
+
ctx,
|
|
301
|
+
runtimeRoot: runtimeHome
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (contextFile) {
|
|
305
|
+
return {
|
|
306
|
+
ctx,
|
|
307
|
+
runtimeRoot: dirname(resolve(contextFile))
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
const inferredContextFile = findRuntimeContextFile(projectRoot);
|
|
311
|
+
if (existsSync3(inferredContextFile)) {
|
|
312
|
+
try {
|
|
313
|
+
ctx = loadRuntimeContext(inferredContextFile);
|
|
314
|
+
} catch {}
|
|
315
|
+
return {
|
|
316
|
+
ctx,
|
|
317
|
+
runtimeRoot: dirname(inferredContextFile)
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return { ctx, runtimeRoot: "" };
|
|
321
|
+
}
|
|
322
|
+
function findRuntimeContextFile(startPath) {
|
|
323
|
+
let current = resolve(startPath);
|
|
324
|
+
while (true) {
|
|
325
|
+
const candidate = resolve(current, "runtime-context.json");
|
|
326
|
+
if (existsSync3(candidate)) {
|
|
327
|
+
return candidate;
|
|
328
|
+
}
|
|
329
|
+
const parent = dirname(current);
|
|
330
|
+
if (parent === current) {
|
|
331
|
+
return "";
|
|
332
|
+
}
|
|
333
|
+
current = parent;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// packages/bundle-default-lifecycle/src/control-plane/hooks/task-runtime-start.ts
|
|
338
|
+
import { taskRuntimeId } from "@rig/core/safe-identifiers";
|
|
339
|
+
import { ISOLATION_BACKEND, REPO_OPERATIONS_CAPABILITY } from "@rig/contracts";
|
|
340
|
+
import { defineCapability as defineCapability2 } from "@rig/core/capability";
|
|
341
|
+
import { requireCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
342
|
+
import { loadRuntimeContextFromEnv as loadRuntimeContextFromEnv2 } from "@rig/core/runtime-context";
|
|
343
|
+
var RepoOperationsCap = defineCapability2(REPO_OPERATIONS_CAPABILITY);
|
|
344
|
+
function resolveTaskRuntimeMode(raw) {
|
|
345
|
+
if (raw === "off" || raw === "worktree") {
|
|
346
|
+
return raw;
|
|
347
|
+
}
|
|
348
|
+
return "worktree";
|
|
349
|
+
}
|
|
350
|
+
function resolveActiveRuntimeId(taskId) {
|
|
351
|
+
const runtimeIdFromEnv = (process.env.RIG_TASK_RUNTIME_ID || "").trim();
|
|
352
|
+
if (runtimeIdFromEnv) {
|
|
353
|
+
return runtimeIdFromEnv;
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
const runtimeIdFromContext = loadRuntimeContextFromEnv2()?.runtimeId?.trim();
|
|
357
|
+
if (runtimeIdFromContext) {
|
|
358
|
+
return runtimeIdFromContext;
|
|
359
|
+
}
|
|
360
|
+
} catch {}
|
|
361
|
+
return taskRuntimeId(taskId);
|
|
362
|
+
}
|
|
363
|
+
function resolveActiveWorkspaceDir() {
|
|
364
|
+
const workspaceDirFromEnv = (process.env.RIG_TASK_WORKSPACE || "").trim();
|
|
365
|
+
if (workspaceDirFromEnv) {
|
|
366
|
+
return workspaceDirFromEnv;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
const workspaceDirFromContext = loadRuntimeContextFromEnv2()?.workspaceDir?.trim();
|
|
370
|
+
if (workspaceDirFromContext) {
|
|
371
|
+
return workspaceDirFromContext;
|
|
372
|
+
}
|
|
373
|
+
} catch {}
|
|
374
|
+
return "";
|
|
375
|
+
}
|
|
376
|
+
function isProvisionedRuntimeWorkspace(workspaceDir) {
|
|
377
|
+
if (!workspaceDir) {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
const layout = resolveRuntimeWorkspaceLayout(workspaceDir);
|
|
381
|
+
return existsSync4(resolve2(workspaceDir, ".git")) && existsSync4(resolve2(layout.binDir, "rig-agent")) && existsSync4(resolve2(layout.binDir, "hooks", "scope-guard")) && existsSync4(resolve2(layout.sessionDir, "session.json"));
|
|
382
|
+
}
|
|
383
|
+
async function main() {
|
|
384
|
+
const projectRoot = resolveProjectRoot();
|
|
385
|
+
const taskId = taskData().currentTaskId(projectRoot);
|
|
386
|
+
if (!taskId) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const runtimeId = resolveActiveRuntimeId(taskId);
|
|
390
|
+
const canonicalRuntimeId = taskRuntimeId(taskId);
|
|
391
|
+
const workspaceDir = resolveActiveWorkspaceDir();
|
|
392
|
+
if (runtimeId === canonicalRuntimeId) {
|
|
393
|
+
if (isProvisionedRuntimeWorkspace(workspaceDir)) {
|
|
394
|
+
console.log(`Task runtime already provisioned (${runtimeId}); skipping in-session reprovision.`);
|
|
395
|
+
console.log(`Task runtime ready (worktree): ${workspaceDir}`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
gitSyncBranch(projectRoot, taskId);
|
|
399
|
+
const repoOperations = await requireCapabilityForRoot(projectRoot, RepoOperationsCap, `No REPO_OPERATIONS capability is registered for project root "${projectRoot}". ` + "Install @rig/repos-plugin (it ships in the default bundle) so task repo pins can be refreshed.");
|
|
400
|
+
repoOperations.repoEnsure(projectRoot, taskId);
|
|
401
|
+
} else {
|
|
402
|
+
console.log(`Task runtime already provisioned (${runtimeId}); skipping canonical branch sync.`);
|
|
403
|
+
console.log(`Task runtime already provisioned (${runtimeId}); skipping repo pin refresh.`);
|
|
404
|
+
if (isProvisionedRuntimeWorkspace(workspaceDir)) {
|
|
405
|
+
console.log(`Task runtime ready (worktree): ${workspaceDir}`);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const mode = resolveTaskRuntimeMode(process.env.RIG_TASK_RUNTIME_MODE);
|
|
410
|
+
if (mode !== "off") {
|
|
411
|
+
const isolationBackend = await requireCapabilityForRoot(projectRoot, defineCapability2(ISOLATION_BACKEND), `No ISOLATION_BACKEND capability is registered for project root "${projectRoot}". ` + "Install @rig/isolation-plugin (it ships in the default bundle) so runtime provisioning can resolve a backend.");
|
|
412
|
+
const runtime = await isolationBackend.ensureAgentRuntime({
|
|
413
|
+
projectRoot,
|
|
414
|
+
id: runtimeId,
|
|
415
|
+
taskId,
|
|
416
|
+
mode
|
|
417
|
+
});
|
|
418
|
+
console.log(`Task runtime ready (${mode}): ${runtime.workspaceDir}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (process.env.RIG_HOOK_ROLE === "task-runtime-start") {
|
|
422
|
+
main().catch((error) => {
|
|
423
|
+
console.error(error);
|
|
424
|
+
process.exit(1);
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// packages/bundle-default-lifecycle/src/control-plane/hooks/submodule-branch.ts
|
|
429
|
+
main().catch((error) => {
|
|
430
|
+
console.error(error);
|
|
431
|
+
process.exit(1);
|
|
432
|
+
});
|