@h-rig/cli 0.0.6-alpha.35 → 0.0.6-alpha.37
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.js +158 -41
- package/dist/src/commands/_help-catalog.js +63 -32
- package/dist/src/commands/_operator-view.js +35 -4
- package/dist/src/commands/_pi-frontend.js +35 -4
- package/dist/src/commands/_pi-worker-bridge-extension.js +35 -2
- package/dist/src/commands/_run-driver-helpers.js +21 -0
- package/dist/src/commands/_server-client.js +0 -9
- package/dist/src/commands/inspect.js +227 -11
- package/dist/src/commands/run.js +35 -4
- package/dist/src/commands/server.js +7 -2
- package/dist/src/commands/task-run-driver.js +21 -0
- package/dist/src/commands/task.js +97 -36
- package/dist/src/commands.js +158 -41
- package/dist/src/index.js +158 -41
- package/package.json +6 -6
|
@@ -566,7 +566,38 @@ function parseWsPayload(message) {
|
|
|
566
566
|
return JSON.parse(message.data);
|
|
567
567
|
return JSON.parse(Buffer.from(message.data).toString("utf8"));
|
|
568
568
|
}
|
|
569
|
-
|
|
569
|
+
var BRIDGE_LOCAL_COMMANDS = new Set(["detach", "quit", "q", "stop"]);
|
|
570
|
+
function registerDaemonCommandsNatively(pi, options, ctx, state, commands, registered) {
|
|
571
|
+
for (const command of commands) {
|
|
572
|
+
const record = recordOf(command);
|
|
573
|
+
const name = typeof record?.name === "string" ? record.name : "";
|
|
574
|
+
if (!name || registered.has(name) || BRIDGE_LOCAL_COMMANDS.has(name))
|
|
575
|
+
continue;
|
|
576
|
+
registered.add(name);
|
|
577
|
+
const description = typeof record?.description === "string" ? record.description : undefined;
|
|
578
|
+
const source = typeof record?.source === "string" ? record.source : "worker";
|
|
579
|
+
try {
|
|
580
|
+
pi.registerCommand(name, {
|
|
581
|
+
description: `[worker ${source}] ${description ?? ""}`.trim(),
|
|
582
|
+
handler: async (args) => {
|
|
583
|
+
const text = `/${name}${args ? ` ${args}` : ""}`;
|
|
584
|
+
appendTranscript(state, "You", text);
|
|
585
|
+
try {
|
|
586
|
+
const result = await runRunPiCommandViaServer(options.context, options.runId, text);
|
|
587
|
+
const message = typeof result.message === "string" ? result.message : "worker command accepted";
|
|
588
|
+
appendTranscript(state, "System", message);
|
|
589
|
+
if (state.nativeStream)
|
|
590
|
+
ctx.ui.notify(message, "info");
|
|
591
|
+
} catch (error) {
|
|
592
|
+
reportBridgeError(ctx, state, error instanceof Error ? error.message : String(error));
|
|
593
|
+
}
|
|
594
|
+
updatePiUi(ctx, state);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
} catch {}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
async function connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands) {
|
|
570
601
|
const ready = await waitForWorkerReady(options, ctx, state);
|
|
571
602
|
if (!ready)
|
|
572
603
|
return;
|
|
@@ -621,6 +652,7 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
621
652
|
const record = recordOf(command);
|
|
622
653
|
return typeof record?.name === "string" ? [`/${record.name}`] : [];
|
|
623
654
|
});
|
|
655
|
+
registerDaemonCommandsNatively(pi, options, ctx, state, commands, registeredDaemonCommands);
|
|
624
656
|
catchupDone = true;
|
|
625
657
|
for (const payload of buffered.splice(0))
|
|
626
658
|
applyEnvelope(ctx, state, payload);
|
|
@@ -748,6 +780,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
748
780
|
};
|
|
749
781
|
if (options.initialMessageSent)
|
|
750
782
|
appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
|
|
783
|
+
const registeredDaemonCommands = new Set;
|
|
751
784
|
let nativePiUiContextAvailable = false;
|
|
752
785
|
pi.on("user_bash", (event) => {
|
|
753
786
|
state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
|
|
@@ -780,7 +813,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
780
813
|
});
|
|
781
814
|
return { consume: true };
|
|
782
815
|
});
|
|
783
|
-
connectWorkerStream(options, ctx, state).catch((error) => {
|
|
816
|
+
connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands).catch((error) => {
|
|
784
817
|
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
785
818
|
updatePiUi(ctx, state);
|
|
786
819
|
});
|
|
@@ -205,6 +205,7 @@ ${acceptance}` : null,
|
|
|
205
205
|
${sourceContractLines.join(`
|
|
206
206
|
`)}` : null,
|
|
207
207
|
scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
|
|
208
|
+
readPriorPrProgressForPrompt(input.projectRoot, input.taskId),
|
|
208
209
|
initialPrompt ? `Additional operator guidance:
|
|
209
210
|
${initialPrompt}` : null,
|
|
210
211
|
providerLines.length > 0 ? `Provider-specific runtime tooling:
|
|
@@ -214,6 +215,26 @@ ${providerLines.join(" ")}` : null,
|
|
|
214
215
|
|
|
215
216
|
`);
|
|
216
217
|
}
|
|
218
|
+
function readPriorPrProgressForPrompt(projectRoot, taskId) {
|
|
219
|
+
for (const candidate of [
|
|
220
|
+
resolve3(projectRoot, ".worktrees", taskId, "artifacts", taskId, "pr-state.json"),
|
|
221
|
+
resolve3(projectRoot, "artifacts", taskId, "pr-state.json")
|
|
222
|
+
]) {
|
|
223
|
+
try {
|
|
224
|
+
const raw = JSON.parse(readFileSync(candidate, "utf8"));
|
|
225
|
+
const entries = Array.isArray(raw) ? raw : [raw];
|
|
226
|
+
const first = entries.find((entry) => entry && typeof entry === "object" && typeof entry.url === "string");
|
|
227
|
+
if (!first)
|
|
228
|
+
continue;
|
|
229
|
+
return [
|
|
230
|
+
`Prior progress exists for this task: PR ${first.url}${first.branch ? ` (branch ${first.branch})` : ""} was opened by an earlier run.`,
|
|
231
|
+
"Check its current state first (diff, checks, review). If the work is already complete and checks are green,",
|
|
232
|
+
"run `rig-agent completion-verification` to merge and close instead of re-implementing anything."
|
|
233
|
+
].join(" ");
|
|
234
|
+
} catch {}
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
217
238
|
function firstPromptString(...values) {
|
|
218
239
|
for (const value of values) {
|
|
219
240
|
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
@@ -416,14 +416,6 @@ async function runRunPiCommandViaServer(context, runId, text) {
|
|
|
416
416
|
});
|
|
417
417
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
|
|
418
418
|
}
|
|
419
|
-
async function respondRunPiCommandViaServer(context, runId, requestId, value) {
|
|
420
|
-
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands/respond`, {
|
|
421
|
-
method: "POST",
|
|
422
|
-
headers: { "content-type": "application/json" },
|
|
423
|
-
body: JSON.stringify({ requestId, value })
|
|
424
|
-
});
|
|
425
|
-
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
|
|
426
|
-
}
|
|
427
419
|
async function respondRunPiExtensionUiViaServer(context, runId, requestId, valueOrCancel) {
|
|
428
420
|
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/extension-ui/respond`, {
|
|
429
421
|
method: "POST",
|
|
@@ -487,7 +479,6 @@ export {
|
|
|
487
479
|
selectNextWorkspaceTaskViaServer,
|
|
488
480
|
runRunPiCommandViaServer,
|
|
489
481
|
respondRunPiExtensionUiViaServer,
|
|
490
|
-
respondRunPiCommandViaServer,
|
|
491
482
|
requestServerJson,
|
|
492
483
|
registerProjectViaServer,
|
|
493
484
|
prepareRemoteCheckoutViaServer,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/inspect.ts
|
|
3
|
-
import { existsSync, readFileSync } from "fs";
|
|
4
|
-
import { resolve } from "path";
|
|
3
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
4
|
+
import { resolve as resolve3 } from "path";
|
|
5
5
|
|
|
6
6
|
// packages/cli/src/runner.ts
|
|
7
7
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
@@ -53,6 +53,205 @@ import {
|
|
|
53
53
|
import { changedFilesForTask } from "@rig/runtime/control-plane/native/task-ops";
|
|
54
54
|
import { resolveHarnessPaths, resolveMonorepoRoot, runCapture } from "@rig/runtime/control-plane/native/utils";
|
|
55
55
|
import { readTaskArtifactPreview } from "@rig/runtime/control-plane/native/workspace-ops";
|
|
56
|
+
|
|
57
|
+
// packages/cli/src/commands/_server-client.ts
|
|
58
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
59
|
+
import { resolve as resolve2 } from "path";
|
|
60
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
61
|
+
|
|
62
|
+
// packages/cli/src/commands/_connection-state.ts
|
|
63
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
64
|
+
import { homedir } from "os";
|
|
65
|
+
import { dirname, resolve } from "path";
|
|
66
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
67
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
68
|
+
if (explicit)
|
|
69
|
+
return resolve(explicit);
|
|
70
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
71
|
+
if (stateDir)
|
|
72
|
+
return resolve(stateDir, "connections.json");
|
|
73
|
+
return resolve(homedir(), ".rig", "connections.json");
|
|
74
|
+
}
|
|
75
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
76
|
+
return resolve(projectRoot, ".rig", "state", "connection.json");
|
|
77
|
+
}
|
|
78
|
+
function readJsonFile(path) {
|
|
79
|
+
if (!existsSync(path))
|
|
80
|
+
return null;
|
|
81
|
+
try {
|
|
82
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function normalizeConnection(value) {
|
|
88
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
89
|
+
return null;
|
|
90
|
+
const record = value;
|
|
91
|
+
if (record.kind === "local")
|
|
92
|
+
return { kind: "local", mode: "auto" };
|
|
93
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
94
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
95
|
+
return { kind: "remote", baseUrl };
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function readGlobalConnections(options = {}) {
|
|
100
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
101
|
+
const payload = readJsonFile(path);
|
|
102
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
103
|
+
return { connections: {} };
|
|
104
|
+
}
|
|
105
|
+
const rawConnections = payload.connections;
|
|
106
|
+
const connections = {};
|
|
107
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
108
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
109
|
+
const connection = normalizeConnection(raw);
|
|
110
|
+
if (connection)
|
|
111
|
+
connections[alias] = connection;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { connections };
|
|
115
|
+
}
|
|
116
|
+
function readRepoConnection(projectRoot) {
|
|
117
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
118
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
119
|
+
return null;
|
|
120
|
+
const record = payload;
|
|
121
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
122
|
+
if (!selected)
|
|
123
|
+
return null;
|
|
124
|
+
return {
|
|
125
|
+
selected,
|
|
126
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
127
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
131
|
+
const repo = readRepoConnection(projectRoot);
|
|
132
|
+
if (!repo)
|
|
133
|
+
return null;
|
|
134
|
+
if (repo.selected === "local")
|
|
135
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
136
|
+
const global = readGlobalConnections(options);
|
|
137
|
+
const connection = global.connections[repo.selected];
|
|
138
|
+
if (!connection) {
|
|
139
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
140
|
+
}
|
|
141
|
+
return { alias: repo.selected, connection };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// packages/cli/src/commands/_server-client.ts
|
|
145
|
+
var scopedGitHubBearerTokens = new Map;
|
|
146
|
+
function cleanToken(value) {
|
|
147
|
+
const trimmed = value?.trim();
|
|
148
|
+
return trimmed ? trimmed : null;
|
|
149
|
+
}
|
|
150
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
151
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
152
|
+
if (!existsSync2(path))
|
|
153
|
+
return null;
|
|
154
|
+
try {
|
|
155
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
156
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
162
|
+
const scopedKey = resolve2(projectRoot);
|
|
163
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
164
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
165
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
166
|
+
if (privateSession)
|
|
167
|
+
return privateSession;
|
|
168
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
169
|
+
}
|
|
170
|
+
async function ensureServerForCli(projectRoot) {
|
|
171
|
+
try {
|
|
172
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
173
|
+
if (selected?.connection.kind === "remote") {
|
|
174
|
+
return {
|
|
175
|
+
baseUrl: selected.connection.baseUrl,
|
|
176
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
177
|
+
connectionKind: "remote"
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
181
|
+
return {
|
|
182
|
+
baseUrl: connection.baseUrl,
|
|
183
|
+
authToken: connection.authToken,
|
|
184
|
+
connectionKind: "local"
|
|
185
|
+
};
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error instanceof Error) {
|
|
188
|
+
throw new CliError2(error.message, 1);
|
|
189
|
+
}
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function mergeHeaders(headers, authToken) {
|
|
194
|
+
const merged = new Headers(headers);
|
|
195
|
+
if (authToken) {
|
|
196
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
197
|
+
}
|
|
198
|
+
return merged;
|
|
199
|
+
}
|
|
200
|
+
function diagnosticMessage(payload) {
|
|
201
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
202
|
+
return null;
|
|
203
|
+
const record = payload;
|
|
204
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
205
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
206
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
207
|
+
return [];
|
|
208
|
+
const diagnostic = entry;
|
|
209
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
210
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
211
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
212
|
+
});
|
|
213
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
214
|
+
}
|
|
215
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
216
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
217
|
+
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
218
|
+
...init,
|
|
219
|
+
headers: mergeHeaders(init.headers, server.authToken)
|
|
220
|
+
});
|
|
221
|
+
const text = await response.text();
|
|
222
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
223
|
+
try {
|
|
224
|
+
return JSON.parse(text);
|
|
225
|
+
} catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
})() : null;
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
const diagnostics = diagnosticMessage(payload);
|
|
231
|
+
const detail = diagnostics ?? (text || response.statusText);
|
|
232
|
+
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
233
|
+
}
|
|
234
|
+
return payload;
|
|
235
|
+
}
|
|
236
|
+
async function listRunsViaServer(context, options = {}) {
|
|
237
|
+
const url = new URL("http://rig.local/api/runs");
|
|
238
|
+
if (options.limit !== undefined)
|
|
239
|
+
url.searchParams.set("limit", String(options.limit));
|
|
240
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
241
|
+
const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
|
|
242
|
+
return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
|
|
243
|
+
}
|
|
244
|
+
async function getRunLogsViaServer(context, runId, options = {}) {
|
|
245
|
+
const url = new URL(`http://rig.local/api/runs/${encodeURIComponent(runId)}/logs`);
|
|
246
|
+
if (options.limit !== undefined)
|
|
247
|
+
url.searchParams.set("limit", String(options.limit));
|
|
248
|
+
if (options.cursor)
|
|
249
|
+
url.searchParams.set("cursor", options.cursor);
|
|
250
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
251
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// packages/cli/src/commands/inspect.ts
|
|
56
255
|
async function executeInspect(context, args) {
|
|
57
256
|
const [command = "failures", ...rest] = args;
|
|
58
257
|
switch (command) {
|
|
@@ -62,10 +261,27 @@ async function executeInspect(context, args) {
|
|
|
62
261
|
const requiredTask = requireTask(task, "rig inspect logs --task <task-id>");
|
|
63
262
|
const latestRun = listAuthorityRuns(context.projectRoot).map((entry) => readAuthorityRun(context.projectRoot, entry.runId)).filter((run) => Boolean(run)).filter((run) => run.taskId === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
|
|
64
263
|
if (!latestRun) {
|
|
65
|
-
|
|
264
|
+
const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
|
|
265
|
+
const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
|
|
266
|
+
if (!serverRun || typeof serverRun.runId !== "string") {
|
|
267
|
+
throw new CliError2(`No runs found for ${requiredTask} (local or on the selected server).`);
|
|
268
|
+
}
|
|
269
|
+
const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
|
|
270
|
+
const entries = Array.isArray(page.entries) ? page.entries : [];
|
|
271
|
+
if (context.outputMode === "text") {
|
|
272
|
+
for (const entry of entries) {
|
|
273
|
+
const record = entry && typeof entry === "object" ? entry : {};
|
|
274
|
+
const title = String(record.title ?? "");
|
|
275
|
+
const detail = String(record.detail ?? "");
|
|
276
|
+
console.log([title, detail].filter(Boolean).join(" \u2014 "));
|
|
277
|
+
}
|
|
278
|
+
if (entries.length === 0)
|
|
279
|
+
console.log(`(no log entries for run ${serverRun.runId})`);
|
|
280
|
+
}
|
|
281
|
+
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: serverRun.runId, source: "server", entries } };
|
|
66
282
|
}
|
|
67
|
-
const logsPath =
|
|
68
|
-
if (!
|
|
283
|
+
const logsPath = resolve3(resolveAuthorityRunDir(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
284
|
+
if (!existsSync3(logsPath)) {
|
|
69
285
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
70
286
|
}
|
|
71
287
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -75,7 +291,7 @@ async function executeInspect(context, args) {
|
|
|
75
291
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
76
292
|
requireNoExtraArgs(remaining, "rig inspect artifacts --task <task-id>");
|
|
77
293
|
const requiredTask = requireTask(task, "rig inspect artifacts --task <task-id>");
|
|
78
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
294
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync3(path));
|
|
79
295
|
if (!artifactRoot) {
|
|
80
296
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
81
297
|
}
|
|
@@ -132,10 +348,10 @@ async function executeInspect(context, args) {
|
|
|
132
348
|
case "failures": {
|
|
133
349
|
requireNoExtraArgs(rest, "rig inspect failures");
|
|
134
350
|
const failed = resolveHarnessPaths(context.projectRoot).failedApproachesPath;
|
|
135
|
-
if (!
|
|
351
|
+
if (!existsSync3(failed)) {
|
|
136
352
|
console.log("No failures recorded.");
|
|
137
353
|
} else {
|
|
138
|
-
process.stdout.write(
|
|
354
|
+
process.stdout.write(readFileSync3(failed, "utf-8"));
|
|
139
355
|
}
|
|
140
356
|
return { ok: true, group: "inspect", command };
|
|
141
357
|
}
|
|
@@ -152,11 +368,11 @@ async function executeInspect(context, args) {
|
|
|
152
368
|
return { ok: true, group: "inspect", command };
|
|
153
369
|
case "audit": {
|
|
154
370
|
requireNoExtraArgs(rest, "rig inspect audit");
|
|
155
|
-
const auditPath =
|
|
156
|
-
if (!
|
|
371
|
+
const auditPath = resolve3(resolveHarnessPaths(context.projectRoot).logsDir, "audit.jsonl");
|
|
372
|
+
if (!existsSync3(auditPath)) {
|
|
157
373
|
console.log("No audit log found.");
|
|
158
374
|
} else {
|
|
159
|
-
const lines =
|
|
375
|
+
const lines = readFileSync3(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
160
376
|
for (const line of lines) {
|
|
161
377
|
console.log(line);
|
|
162
378
|
}
|
package/dist/src/commands/run.js
CHANGED
|
@@ -880,7 +880,38 @@ function parseWsPayload(message) {
|
|
|
880
880
|
return JSON.parse(message.data);
|
|
881
881
|
return JSON.parse(Buffer.from(message.data).toString("utf8"));
|
|
882
882
|
}
|
|
883
|
-
|
|
883
|
+
var BRIDGE_LOCAL_COMMANDS = new Set(["detach", "quit", "q", "stop"]);
|
|
884
|
+
function registerDaemonCommandsNatively(pi, options, ctx, state, commands, registered) {
|
|
885
|
+
for (const command of commands) {
|
|
886
|
+
const record = recordOf(command);
|
|
887
|
+
const name = typeof record?.name === "string" ? record.name : "";
|
|
888
|
+
if (!name || registered.has(name) || BRIDGE_LOCAL_COMMANDS.has(name))
|
|
889
|
+
continue;
|
|
890
|
+
registered.add(name);
|
|
891
|
+
const description = typeof record?.description === "string" ? record.description : undefined;
|
|
892
|
+
const source = typeof record?.source === "string" ? record.source : "worker";
|
|
893
|
+
try {
|
|
894
|
+
pi.registerCommand(name, {
|
|
895
|
+
description: `[worker ${source}] ${description ?? ""}`.trim(),
|
|
896
|
+
handler: async (args) => {
|
|
897
|
+
const text = `/${name}${args ? ` ${args}` : ""}`;
|
|
898
|
+
appendTranscript(state, "You", text);
|
|
899
|
+
try {
|
|
900
|
+
const result = await runRunPiCommandViaServer(options.context, options.runId, text);
|
|
901
|
+
const message = typeof result.message === "string" ? result.message : "worker command accepted";
|
|
902
|
+
appendTranscript(state, "System", message);
|
|
903
|
+
if (state.nativeStream)
|
|
904
|
+
ctx.ui.notify(message, "info");
|
|
905
|
+
} catch (error) {
|
|
906
|
+
reportBridgeError(ctx, state, error instanceof Error ? error.message : String(error));
|
|
907
|
+
}
|
|
908
|
+
updatePiUi(ctx, state);
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
} catch {}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
async function connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands) {
|
|
884
915
|
const ready = await waitForWorkerReady(options, ctx, state);
|
|
885
916
|
if (!ready)
|
|
886
917
|
return;
|
|
@@ -935,6 +966,7 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
935
966
|
const record = recordOf(command);
|
|
936
967
|
return typeof record?.name === "string" ? [`/${record.name}`] : [];
|
|
937
968
|
});
|
|
969
|
+
registerDaemonCommandsNatively(pi, options, ctx, state, commands, registeredDaemonCommands);
|
|
938
970
|
catchupDone = true;
|
|
939
971
|
for (const payload of buffered.splice(0))
|
|
940
972
|
applyEnvelope(ctx, state, payload);
|
|
@@ -1062,6 +1094,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
1062
1094
|
};
|
|
1063
1095
|
if (options.initialMessageSent)
|
|
1064
1096
|
appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
|
|
1097
|
+
const registeredDaemonCommands = new Set;
|
|
1065
1098
|
let nativePiUiContextAvailable = false;
|
|
1066
1099
|
pi.on("user_bash", (event) => {
|
|
1067
1100
|
state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
|
|
@@ -1094,7 +1127,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
1094
1127
|
});
|
|
1095
1128
|
return { consume: true };
|
|
1096
1129
|
});
|
|
1097
|
-
connectWorkerStream(options, ctx, state).catch((error) => {
|
|
1130
|
+
connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands).catch((error) => {
|
|
1098
1131
|
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
1099
1132
|
updatePiUi(ctx, state);
|
|
1100
1133
|
});
|
|
@@ -1141,8 +1174,6 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
1141
1174
|
"--no-tools",
|
|
1142
1175
|
"--no-builtin-tools",
|
|
1143
1176
|
"--no-skills",
|
|
1144
|
-
"--no-prompt-templates",
|
|
1145
|
-
"--no-themes",
|
|
1146
1177
|
"--no-context-files",
|
|
1147
1178
|
"--no-approve"
|
|
1148
1179
|
], {
|
|
@@ -32,6 +32,9 @@ Usage: ${usage}`);
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// packages/cli/src/commands/server.ts
|
|
36
|
+
import { resolveRigServerCommand } from "@rig/runtime/local-server";
|
|
37
|
+
|
|
35
38
|
// packages/cli/src/commands/_authority-runs.ts
|
|
36
39
|
import {
|
|
37
40
|
readAuthorityRun,
|
|
@@ -483,7 +486,8 @@ async function executeServer(context, args, options) {
|
|
|
483
486
|
const authTokenResult = takeOption(pending, "--auth-token");
|
|
484
487
|
pending = authTokenResult.rest;
|
|
485
488
|
requireNoExtraArgs(pending, "rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
|
|
486
|
-
const
|
|
489
|
+
const serverCommand = resolveRigServerCommand(context.projectRoot);
|
|
490
|
+
const commandParts = [serverCommand.command, ...serverCommand.commandArgs, "start"];
|
|
487
491
|
if (hostResult.value) {
|
|
488
492
|
commandParts.push("--host", hostResult.value);
|
|
489
493
|
}
|
|
@@ -506,7 +510,8 @@ async function executeServer(context, args, options) {
|
|
|
506
510
|
const eventResult = takeOption(pending, "--event");
|
|
507
511
|
pending = eventResult.rest;
|
|
508
512
|
requireNoExtraArgs(pending, "rig server notify-test [--event <type>]");
|
|
509
|
-
const
|
|
513
|
+
const serverCommand = resolveRigServerCommand(context.projectRoot);
|
|
514
|
+
const commandParts = [serverCommand.command, ...serverCommand.commandArgs, "notify-test"];
|
|
510
515
|
if (eventResult.value) {
|
|
511
516
|
commandParts.push("--event", eventResult.value);
|
|
512
517
|
}
|
|
@@ -314,6 +314,7 @@ ${acceptance}` : null,
|
|
|
314
314
|
${sourceContractLines.join(`
|
|
315
315
|
`)}` : null,
|
|
316
316
|
scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
|
|
317
|
+
readPriorPrProgressForPrompt(input.projectRoot, input.taskId),
|
|
317
318
|
initialPrompt ? `Additional operator guidance:
|
|
318
319
|
${initialPrompt}` : null,
|
|
319
320
|
providerLines.length > 0 ? `Provider-specific runtime tooling:
|
|
@@ -323,6 +324,26 @@ ${providerLines.join(" ")}` : null,
|
|
|
323
324
|
|
|
324
325
|
`);
|
|
325
326
|
}
|
|
327
|
+
function readPriorPrProgressForPrompt(projectRoot, taskId) {
|
|
328
|
+
for (const candidate of [
|
|
329
|
+
resolve3(projectRoot, ".worktrees", taskId, "artifacts", taskId, "pr-state.json"),
|
|
330
|
+
resolve3(projectRoot, "artifacts", taskId, "pr-state.json")
|
|
331
|
+
]) {
|
|
332
|
+
try {
|
|
333
|
+
const raw = JSON.parse(readFileSync(candidate, "utf8"));
|
|
334
|
+
const entries = Array.isArray(raw) ? raw : [raw];
|
|
335
|
+
const first = entries.find((entry) => entry && typeof entry === "object" && typeof entry.url === "string");
|
|
336
|
+
if (!first)
|
|
337
|
+
continue;
|
|
338
|
+
return [
|
|
339
|
+
`Prior progress exists for this task: PR ${first.url}${first.branch ? ` (branch ${first.branch})` : ""} was opened by an earlier run.`,
|
|
340
|
+
"Check its current state first (diff, checks, review). If the work is already complete and checks are green,",
|
|
341
|
+
"run `rig-agent completion-verification` to merge and close instead of re-implementing anything."
|
|
342
|
+
].join(" ");
|
|
343
|
+
} catch {}
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
326
347
|
function firstPromptString(...values) {
|
|
327
348
|
for (const value of values) {
|
|
328
349
|
const trimmed = typeof value === "string" ? value.trim() : "";
|