@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.
@@ -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
- async function connectWorkerStream(options, ctx, state) {
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
- throw new CliError2(`No runs found for ${requiredTask}.`);
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 = resolve(resolveAuthorityRunDir(context.projectRoot, latestRun.runId), "logs.jsonl");
68
- if (!existsSync(logsPath)) {
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) => existsSync(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 (!existsSync(failed)) {
351
+ if (!existsSync3(failed)) {
136
352
  console.log("No failures recorded.");
137
353
  } else {
138
- process.stdout.write(readFileSync(failed, "utf-8"));
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 = resolve(resolveHarnessPaths(context.projectRoot).logsDir, "audit.jsonl");
156
- if (!existsSync(auditPath)) {
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 = readFileSync(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
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
  }
@@ -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
- async function connectWorkerStream(options, ctx, state) {
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 commandParts = ["rig-server", "start"];
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 commandParts = ["rig-server", "notify-test"];
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() : "";