@h-rig/cli 0.0.6-alpha.45 → 0.0.6-alpha.46

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.
@@ -146,7 +146,8 @@ function readRepoConnection(projectRoot) {
146
146
  return {
147
147
  selected,
148
148
  project: typeof record.project === "string" ? record.project : undefined,
149
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
149
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
150
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
150
151
  };
151
152
  }
152
153
  function writeRepoConnection(projectRoot, state) {
@@ -157,13 +158,19 @@ function resolveSelectedConnection(projectRoot, options = {}) {
157
158
  if (!repo)
158
159
  return null;
159
160
  if (repo.selected === "local")
160
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
161
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
161
162
  const global = readGlobalConnections(options);
162
163
  const connection = global.connections[repo.selected];
163
164
  if (!connection) {
164
165
  throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
165
166
  }
166
- return { alias: repo.selected, connection };
167
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
168
+ }
169
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
170
+ const repo = readRepoConnection(projectRoot);
171
+ if (!repo)
172
+ return;
173
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
167
174
  }
168
175
 
169
176
  // packages/cli/src/commands/_cli-format.ts
@@ -374,17 +381,21 @@ async function ensureServerForCli(projectRoot) {
374
381
  try {
375
382
  const selected = resolveSelectedConnection(projectRoot);
376
383
  if (selected?.connection.kind === "remote") {
384
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
385
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
377
386
  return {
378
387
  baseUrl: selected.connection.baseUrl,
379
- authToken: readGitHubBearerTokenForRemote(projectRoot),
380
- connectionKind: "remote"
388
+ authToken,
389
+ connectionKind: "remote",
390
+ serverProjectRoot
381
391
  };
382
392
  }
383
393
  const connection = await ensureLocalRigServerConnection(projectRoot);
384
394
  return {
385
395
  baseUrl: connection.baseUrl,
386
396
  authToken: connection.authToken,
387
- connectionKind: "local"
397
+ connectionKind: "local",
398
+ serverProjectRoot: resolve2(projectRoot)
388
399
  };
389
400
  } catch (error) {
390
401
  if (error instanceof Error) {
@@ -393,6 +404,28 @@ async function ensureServerForCli(projectRoot) {
393
404
  throw error;
394
405
  }
395
406
  }
407
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
408
+ const repo = readRepoConnection(projectRoot);
409
+ const slug = repo?.project?.trim();
410
+ if (!slug)
411
+ return null;
412
+ try {
413
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
414
+ headers: mergeHeaders(undefined, authToken)
415
+ });
416
+ if (!response.ok)
417
+ return null;
418
+ const payload = await response.json();
419
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
420
+ const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
421
+ const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
422
+ if (path)
423
+ writeRepoServerProjectRoot(projectRoot, path);
424
+ return path;
425
+ } catch {
426
+ return null;
427
+ }
428
+ }
396
429
  function mergeHeaders(headers, authToken) {
397
430
  const merged = new Headers(headers);
398
431
  if (authToken) {
@@ -417,9 +450,12 @@ function diagnosticMessage(payload) {
417
450
  }
418
451
  async function requestServerJson(context, pathname, init = {}) {
419
452
  const server = await ensureServerForCli(context.projectRoot);
453
+ const headers = mergeHeaders(init.headers, server.authToken);
454
+ if (server.serverProjectRoot)
455
+ headers.set("x-rig-project-root", server.serverProjectRoot);
420
456
  const response = await fetch(`${server.baseUrl}${pathname}`, {
421
457
  ...init,
422
- headers: mergeHeaders(init.headers, server.authToken)
458
+ headers
423
459
  });
424
460
  const text = await response.text();
425
461
  const payload = text.trim().length > 0 ? (() => {
@@ -199,6 +199,11 @@ function readJsonFile(path) {
199
199
  throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
200
200
  }
201
201
  }
202
+ function writeJsonFile(path, value) {
203
+ mkdirSync(dirname(path), { recursive: true });
204
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}
205
+ `, "utf8");
206
+ }
202
207
  function normalizeConnection(value) {
203
208
  if (!value || typeof value !== "object" || Array.isArray(value))
204
209
  return null;
@@ -239,21 +244,31 @@ function readRepoConnection(projectRoot) {
239
244
  return {
240
245
  selected,
241
246
  project: typeof record.project === "string" ? record.project : undefined,
242
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
247
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
248
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
243
249
  };
244
250
  }
251
+ function writeRepoConnection(projectRoot, state) {
252
+ writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
253
+ }
245
254
  function resolveSelectedConnection(projectRoot, options = {}) {
246
255
  const repo = readRepoConnection(projectRoot);
247
256
  if (!repo)
248
257
  return null;
249
258
  if (repo.selected === "local")
250
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
259
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
251
260
  const global = readGlobalConnections(options);
252
261
  const connection = global.connections[repo.selected];
253
262
  if (!connection) {
254
263
  throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
255
264
  }
256
- return { alias: repo.selected, connection };
265
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
266
+ }
267
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
268
+ const repo = readRepoConnection(projectRoot);
269
+ if (!repo)
270
+ return;
271
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
257
272
  }
258
273
 
259
274
  // packages/cli/src/commands/_server-client.ts
@@ -289,17 +304,21 @@ async function ensureServerForCli(projectRoot) {
289
304
  try {
290
305
  const selected = resolveSelectedConnection(projectRoot);
291
306
  if (selected?.connection.kind === "remote") {
307
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
308
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
292
309
  return {
293
310
  baseUrl: selected.connection.baseUrl,
294
- authToken: readGitHubBearerTokenForRemote(projectRoot),
295
- connectionKind: "remote"
311
+ authToken,
312
+ connectionKind: "remote",
313
+ serverProjectRoot
296
314
  };
297
315
  }
298
316
  const connection = await ensureLocalRigServerConnection(projectRoot);
299
317
  return {
300
318
  baseUrl: connection.baseUrl,
301
319
  authToken: connection.authToken,
302
- connectionKind: "local"
320
+ connectionKind: "local",
321
+ serverProjectRoot: resolve4(projectRoot)
303
322
  };
304
323
  } catch (error) {
305
324
  if (error instanceof Error) {
@@ -308,6 +327,28 @@ async function ensureServerForCli(projectRoot) {
308
327
  throw error;
309
328
  }
310
329
  }
330
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
331
+ const repo = readRepoConnection(projectRoot);
332
+ const slug = repo?.project?.trim();
333
+ if (!slug)
334
+ return null;
335
+ try {
336
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
337
+ headers: mergeHeaders(undefined, authToken)
338
+ });
339
+ if (!response.ok)
340
+ return null;
341
+ const payload = await response.json();
342
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
343
+ const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
344
+ const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
345
+ if (path)
346
+ writeRepoServerProjectRoot(projectRoot, path);
347
+ return path;
348
+ } catch {
349
+ return null;
350
+ }
351
+ }
311
352
  function mergeHeaders(headers, authToken) {
312
353
  const merged = new Headers(headers);
313
354
  if (authToken) {
@@ -332,9 +373,12 @@ function diagnosticMessage(payload) {
332
373
  }
333
374
  async function requestServerJson(context, pathname, init = {}) {
334
375
  const server = await ensureServerForCli(context.projectRoot);
376
+ const headers = mergeHeaders(init.headers, server.authToken);
377
+ if (server.serverProjectRoot)
378
+ headers.set("x-rig-project-root", server.serverProjectRoot);
335
379
  const response = await fetch(`${server.baseUrl}${pathname}`, {
336
380
  ...init,
337
- headers: mergeHeaders(init.headers, server.authToken)
381
+ headers
338
382
  });
339
383
  const text = await response.text();
340
384
  const payload = text.trim().length > 0 ? (() => {
@@ -418,6 +418,11 @@ function readJsonFile(path) {
418
418
  throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
419
419
  }
420
420
  }
421
+ function writeJsonFile3(path, value) {
422
+ mkdirSync(dirname(path), { recursive: true });
423
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}
424
+ `, "utf8");
425
+ }
421
426
  function normalizeConnection(value) {
422
427
  if (!value || typeof value !== "object" || Array.isArray(value))
423
428
  return null;
@@ -458,21 +463,31 @@ function readRepoConnection(projectRoot) {
458
463
  return {
459
464
  selected,
460
465
  project: typeof record.project === "string" ? record.project : undefined,
461
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
466
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
467
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
462
468
  };
463
469
  }
470
+ function writeRepoConnection(projectRoot, state) {
471
+ writeJsonFile3(resolveRepoConnectionPath(projectRoot), state);
472
+ }
464
473
  function resolveSelectedConnection(projectRoot, options = {}) {
465
474
  const repo = readRepoConnection(projectRoot);
466
475
  if (!repo)
467
476
  return null;
468
477
  if (repo.selected === "local")
469
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
478
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
470
479
  const global = readGlobalConnections(options);
471
480
  const connection = global.connections[repo.selected];
472
481
  if (!connection) {
473
482
  throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
474
483
  }
475
- return { alias: repo.selected, connection };
484
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
485
+ }
486
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
487
+ const repo = readRepoConnection(projectRoot);
488
+ if (!repo)
489
+ return;
490
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
476
491
  }
477
492
 
478
493
  // packages/cli/src/commands/_server-client.ts
@@ -505,17 +520,21 @@ async function ensureServerForCli(projectRoot) {
505
520
  try {
506
521
  const selected = resolveSelectedConnection(projectRoot);
507
522
  if (selected?.connection.kind === "remote") {
523
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
524
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
508
525
  return {
509
526
  baseUrl: selected.connection.baseUrl,
510
- authToken: readGitHubBearerTokenForRemote(projectRoot),
511
- connectionKind: "remote"
527
+ authToken,
528
+ connectionKind: "remote",
529
+ serverProjectRoot
512
530
  };
513
531
  }
514
532
  const connection = await ensureLocalRigServerConnection(projectRoot);
515
533
  return {
516
534
  baseUrl: connection.baseUrl,
517
535
  authToken: connection.authToken,
518
- connectionKind: "local"
536
+ connectionKind: "local",
537
+ serverProjectRoot: resolve5(projectRoot)
519
538
  };
520
539
  } catch (error) {
521
540
  if (error instanceof Error) {
@@ -524,6 +543,28 @@ async function ensureServerForCli(projectRoot) {
524
543
  throw error;
525
544
  }
526
545
  }
546
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
547
+ const repo = readRepoConnection(projectRoot);
548
+ const slug = repo?.project?.trim();
549
+ if (!slug)
550
+ return null;
551
+ try {
552
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
553
+ headers: mergeHeaders(undefined, authToken)
554
+ });
555
+ if (!response.ok)
556
+ return null;
557
+ const payload = await response.json();
558
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
559
+ const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
560
+ const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
561
+ if (path)
562
+ writeRepoServerProjectRoot(projectRoot, path);
563
+ return path;
564
+ } catch {
565
+ return null;
566
+ }
567
+ }
527
568
  function mergeHeaders(headers, authToken) {
528
569
  const merged = new Headers(headers);
529
570
  if (authToken) {
@@ -548,9 +589,12 @@ function diagnosticMessage(payload) {
548
589
  }
549
590
  async function requestServerJson(context, pathname, init = {}) {
550
591
  const server = await ensureServerForCli(context.projectRoot);
592
+ const headers = mergeHeaders(init.headers, server.authToken);
593
+ if (server.serverProjectRoot)
594
+ headers.set("x-rig-project-root", server.serverProjectRoot);
551
595
  const response = await fetch(`${server.baseUrl}${pathname}`, {
552
596
  ...init,
553
- headers: mergeHeaders(init.headers, server.authToken)
597
+ headers
554
598
  });
555
599
  const text = await response.text();
556
600
  const payload = text.trim().length > 0 ? (() => {
@@ -128,6 +128,11 @@ function readJsonFile(path) {
128
128
  throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
129
129
  }
130
130
  }
131
+ function writeJsonFile2(path, value) {
132
+ mkdirSync(dirname(path), { recursive: true });
133
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}
134
+ `, "utf8");
135
+ }
131
136
  function normalizeConnection(value) {
132
137
  if (!value || typeof value !== "object" || Array.isArray(value))
133
138
  return null;
@@ -168,21 +173,31 @@ function readRepoConnection(projectRoot) {
168
173
  return {
169
174
  selected,
170
175
  project: typeof record.project === "string" ? record.project : undefined,
171
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
176
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
177
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
172
178
  };
173
179
  }
180
+ function writeRepoConnection(projectRoot, state) {
181
+ writeJsonFile2(resolveRepoConnectionPath(projectRoot), state);
182
+ }
174
183
  function resolveSelectedConnection(projectRoot, options = {}) {
175
184
  const repo = readRepoConnection(projectRoot);
176
185
  if (!repo)
177
186
  return null;
178
187
  if (repo.selected === "local")
179
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
188
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
180
189
  const global = readGlobalConnections(options);
181
190
  const connection = global.connections[repo.selected];
182
191
  if (!connection) {
183
192
  throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
184
193
  }
185
- return { alias: repo.selected, connection };
194
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
195
+ }
196
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
197
+ const repo = readRepoConnection(projectRoot);
198
+ if (!repo)
199
+ return;
200
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
186
201
  }
187
202
 
188
203
  // packages/cli/src/commands/_server-client.ts
@@ -218,17 +233,21 @@ async function ensureServerForCli(projectRoot) {
218
233
  try {
219
234
  const selected = resolveSelectedConnection(projectRoot);
220
235
  if (selected?.connection.kind === "remote") {
236
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
237
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
221
238
  return {
222
239
  baseUrl: selected.connection.baseUrl,
223
- authToken: readGitHubBearerTokenForRemote(projectRoot),
224
- connectionKind: "remote"
240
+ authToken,
241
+ connectionKind: "remote",
242
+ serverProjectRoot
225
243
  };
226
244
  }
227
245
  const connection = await ensureLocalRigServerConnection(projectRoot);
228
246
  return {
229
247
  baseUrl: connection.baseUrl,
230
248
  authToken: connection.authToken,
231
- connectionKind: "local"
249
+ connectionKind: "local",
250
+ serverProjectRoot: resolve2(projectRoot)
232
251
  };
233
252
  } catch (error) {
234
253
  if (error instanceof Error) {
@@ -237,6 +256,28 @@ async function ensureServerForCli(projectRoot) {
237
256
  throw error;
238
257
  }
239
258
  }
259
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
260
+ const repo = readRepoConnection(projectRoot);
261
+ const slug = repo?.project?.trim();
262
+ if (!slug)
263
+ return null;
264
+ try {
265
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
266
+ headers: mergeHeaders(undefined, authToken)
267
+ });
268
+ if (!response.ok)
269
+ return null;
270
+ const payload = await response.json();
271
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
272
+ const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
273
+ const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
274
+ if (path)
275
+ writeRepoServerProjectRoot(projectRoot, path);
276
+ return path;
277
+ } catch {
278
+ return null;
279
+ }
280
+ }
240
281
  function appendTaskFilterParams(url, filters) {
241
282
  if (filters.assignee)
242
283
  url.searchParams.set("assignee", filters.assignee);
@@ -271,9 +312,12 @@ function diagnosticMessage(payload) {
271
312
  }
272
313
  async function requestServerJson(context, pathname, init = {}) {
273
314
  const server = await ensureServerForCli(context.projectRoot);
315
+ const headers = mergeHeaders(init.headers, server.authToken);
316
+ if (server.serverProjectRoot)
317
+ headers.set("x-rig-project-root", server.serverProjectRoot);
274
318
  const response = await fetch(`${server.baseUrl}${pathname}`, {
275
319
  ...init,
276
- headers: mergeHeaders(init.headers, server.authToken)
320
+ headers
277
321
  });
278
322
  const text = await response.text();
279
323
  const payload = text.trim().length > 0 ? (() => {
@@ -419,6 +463,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
419
463
  url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
420
464
  if (server.authToken)
421
465
  url.searchParams.set("token", server.authToken);
466
+ if (server.serverProjectRoot)
467
+ url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
422
468
  return url.toString();
423
469
  }
424
470
  async function submitTaskRunViaServer(context, input) {
@@ -2694,7 +2694,8 @@ function readRepoConnection(projectRoot) {
2694
2694
  return {
2695
2695
  selected,
2696
2696
  project: typeof record.project === "string" ? record.project : undefined,
2697
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
2697
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
2698
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
2698
2699
  };
2699
2700
  }
2700
2701
  function writeRepoConnection(projectRoot, state) {
@@ -2705,13 +2706,19 @@ function resolveSelectedConnection(projectRoot, options = {}) {
2705
2706
  if (!repo)
2706
2707
  return null;
2707
2708
  if (repo.selected === "local")
2708
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
2709
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
2709
2710
  const global = readGlobalConnections(options);
2710
2711
  const connection = global.connections[repo.selected];
2711
2712
  if (!connection) {
2712
2713
  throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
2713
2714
  }
2714
- return { alias: repo.selected, connection };
2715
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
2716
+ }
2717
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
2718
+ const repo = readRepoConnection(projectRoot);
2719
+ if (!repo)
2720
+ return;
2721
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
2715
2722
  }
2716
2723
 
2717
2724
  // packages/cli/src/commands/_server-client.ts
@@ -2752,17 +2759,21 @@ async function ensureServerForCli(projectRoot) {
2752
2759
  try {
2753
2760
  const selected = resolveSelectedConnection(projectRoot);
2754
2761
  if (selected?.connection.kind === "remote") {
2762
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
2763
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
2755
2764
  return {
2756
2765
  baseUrl: selected.connection.baseUrl,
2757
- authToken: readGitHubBearerTokenForRemote(projectRoot),
2758
- connectionKind: "remote"
2766
+ authToken,
2767
+ connectionKind: "remote",
2768
+ serverProjectRoot
2759
2769
  };
2760
2770
  }
2761
2771
  const connection = await ensureLocalRigServerConnection(projectRoot);
2762
2772
  return {
2763
2773
  baseUrl: connection.baseUrl,
2764
2774
  authToken: connection.authToken,
2765
- connectionKind: "local"
2775
+ connectionKind: "local",
2776
+ serverProjectRoot: resolve10(projectRoot)
2766
2777
  };
2767
2778
  } catch (error) {
2768
2779
  if (error instanceof Error) {
@@ -2771,6 +2782,28 @@ async function ensureServerForCli(projectRoot) {
2771
2782
  throw error;
2772
2783
  }
2773
2784
  }
2785
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
2786
+ const repo = readRepoConnection(projectRoot);
2787
+ const slug = repo?.project?.trim();
2788
+ if (!slug)
2789
+ return null;
2790
+ try {
2791
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
2792
+ headers: mergeHeaders(undefined, authToken)
2793
+ });
2794
+ if (!response.ok)
2795
+ return null;
2796
+ const payload = await response.json();
2797
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
2798
+ const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
2799
+ const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
2800
+ if (path)
2801
+ writeRepoServerProjectRoot(projectRoot, path);
2802
+ return path;
2803
+ } catch {
2804
+ return null;
2805
+ }
2806
+ }
2774
2807
  function appendTaskFilterParams(url, filters) {
2775
2808
  if (filters.assignee)
2776
2809
  url.searchParams.set("assignee", filters.assignee);
@@ -2805,9 +2838,12 @@ function diagnosticMessage(payload) {
2805
2838
  }
2806
2839
  async function requestServerJson(context, pathname, init = {}) {
2807
2840
  const server = await ensureServerForCli(context.projectRoot);
2841
+ const headers = mergeHeaders(init.headers, server.authToken);
2842
+ if (server.serverProjectRoot)
2843
+ headers.set("x-rig-project-root", server.serverProjectRoot);
2808
2844
  const response = await fetch(`${server.baseUrl}${pathname}`, {
2809
2845
  ...init,
2810
- headers: mergeHeaders(init.headers, server.authToken)
2846
+ headers
2811
2847
  });
2812
2848
  const text2 = await response.text();
2813
2849
  const payload = text2.trim().length > 0 ? (() => {
@@ -2914,22 +2950,12 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
2914
2950
  if (!switched) {
2915
2951
  throw new CliError2(`Rig server did not accept project-root switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no response")}).`, 1);
2916
2952
  }
2917
- while (Date.now() < deadline) {
2918
- try {
2919
- const status = await requestServerJson(context, "/api/server/status");
2920
- if (status && typeof status === "object" && !Array.isArray(status)) {
2921
- const record = status;
2922
- if (record.projectRoot === projectRoot) {
2923
- return { ok: true, switched, status: record };
2924
- }
2925
- lastError = `server projectRoot=${String(record.projectRoot ?? "unknown")}`;
2926
- }
2927
- } catch (error) {
2928
- lastError = error;
2929
- }
2930
- await sleep(pollMs);
2953
+ const record = switched && typeof switched === "object" && !Array.isArray(switched) ? switched : {};
2954
+ if (record.ok === true) {
2955
+ writeRepoServerProjectRoot(context.projectRoot, projectRoot);
2956
+ return { ok: true, switched: record };
2931
2957
  }
2932
- throw new CliError2(`Rig server did not switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no status")}).`, 1);
2958
+ throw new CliError2(`Rig server rejected project-root scope ${projectRoot}: ${String(record.message ?? record.error ?? "unknown error")}`, 1);
2933
2959
  }
2934
2960
  async function listRunsViaServer(context, options = {}) {
2935
2961
  const url = new URL("http://rig.local/api/runs");
@@ -3061,6 +3087,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
3061
3087
  url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
3062
3088
  if (server.authToken)
3063
3089
  url.searchParams.set("token", server.authToken);
3090
+ if (server.serverProjectRoot)
3091
+ url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
3064
3092
  return url.toString();
3065
3093
  }
3066
3094
  async function submitTaskRunViaServer(context, input) {