@h-rig/cli-surface-plugin 0.0.6-alpha.157 → 0.0.6-alpha.158

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.
Files changed (112) hide show
  1. package/dist/src/app/drone-ui.d.ts +0 -11
  2. package/dist/src/app/drone-ui.js +0 -114
  3. package/dist/src/commands/_async-ui.d.ts +1 -1
  4. package/dist/src/commands/_cli-format.d.ts +0 -29
  5. package/dist/src/commands/_cli-format.js +59 -113
  6. package/dist/src/commands/_connection-state.d.ts +6 -33
  7. package/dist/src/commands/_connection-state.js +654 -138
  8. package/dist/src/commands/_doctor-checks.d.ts +2 -5
  9. package/dist/src/commands/_doctor-checks.js +10 -9
  10. package/dist/src/commands/_help-catalog.d.ts +2 -1
  11. package/dist/src/commands/_help-catalog.js +654 -7
  12. package/dist/src/commands/_inprocess-services.d.ts +5 -5
  13. package/dist/src/commands/_inprocess-services.js +1 -1
  14. package/dist/src/commands/_parsers.js +651 -12
  15. package/dist/src/commands/_paths.d.ts +0 -2
  16. package/dist/src/commands/_paths.js +2 -10
  17. package/dist/src/commands/_pi-install.d.ts +2 -12
  18. package/dist/src/commands/_pi-install.js +3 -36
  19. package/dist/src/commands/_policy.js +659 -20
  20. package/dist/src/commands/agent.d.ts +1 -1
  21. package/dist/src/commands/agent.js +675 -24
  22. package/dist/src/commands/config.d.ts +1 -1
  23. package/dist/src/commands/config.js +656 -21
  24. package/dist/src/commands/dist.d.ts +1 -1
  25. package/dist/src/commands/dist.js +828 -102
  26. package/dist/src/commands/doctor.d.ts +1 -1
  27. package/dist/src/commands/doctor.js +658 -12
  28. package/dist/src/commands/github.d.ts +1 -1
  29. package/dist/src/commands/github.js +658 -19
  30. package/dist/src/commands/inbox.d.ts +12 -8
  31. package/dist/src/commands/inbox.js +741 -22
  32. package/dist/src/commands/init.d.ts +17 -19
  33. package/dist/src/commands/init.js +836 -306
  34. package/dist/src/commands/inspect.d.ts +5 -6
  35. package/dist/src/commands/inspect.js +754 -42
  36. package/dist/src/commands/pi.d.ts +1 -1
  37. package/dist/src/commands/pi.js +655 -16
  38. package/dist/src/commands/plugin.d.ts +9 -9
  39. package/dist/src/commands/plugin.js +652 -13
  40. package/dist/src/commands/profile-and-review.d.ts +1 -1
  41. package/dist/src/commands/profile-and-review.js +655 -16
  42. package/dist/src/commands/queue.d.ts +1 -1
  43. package/dist/src/commands/queue.js +871 -12
  44. package/dist/src/commands/remote-client.d.ts +152 -0
  45. package/dist/src/commands/remote-client.js +475 -0
  46. package/dist/src/commands/remote.d.ts +1 -1
  47. package/dist/src/commands/remote.js +1100 -29
  48. package/dist/src/commands/repo-git-harness.d.ts +1 -1
  49. package/dist/src/commands/repo-git-harness.js +2321 -47
  50. package/dist/src/commands/run.d.ts +10 -6
  51. package/dist/src/commands/run.js +830 -50
  52. package/dist/src/commands/server.d.ts +1 -1
  53. package/dist/src/commands/server.js +649 -11
  54. package/dist/src/commands/setup.d.ts +2 -2
  55. package/dist/src/commands/setup.js +829 -18
  56. package/dist/src/commands/stats.d.ts +2 -4
  57. package/dist/src/commands/stats.js +1299 -20
  58. package/dist/src/commands/test.d.ts +1 -1
  59. package/dist/src/commands/test.js +648 -9
  60. package/dist/src/commands/triage.d.ts +2 -3
  61. package/dist/src/commands/triage.js +657 -11
  62. package/dist/src/commands/workspace.d.ts +1 -1
  63. package/dist/src/commands/workspace.js +1280 -15
  64. package/dist/src/control-plane/agent-binary-build.d.ts +9 -0
  65. package/dist/src/control-plane/agent-binary-build.js +88 -0
  66. package/dist/src/control-plane/embedded-native-assets.d.ts +7 -0
  67. package/dist/src/control-plane/embedded-native-assets.js +6 -0
  68. package/dist/src/control-plane/guard.d.ts +17 -0
  69. package/dist/src/control-plane/guard.js +684 -0
  70. package/dist/src/control-plane/harness-cli.d.ts +12 -0
  71. package/dist/src/control-plane/harness-cli.js +1623 -0
  72. package/dist/src/control-plane/native/git-ops.d.ts +67 -0
  73. package/dist/src/control-plane/native/git-ops.js +1381 -0
  74. package/dist/src/control-plane/native/github-auth-env.d.ts +1 -0
  75. package/dist/src/control-plane/native/github-auth-env.js +21 -0
  76. package/dist/src/control-plane/native/host-git.d.ts +4 -0
  77. package/dist/src/control-plane/native/host-git.js +51 -0
  78. package/dist/src/control-plane/priority-queue.d.ts +22 -0
  79. package/dist/src/control-plane/priority-queue.js +212 -0
  80. package/dist/src/control-plane/rigfig.d.ts +9 -0
  81. package/dist/src/control-plane/rigfig.js +70 -0
  82. package/dist/src/control-plane/scope.d.ts +3 -0
  83. package/dist/src/control-plane/scope.js +58 -0
  84. package/dist/src/control-plane/setup-status.d.ts +44 -0
  85. package/dist/src/control-plane/setup-status.js +164 -0
  86. package/dist/src/control-plane/task-data.d.ts +2 -0
  87. package/dist/src/control-plane/task-data.js +12 -0
  88. package/dist/src/control-plane/workspace-ops.d.ts +79 -0
  89. package/dist/src/control-plane/workspace-ops.js +639 -0
  90. package/dist/src/help-catalog-data.d.ts +7 -0
  91. package/dist/src/help-catalog-data.js +660 -0
  92. package/dist/src/kernel-dispatch.js +1 -3
  93. package/dist/src/plugin.js +10072 -30
  94. package/dist/src/runner.d.ts +7 -9
  95. package/dist/src/runner.js +750 -30
  96. package/package.json +12 -13
  97. package/dist/src/commands/_json-output.d.ts +0 -11
  98. package/dist/src/commands/_json-output.js +0 -54
  99. package/dist/src/commands/_pi-frontend.d.ts +0 -35
  100. package/dist/src/commands/_pi-frontend.js +0 -64
  101. package/dist/src/commands/_run-driver-helpers.d.ts +0 -26
  102. package/dist/src/commands/_run-driver-helpers.js +0 -132
  103. package/dist/src/commands/task-run-driver.d.ts +0 -93
  104. package/dist/src/commands/task-run-driver.js +0 -136
  105. package/dist/src/commands/task.d.ts +0 -46
  106. package/dist/src/commands/task.js +0 -555
  107. package/dist/src/provider-model.d.ts +0 -34
  108. package/dist/src/provider-model.js +0 -56
  109. package/dist/src/rig-config-package-deps.d.ts +0 -10
  110. package/dist/src/rig-config-package-deps.js +0 -272
  111. package/dist/src/version.d.ts +0 -8
  112. package/dist/src/version.js +0 -47
@@ -15,83 +15,13 @@ var __export = (target, all) => {
15
15
  };
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
- // packages/cli-surface-plugin/src/commands/_spinner.ts
19
- function createTtySpinner(input) {
20
- const output = input.output ?? process.stdout;
21
- const isTty = output.isTTY === true;
22
- const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
23
- let label = input.label;
24
- let frame = 0;
25
- let paused = false;
26
- let stopped = false;
27
- let lastPrintedLabel = "";
28
- const render = () => {
29
- if (stopped || paused)
30
- return;
31
- if (!isTty) {
32
- if (label !== lastPrintedLabel) {
33
- output.write(`${label}
34
- `);
35
- lastPrintedLabel = label;
36
- }
37
- return;
38
- }
39
- frame = (frame + 1) % frames.length;
40
- const glyph = frames[frame] ?? frames[0] ?? "";
41
- output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
42
- };
43
- const clearLine = () => {
44
- if (isTty)
45
- output.write("\r\x1B[2K");
46
- };
47
- render();
48
- const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
49
- return {
50
- setLabel(next) {
51
- label = next;
52
- render();
53
- },
54
- pause() {
55
- paused = true;
56
- clearLine();
57
- },
58
- resume() {
59
- if (stopped)
60
- return;
61
- paused = false;
62
- render();
63
- },
64
- stop(finalLine) {
65
- if (stopped)
66
- return;
67
- stopped = true;
68
- if (timer)
69
- clearInterval(timer);
70
- clearLine();
71
- if (finalLine)
72
- output.write(`${finalLine}
73
- `);
74
- }
75
- };
76
- }
77
- var SPINNER_FRAMES;
78
- var init__spinner = __esm(() => {
79
- SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
80
- });
81
-
82
18
  // packages/cli-surface-plugin/src/app/drone-ui.ts
83
19
  var exports_drone_ui = {};
84
20
  __export(exports_drone_ui, {
85
- droneWarn: () => droneWarn,
86
21
  droneText: () => droneText,
87
- droneStep: () => droneStep,
88
- droneSpinner: () => droneSpinner,
89
22
  droneSelect: () => droneSelect,
90
23
  droneOutro: () => droneOutro,
91
- droneNote: () => droneNote,
92
24
  droneIntro: () => droneIntro,
93
- droneInfo: () => droneInfo,
94
- droneError: () => droneError,
95
25
  droneConfirm: () => droneConfirm,
96
26
  droneCancel: () => droneCancel
97
27
  });
@@ -111,12 +41,6 @@ function fg(hex) {
111
41
  function bold(text) {
112
42
  return `\x1B[1m${text}\x1B[22m`;
113
43
  }
114
- function renderMicroDroneFrame(frame) {
115
- const tick = Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame));
116
- const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
117
- const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
118
- return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
119
- }
120
44
  function hairline(width = Math.min(process.stdout.columns ?? 80, 100)) {
121
45
  return ink4("\u2500".repeat(Math.max(10, width)));
122
46
  }
@@ -130,58 +54,19 @@ function droneOutro(text) {
130
54
  console.log(` ${accent("\u25C6")} ${ink2(text)}`);
131
55
  console.log("");
132
56
  }
133
- function droneNote(message, title) {
134
- if (title)
135
- console.log(` ${accentDim("\u25C7")} ${bold(ink2(title))}`);
136
- for (const line of message.split(`
137
- `))
138
- console.log(` ${ink4("\u2502")} ${line}`);
139
- }
140
- function droneStep(text) {
141
- console.log(` ${accent("\u203A")} ${ink(text)}`);
142
- }
143
- function droneInfo(text) {
144
- console.log(` ${cyan("\xB7")} ${ink2(text)}`);
145
- }
146
- function droneWarn(text) {
147
- console.log(` ${yellow("\u25B2")} ${ink2(text)}`);
148
- }
149
- function droneError(text) {
150
- console.log(` ${red("\u2716")} ${ink2(text)}`);
151
- }
152
57
  function droneCancel(text) {
153
58
  console.log(` ${red("\u2716")} ${ink3(text)}`);
154
59
  }
155
- function droneSpinner() {
156
- let active = null;
157
- return {
158
- start(message) {
159
- active = createTtySpinner({
160
- label: message,
161
- frames: MICRO_DRONE_FRAMES,
162
- styleFrame: renderMicroDroneFrame
163
- });
164
- },
165
- stop(message) {
166
- active?.stop(message ? ` ${accent("\u25C6")} ${ink2(message)}` : undefined);
167
- active = null;
168
- },
169
- error(message) {
170
- active?.stop(message ? ` ${red("\u2716")} ${ink2(message)}` : undefined);
171
- active = null;
172
- }
173
- };
174
- }
175
60
  async function runMiniTui(build) {
176
61
  const tui = new TUI(new ProcessTerminal);
177
62
  let settled = false;
178
- return await new Promise((resolve5) => {
63
+ return await new Promise((resolve6) => {
179
64
  const finish = (result) => {
180
65
  if (settled)
181
66
  return;
182
67
  settled = true;
183
68
  tui.stop();
184
- resolve5(result);
69
+ resolve6(result);
185
70
  };
186
71
  build(tui, finish);
187
72
  tui.start();
@@ -250,7 +135,6 @@ async function droneConfirm(input) {
250
135
  }
251
136
  var PALETTE, ink, ink2, ink3, ink4, accent, accentDim, cyan, red, yellow, MICRO_BLADES, EYE_FRAMES, MICRO_DRONE_FRAMES, DRONE_SYMBOLS, DRONE_SELECT_THEME;
252
137
  var init_drone_ui = __esm(() => {
253
- init__spinner();
254
138
  PALETTE = {
255
139
  ink: "#f2f3f6",
256
140
  ink2: "#aeb0ba",
@@ -300,23 +184,662 @@ var init_drone_ui = __esm(() => {
300
184
  });
301
185
 
302
186
  // packages/cli-surface-plugin/src/commands/init.ts
303
- import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
187
+ import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
304
188
  import { spawnSync } from "child_process";
305
- import { basename, resolve as resolve5 } from "path";
189
+ import { resolve as resolve6 } from "path";
306
190
 
307
191
  // packages/cli-surface-plugin/src/runner.ts
308
- import { EventBus } from "@rig/runtime/control-plane/runtime/events";
309
- import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
310
- import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
311
- import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
192
+ import { EventBus } from "@rig/core/runtime-events";
193
+ import { CliError as RuntimeCliError } from "@rig/contracts";
194
+
195
+ // packages/cli-surface-plugin/src/control-plane/guard.ts
196
+ import { optimizeNextInvocation } from "bun:jsc";
197
+ import { existsSync, readFileSync, statSync } from "fs";
198
+ import { resolve } from "path";
199
+
200
+ // packages/cli-surface-plugin/src/control-plane/scope.ts
201
+ import { getScopeRules } from "@rig/core/scope-rules";
202
+ var scopeRegexCache = new Map;
203
+ function unique(values) {
204
+ return [...new Set(values)];
205
+ }
206
+ function normalizeRelativeScopePath(inputPath) {
207
+ let normalized = inputPath.replace(/^\.\//, "");
208
+ const rules = getScopeRules();
209
+ if (rules?.stripPrefixes) {
210
+ for (const prefix of rules.stripPrefixes) {
211
+ if (normalized.startsWith(prefix)) {
212
+ normalized = normalized.slice(prefix.length);
213
+ }
214
+ }
215
+ }
216
+ return normalized;
217
+ }
218
+ function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
219
+ let normalized = inputPath.replace(/^\.\//, "");
220
+ if (normalized.startsWith(projectRoot + "/")) {
221
+ normalized = normalized.slice(projectRoot.length + 1);
222
+ }
223
+ if (normalized.startsWith(monorepoRoot + "/")) {
224
+ normalized = normalized.slice(monorepoRoot.length + 1);
225
+ }
226
+ return normalizeRelativeScopePath(normalized);
227
+ }
228
+ function scopeGlobToRegex(glob) {
229
+ const cached = scopeRegexCache.get(glob);
230
+ if (cached) {
231
+ return cached;
232
+ }
233
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*");
234
+ const compiled = new RegExp(`^${escaped}$`);
235
+ scopeRegexCache.set(glob, compiled);
236
+ return compiled;
237
+ }
238
+ function scopeMatches(path, scopes) {
239
+ const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
240
+ for (const scope of scopes) {
241
+ const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
242
+ for (const candidatePath of pathVariants) {
243
+ for (const candidateScope of scopeVariants) {
244
+ if (candidatePath === candidateScope || scopeGlobToRegex(candidateScope).test(candidatePath)) {
245
+ return true;
246
+ }
247
+ }
248
+ }
249
+ }
250
+ return false;
251
+ }
252
+
253
+ // packages/cli-surface-plugin/src/control-plane/guard.ts
254
+ import {
255
+ POLICY_VERSION
256
+ } from "@rig/contracts";
257
+ var DEFAULT_SCOPE = {
258
+ fail_closed: true,
259
+ harness_paths_exempt: true,
260
+ runtime_paths_exempt: true
261
+ };
262
+ var DEFAULT_SANDBOX = {
263
+ mode: "enforce",
264
+ network: true,
265
+ read_deny: [],
266
+ write_allow_from_runtime: true
267
+ };
268
+ var DEFAULT_ISOLATION = {
269
+ default_mode: "worktree",
270
+ repo_symlink_fallback: false,
271
+ strict_provisioning: true,
272
+ fail_closed_on_provision_error: true
273
+ };
274
+ var DEFAULT_COMPLETION = {
275
+ derive_checks_from_scope: true,
276
+ checks: [],
277
+ typescript_config_probe: ["tsconfig.json"],
278
+ eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
279
+ };
280
+ var DEFAULT_RUNTIME_IMAGE = {
281
+ deps: {
282
+ monorepo_install: false,
283
+ hp_next_install: false
284
+ },
285
+ plugins_require_binaries: true
286
+ };
287
+ var DEFAULT_RUNTIME_SNAPSHOT = {
288
+ enabled: true
289
+ };
290
+ function defaultPolicy() {
291
+ return {
292
+ version: POLICY_VERSION,
293
+ mode: "enforce",
294
+ scope: { ...DEFAULT_SCOPE },
295
+ rules: [],
296
+ sandbox: { ...DEFAULT_SANDBOX },
297
+ isolation: { ...DEFAULT_ISOLATION },
298
+ completion: { ...DEFAULT_COMPLETION },
299
+ runtime_image: {
300
+ deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
301
+ plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
302
+ },
303
+ runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
304
+ };
305
+ }
306
+ var policyCache = null;
307
+ var policyCachePath = null;
308
+ var seededPolicyConfig = null;
309
+ var compiledRegexCache = new Map;
310
+ function loadPolicy(projectRoot) {
311
+ if (seededPolicyConfig) {
312
+ return seededPolicyConfig;
313
+ }
314
+ const configPath = resolve(projectRoot, "rig/policy/policy.json");
315
+ if (!existsSync(configPath)) {
316
+ return defaultPolicy();
317
+ }
318
+ let mtimeMs;
319
+ try {
320
+ mtimeMs = statSync(configPath).mtimeMs;
321
+ } catch {
322
+ return defaultPolicy();
323
+ }
324
+ if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
325
+ return policyCache.config;
326
+ }
327
+ let parsed;
328
+ try {
329
+ parsed = JSON.parse(readFileSync(configPath, "utf-8"));
330
+ } catch {
331
+ return defaultPolicy();
332
+ }
333
+ const config = mergeWithDefaults(parsed);
334
+ policyCache = { mtimeMs, config };
335
+ policyCachePath = configPath;
336
+ return config;
337
+ }
338
+ function mergeWithDefaults(parsed) {
339
+ const base = defaultPolicy();
340
+ if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
341
+ base.mode = parsed.mode;
342
+ }
343
+ if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
344
+ const s = parsed.scope;
345
+ if (typeof s.fail_closed === "boolean")
346
+ base.scope.fail_closed = s.fail_closed;
347
+ if (typeof s.harness_paths_exempt === "boolean")
348
+ base.scope.harness_paths_exempt = s.harness_paths_exempt;
349
+ if (typeof s.runtime_paths_exempt === "boolean")
350
+ base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
351
+ }
352
+ if (Array.isArray(parsed.rules)) {
353
+ base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
354
+ }
355
+ if (Array.isArray(parsed.deny) && base.rules.length === 0) {
356
+ base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
357
+ }
358
+ if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
359
+ const sb = parsed.sandbox;
360
+ if (typeof sb.mode === "string" && isValidMode(sb.mode))
361
+ base.sandbox.mode = sb.mode;
362
+ if (typeof sb.network === "boolean")
363
+ base.sandbox.network = sb.network;
364
+ if (Array.isArray(sb.read_deny))
365
+ base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
366
+ if (typeof sb.write_allow_from_runtime === "boolean")
367
+ base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
368
+ }
369
+ if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
370
+ const iso = parsed.isolation;
371
+ if (iso.default_mode === "worktree")
372
+ base.isolation.default_mode = iso.default_mode;
373
+ if (typeof iso.repo_symlink_fallback === "boolean")
374
+ base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
375
+ if (typeof iso.strict_provisioning === "boolean")
376
+ base.isolation.strict_provisioning = iso.strict_provisioning;
377
+ if (typeof iso.fail_closed_on_provision_error === "boolean")
378
+ base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
379
+ }
380
+ if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
381
+ const comp = parsed.completion;
382
+ if (typeof comp.derive_checks_from_scope === "boolean")
383
+ base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
384
+ if (Array.isArray(comp.checks))
385
+ base.completion.checks = comp.checks.filter((v) => typeof v === "string");
386
+ if (Array.isArray(comp.typescript_config_probe))
387
+ base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
388
+ if (Array.isArray(comp.eslint_config_probe))
389
+ base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
390
+ }
391
+ if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
392
+ const runtimeImage = parsed.runtime_image;
393
+ if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
394
+ const deps = runtimeImage.deps;
395
+ if (typeof deps.monorepo_install === "boolean") {
396
+ base.runtime_image.deps.monorepo_install = deps.monorepo_install;
397
+ }
398
+ if (typeof deps.hp_next_install === "boolean") {
399
+ base.runtime_image.deps.hp_next_install = deps.hp_next_install;
400
+ }
401
+ }
402
+ if (typeof runtimeImage.plugins_require_binaries === "boolean") {
403
+ base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
404
+ }
405
+ }
406
+ if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
407
+ const runtimeSnapshot = parsed.runtime_snapshot;
408
+ if (typeof runtimeSnapshot.enabled === "boolean") {
409
+ base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
410
+ }
411
+ }
412
+ return base;
413
+ }
414
+ function isValidMode(value) {
415
+ return value === "off" || value === "observe" || value === "enforce";
416
+ }
417
+ function isValidRule(value) {
418
+ if (!value || typeof value !== "object" || Array.isArray(value))
419
+ return false;
420
+ const r = value;
421
+ return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
422
+ }
423
+ function migrateLegacyDeny(deny) {
424
+ const rules = [];
425
+ for (const entry of deny) {
426
+ if (typeof entry.id !== "string")
427
+ continue;
428
+ const match = {};
429
+ if (typeof entry.pattern === "string")
430
+ match.pattern = entry.pattern;
431
+ if (typeof entry.regex === "string")
432
+ match.regex = entry.regex;
433
+ if (!match.pattern && !match.regex)
434
+ continue;
435
+ rules.push({
436
+ id: entry.id,
437
+ category: "command",
438
+ match,
439
+ action: "block",
440
+ ...typeof entry.description === "string" ? { description: entry.description } : {}
441
+ });
442
+ }
443
+ return rules;
444
+ }
445
+ function precompilePolicyRuleRegexes(rules) {
446
+ return rules.map((rule) => {
447
+ const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
448
+ const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
449
+ return {
450
+ ...rule,
451
+ ...compiledRegex ? { compiledRegex } : {},
452
+ ...compiledUnlessRegex ? { compiledUnlessRegex } : {}
453
+ };
454
+ });
455
+ }
456
+ function getRegexUnsafeReason(pattern) {
457
+ if (pattern.length > 512) {
458
+ return "pattern exceeds max safe length (512 chars)";
459
+ }
460
+ if (/\\[1-9]/.test(pattern)) {
461
+ return "pattern uses backreferences";
462
+ }
463
+ if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
464
+ return "pattern contains nested quantifiers";
465
+ }
466
+ if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
467
+ return "pattern contains nested broad quantifiers";
468
+ }
469
+ return null;
470
+ }
471
+ function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
472
+ const cached = compiledRegexCache.get(pattern);
473
+ if (cached !== undefined) {
474
+ return cached ?? undefined;
475
+ }
476
+ const unsafeReason = getRegexUnsafeReason(pattern);
477
+ if (unsafeReason) {
478
+ if (logOnFailure) {
479
+ console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
480
+ }
481
+ compiledRegexCache.set(pattern, null);
482
+ return;
483
+ }
484
+ try {
485
+ const compiled = new RegExp(pattern);
486
+ compiledRegexCache.set(pattern, compiled);
487
+ return compiled;
488
+ } catch (error) {
489
+ if (logOnFailure) {
490
+ const message = error instanceof Error ? error.message : String(error);
491
+ console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
492
+ }
493
+ compiledRegexCache.set(pattern, null);
494
+ return;
495
+ }
496
+ }
497
+ function matchRule(rule, input) {
498
+ const { match } = rule;
499
+ if (match.pattern && input.includes(match.pattern)) {
500
+ return true;
501
+ }
502
+ if (match.regex) {
503
+ const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
504
+ if (!compiled) {
505
+ return false;
506
+ }
507
+ try {
508
+ return compiled.test(input);
509
+ } catch {
510
+ return false;
511
+ }
512
+ }
513
+ return false;
514
+ }
515
+ function matchRuleUnless(rule, command, taskId) {
516
+ if (!rule.unless)
517
+ return false;
518
+ if (rule.unless.regex) {
519
+ const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
520
+ if (!compiled) {
521
+ return false;
522
+ }
523
+ try {
524
+ if (compiled.test(command))
525
+ return true;
526
+ } catch {}
527
+ }
528
+ if (rule.unless.task_in && taskId) {
529
+ if (rule.unless.task_in.includes(taskId))
530
+ return true;
531
+ }
532
+ return false;
533
+ }
534
+ function resolveAction(mode, matched) {
535
+ if (matched.length === 0)
536
+ return "allow";
537
+ if (mode === "off")
538
+ return "allow";
539
+ if (mode === "observe")
540
+ return "warn";
541
+ return "block";
542
+ }
543
+ function resolveAbsolutePath(projectRoot, rawPath) {
544
+ if (rawPath.startsWith("/"))
545
+ return resolve(rawPath);
546
+ return resolve(projectRoot, rawPath);
547
+ }
548
+ function isHarnessPath(projectRoot, rawPath) {
549
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
550
+ const managedRoots = [
551
+ resolve(projectRoot, "rig"),
552
+ resolve(projectRoot, ".rig"),
553
+ resolve(projectRoot, "artifacts")
554
+ ];
555
+ return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
556
+ }
557
+ function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
558
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
559
+ if (taskWorkspace) {
560
+ const workspaceRigRoot = resolve(taskWorkspace, ".rig");
561
+ const workspaceArtifactsRoot = resolve(taskWorkspace, "artifacts");
562
+ if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
563
+ return true;
564
+ }
565
+ }
566
+ const runtimeRoot = resolve(projectRoot, ".rig/runtime/agents");
567
+ return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
568
+ }
569
+ function isTestFile(path) {
570
+ return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
571
+ }
572
+ function evaluate(context) {
573
+ const policy = loadPolicy(context.projectRoot);
574
+ switch (context.evaluation.type) {
575
+ case "tool-call":
576
+ return evaluateToolCall(policy, context);
577
+ case "command":
578
+ return evaluateCommand(policy, context);
579
+ case "content-write":
580
+ return evaluateContent(policy, context);
581
+ case "file-access":
582
+ return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
583
+ }
584
+ }
585
+ function evaluateScope(policy, context, filePath, access) {
586
+ const allowed = () => ({
587
+ allowed: true,
588
+ matchedRules: [],
589
+ action: "allow",
590
+ failClosed: false
591
+ });
592
+ if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
593
+ return allowed();
594
+ }
595
+ if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
596
+ return allowed();
597
+ }
598
+ if (!context.taskId) {
599
+ if (access === "write" && policy.scope.fail_closed) {
600
+ return {
601
+ allowed: false,
602
+ matchedRules: [],
603
+ action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
604
+ failClosed: true
605
+ };
606
+ }
607
+ return allowed();
608
+ }
609
+ const scopes = context.taskScopes || [];
610
+ if (scopes.length === 0) {
611
+ return allowed();
612
+ }
613
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
614
+ const absPath = resolve(filePath);
615
+ if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
616
+ const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
617
+ const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
618
+ return {
619
+ allowed: policy.mode !== "enforce",
620
+ matchedRules: matched2,
621
+ action: resolveAction(policy.mode, matched2),
622
+ failClosed: false
623
+ };
624
+ }
625
+ }
626
+ const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
627
+ let normalizedPath = filePath;
628
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
629
+ normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
630
+ }
631
+ normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
632
+ if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
633
+ return allowed();
634
+ }
635
+ const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
636
+ const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
637
+ return {
638
+ allowed: policy.mode !== "enforce",
639
+ matchedRules: matched,
640
+ action: resolveAction(policy.mode, matched),
641
+ failClosed: false
642
+ };
643
+ }
644
+ function evaluateCommand(policy, context) {
645
+ const evaluation = context.evaluation;
646
+ if (evaluation.type !== "command") {
647
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
648
+ }
649
+ const command = evaluation.command;
650
+ const matchedRules = [];
651
+ for (const rule of policy.rules) {
652
+ if (rule.category !== "command")
653
+ continue;
654
+ if (!matchRule(rule, command))
655
+ continue;
656
+ if (matchRuleUnless(rule, command, context.taskId))
657
+ continue;
658
+ matchedRules.push({
659
+ id: rule.id,
660
+ category: rule.category,
661
+ ...rule.description !== undefined ? { description: rule.description } : {},
662
+ reason: rule.description || `Matched rule ${rule.id}`
663
+ });
664
+ }
665
+ const writeTarget = extractWriteTarget(command);
666
+ if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
667
+ const scopeResult = evaluateScope(policy, context, writeTarget, "write");
668
+ if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
669
+ matchedRules.push(...scopeResult.matchedRules);
670
+ }
671
+ }
672
+ const action = resolveAction(policy.mode, matchedRules);
673
+ return {
674
+ allowed: action !== "block",
675
+ matchedRules,
676
+ action,
677
+ failClosed: false
678
+ };
679
+ }
680
+ function extractWriteTarget(command) {
681
+ const redirect = command.match(/>>?\s+([^\s;|&]+)/);
682
+ if (redirect?.[1])
683
+ return redirect[1];
684
+ const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
685
+ if (tee?.[2])
686
+ return tee[2];
687
+ return "";
688
+ }
689
+ function evaluateContent(policy, context) {
690
+ const evaluation = context.evaluation;
691
+ if (evaluation.type !== "content-write") {
692
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
693
+ }
694
+ const { content, file_path } = evaluation;
695
+ const matchedRules = [];
696
+ const scopeResult = evaluateScope(policy, context, file_path, "write");
697
+ if (scopeResult.matchedRules.length > 0) {
698
+ matchedRules.push(...scopeResult.matchedRules);
699
+ }
700
+ for (const rule of policy.rules) {
701
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
702
+ continue;
703
+ if (rule.applies_to === "test-files" && !isTestFile(file_path))
704
+ continue;
705
+ if (!matchRule(rule, content))
706
+ continue;
707
+ if (matchRuleUnless(rule, content, context.taskId))
708
+ continue;
709
+ matchedRules.push({
710
+ id: rule.id,
711
+ category: rule.category,
712
+ ...rule.description !== undefined ? { description: rule.description } : {},
713
+ reason: rule.description || `Matched rule ${rule.id}`
714
+ });
715
+ }
716
+ const action = resolveAction(policy.mode, matchedRules);
717
+ return {
718
+ allowed: action !== "block",
719
+ matchedRules,
720
+ action,
721
+ failClosed: false
722
+ };
723
+ }
724
+ function evaluateToolCall(policy, context) {
725
+ const evaluation = context.evaluation;
726
+ if (evaluation.type !== "tool-call") {
727
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
728
+ }
729
+ const { tool_name, tool_input } = evaluation;
730
+ const allMatched = [];
731
+ const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
732
+ for (const fp of filePaths) {
733
+ const access = isWriteTool(tool_name) ? "write" : "read";
734
+ const scopeResult = evaluateScope(policy, context, fp, access);
735
+ if (scopeResult.matchedRules.length > 0) {
736
+ allMatched.push(...scopeResult.matchedRules);
737
+ }
738
+ }
739
+ const content = extractContentFromToolInput(tool_input);
740
+ if (content) {
741
+ const filePath = filePaths[0] || "";
742
+ const contentContext = {
743
+ ...context,
744
+ evaluation: { type: "content-write", file_path: filePath, content }
745
+ };
746
+ const contentPolicy = loadPolicy(context.projectRoot);
747
+ for (const rule of contentPolicy.rules) {
748
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
749
+ continue;
750
+ if (rule.applies_to === "test-files" && !isTestFile(filePath))
751
+ continue;
752
+ if (!matchRule(rule, content))
753
+ continue;
754
+ if (matchRuleUnless(rule, content, context.taskId))
755
+ continue;
756
+ allMatched.push({
757
+ id: rule.id,
758
+ category: rule.category,
759
+ ...rule.description !== undefined ? { description: rule.description } : {},
760
+ reason: rule.description || `Matched rule ${rule.id}`
761
+ });
762
+ }
763
+ }
764
+ if (tool_name === "Bash") {
765
+ const command = String(tool_input.command || tool_input.cmd || "");
766
+ if (command) {
767
+ const cmdContext = {
768
+ ...context,
769
+ evaluation: { type: "command", command }
770
+ };
771
+ const cmdResult = evaluateCommand(policy, cmdContext);
772
+ if (cmdResult.matchedRules.length > 0) {
773
+ allMatched.push(...cmdResult.matchedRules);
774
+ }
775
+ }
776
+ }
777
+ const seen = new Set;
778
+ const deduplicated = [];
779
+ for (const rule of allMatched) {
780
+ if (!seen.has(rule.id)) {
781
+ seen.add(rule.id);
782
+ deduplicated.push(rule);
783
+ }
784
+ }
785
+ const action = resolveAction(policy.mode, deduplicated);
786
+ return {
787
+ allowed: action !== "block",
788
+ matchedRules: deduplicated,
789
+ action,
790
+ failClosed: false
791
+ };
792
+ }
793
+ function isWriteTool(toolName) {
794
+ return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
795
+ }
796
+ function extractFilePathsFromToolInput(toolName, input) {
797
+ const paths = [];
798
+ const add = (value) => {
799
+ if (typeof value === "string" && value.trim()) {
800
+ paths.push(value.trim());
801
+ }
802
+ };
803
+ if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
804
+ add(input.file_path);
805
+ add(input.path);
806
+ } else if (toolName === "Glob") {
807
+ add(input.path);
808
+ } else if (toolName === "Grep") {
809
+ add(input.path);
810
+ } else {
811
+ add(input.file_path);
812
+ add(input.path);
813
+ }
814
+ return paths;
815
+ }
816
+ function extractContentFromToolInput(input) {
817
+ if (typeof input.content === "string")
818
+ return input.content;
819
+ if (typeof input.new_string === "string")
820
+ return input.new_string;
821
+ return "";
822
+ }
823
+ var guardHotPathPrimed = false;
824
+ function primeGuardHotPaths() {
825
+ if (guardHotPathPrimed) {
826
+ return;
827
+ }
828
+ guardHotPathPrimed = true;
829
+ try {
830
+ optimizeNextInvocation(matchRule);
831
+ optimizeNextInvocation(evaluate);
832
+ } catch {}
833
+ }
834
+ primeGuardHotPaths();
312
835
 
836
+ // packages/cli-surface-plugin/src/control-plane/agent-binary-build.ts
837
+ import { runtimeProvisioningEnv } from "@rig/core/runtime-provisioning-env";
838
+
839
+ // packages/cli-surface-plugin/src/runner.ts
313
840
  class CliError extends RuntimeCliError {
314
- hint;
315
841
  constructor(message, exitCode = 1, options = {}) {
316
- super(message, exitCode);
317
- if (options.hint?.trim()) {
318
- this.hint = options.hint.trim();
319
- }
842
+ super(message, exitCode, options);
320
843
  }
321
844
  }
322
845
  function takeFlag(args, flag) {
@@ -352,27 +875,84 @@ function takeOption(args, option) {
352
875
  return { value, rest };
353
876
  }
354
877
 
355
- // packages/cli-surface-plugin/src/commands/init.ts
356
- import { buildRigInitConfigSource } from "@rig/init-plugin";
878
+ // packages/cli-surface-plugin/src/control-plane/rigfig.ts
879
+ import { existsSync as existsSync2, mkdirSync, writeFileSync } from "fs";
880
+ import { resolve as resolve2 } from "path";
881
+ import { stringify as stringifyToml } from "smol-toml";
882
+ import { getStandardPluginsResolver } from "@rig/core/embedded-plugins";
883
+ function rigfigConfigPath(projectRoot) {
884
+ return resolve2(projectRoot, ".rig", "rigfig.toml");
885
+ }
886
+ function isPlainObject(value) {
887
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
888
+ }
889
+ function deepMerge(base, overlay) {
890
+ const out = { ...base };
891
+ for (const [key, value] of Object.entries(overlay)) {
892
+ const previous = out[key];
893
+ out[key] = isPlainObject(previous) && isPlainObject(value) ? deepMerge(previous, value) : value;
894
+ }
895
+ return out;
896
+ }
897
+ function composeDeclarativeConfig(plugins, context) {
898
+ let merged = {};
899
+ for (const plugin of plugins) {
900
+ const fragment = plugin.contributes?.config?.defaults?.(context);
901
+ if (isPlainObject(fragment))
902
+ merged = deepMerge(merged, fragment);
903
+ }
904
+ return merged;
905
+ }
906
+ var HEADER = [
907
+ "# Declarative rig configuration \u2014 the happy path.",
908
+ "# Plain DATA, composed from the standard plugins' defaults. No top-level",
909
+ "# rig.config.ts and no @h-rig/* install: the global rig binary resolves",
910
+ "# plugins from its embedded standard collection. rig created this for you;",
911
+ "# edit it freely (each plugin owns the section it needs).",
912
+ "",
913
+ ""
914
+ ].join(`
915
+ `);
916
+ function writeRigfigConfig(projectRoot, config) {
917
+ mkdirSync(resolve2(projectRoot, ".rig"), { recursive: true });
918
+ const path = rigfigConfigPath(projectRoot);
919
+ writeFileSync(path, `${HEADER}${stringifyToml(config)}
920
+ `, "utf-8");
921
+ return path;
922
+ }
923
+ function composeAndWriteRigfig(projectRoot, options = {}) {
924
+ const resolver = getStandardPluginsResolver();
925
+ if (!resolver) {
926
+ throw new Error("Cannot write rig config: embedded standard plugins are not registered (seed wiring error).");
927
+ }
928
+ const context = options.repoSlug ? { projectRoot, repoSlug: options.repoSlug } : { projectRoot };
929
+ let composed = composeDeclarativeConfig(resolver(), context);
930
+ if (options.overlay) {
931
+ if (isPlainObject(options.overlay.taskSource) && "taskSource" in composed)
932
+ delete composed.taskSource;
933
+ composed = deepMerge(composed, options.overlay);
934
+ }
935
+ return writeRigfigConfig(projectRoot, composed);
936
+ }
357
937
 
358
938
  // packages/cli-surface-plugin/src/commands/_connection-state.ts
359
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
360
- import { dirname, resolve } from "path";
939
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
940
+ import { dirname, resolve as resolve3 } from "path";
361
941
  function resolveRepoConnectionPath(projectRoot) {
362
- return resolve(projectRoot, ".rig", "state", "connection.json");
942
+ return resolve3(projectRoot, ".rig", "state", "connection.json");
363
943
  }
364
944
  function readJsonFile(path) {
365
- if (!existsSync(path))
945
+ if (!existsSync3(path))
366
946
  return null;
367
947
  try {
368
- return JSON.parse(readFileSync(path, "utf8"));
948
+ return JSON.parse(readFileSync2(path, "utf8"));
369
949
  } catch (error) {
370
950
  throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
371
951
  }
372
952
  }
373
953
  function writeJsonFile(path, value) {
374
- mkdirSync(dirname(path), { recursive: true });
375
- writeFileSync(path, `${JSON.stringify(value, null, 2)}
954
+ mkdirSync2(dirname(path), { recursive: true });
955
+ writeFileSync2(path, `${JSON.stringify(value, null, 2)}
376
956
  `, "utf8");
377
957
  }
378
958
  function readRepoConnection(projectRoot) {
@@ -397,11 +977,11 @@ function writeRepoConnection(projectRoot, state) {
397
977
  }
398
978
 
399
979
  // packages/cli-surface-plugin/src/commands/init.ts
400
- import { upsertManagedRemoteEndpoint } from "@rig/runtime/control-plane/remote-config";
980
+ import { upsertManagedRemoteEndpoint } from "@rig/core/remote-config";
401
981
  import { loadConfig } from "@rig/core/load-config";
402
982
 
403
983
  // packages/cli-surface-plugin/src/commands/_inprocess-services.ts
404
- import { resolve as resolve2 } from "path";
984
+ import { resolve as resolve4 } from "path";
405
985
  import {
406
986
  beginGitHubDeviceFlow,
407
987
  checkGitHubRepoPermissions,
@@ -411,7 +991,7 @@ import {
411
991
  resolveGitHubAuthStatus,
412
992
  resolveProjectStatusField,
413
993
  saveGitHubTokenForProject
414
- } from "@rig/github-provider-plugin";
994
+ } from "@rig/github-lib";
415
995
  var scopedGitHubBearerTokens = new Map;
416
996
  function cleanToken(value) {
417
997
  const trimmed = value?.trim();
@@ -424,11 +1004,11 @@ function oauthClientId() {
424
1004
  return cleanToken(process.env.RIG_GITHUB_OAUTH_CLIENT_ID);
425
1005
  }
426
1006
  function tokenForProject(projectRoot, override) {
427
- const scoped = scopedGitHubBearerTokens.get(resolve2(projectRoot));
1007
+ const scoped = scopedGitHubBearerTokens.get(resolve4(projectRoot));
428
1008
  return cleanToken(override) ?? scoped ?? createGitHubAuthStore(projectRoot).readToken() ?? cleanToken(process.env.RIG_GITHUB_TOKEN);
429
1009
  }
430
1010
  function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
431
- scopedGitHubBearerTokens.set(resolve2(projectRoot ?? process.cwd()), cleanToken(token));
1011
+ scopedGitHubBearerTokens.set(resolve4(projectRoot ?? process.cwd()), cleanToken(token));
432
1012
  }
433
1013
  async function getGitHubAuthStatusInProcess(context) {
434
1014
  return { ok: true, ...resolveGitHubAuthStatus({ projectRoot: context.projectRoot, oauthConfigured: Boolean(oauthClientId()) }) };
@@ -495,12 +1075,12 @@ async function requestGitHubAuthJsonInProcess(context, pathname, init = {}) {
495
1075
  }
496
1076
 
497
1077
  // packages/cli-surface-plugin/src/commands/_pi-install.ts
498
- import { existsSync as existsSync2 } from "fs";
1078
+ import { existsSync as existsSync4 } from "fs";
499
1079
  import { homedir } from "os";
500
- import { resolve as resolve3 } from "path";
1080
+ import { resolve as resolve5 } from "path";
501
1081
  var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
502
1082
  async function defaultCommandRunner(command, options = {}) {
503
- const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
1083
+ const proc = Bun.spawn(command, { ...options.cwd !== undefined ? { cwd: options.cwd } : {}, stdout: "pipe", stderr: "pipe" });
504
1084
  const [stdout, stderr, exitCode] = await Promise.all([
505
1085
  new Response(proc.stdout).text(),
506
1086
  new Response(proc.stderr).text(),
@@ -509,11 +1089,11 @@ async function defaultCommandRunner(command, options = {}) {
509
1089
  return { exitCode, stdout, stderr };
510
1090
  }
511
1091
  function resolvePiRigExtensionPath(homeDir) {
512
- return resolve3(homeDir, ".pi", "agent", "extensions", "pi-rig");
1092
+ return resolve5(homeDir, ".pi", "agent", "extensions", "pi-rig");
513
1093
  }
514
- function resolvePiRigPackageSource(projectRoot, exists = existsSync2) {
515
- const localPackage = resolve3(projectRoot, "packages", "pi-rig");
516
- if (exists(resolve3(localPackage, "package.json")))
1094
+ function resolvePiRigPackageSource(projectRoot, exists = existsSync4) {
1095
+ const localPackage = resolve5(projectRoot, "packages", "pi-rig");
1096
+ if (exists(resolve5(localPackage, "package.json")))
517
1097
  return localPackage;
518
1098
  return `npm:${PI_RIG_PACKAGE_NAME}`;
519
1099
  }
@@ -585,7 +1165,7 @@ async function checkPiRigInstall(input = {}) {
585
1165
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
586
1166
  const hasPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
587
1167
  ${piListResult.stderr}`);
588
- const hasLegacyBridgeScaffold = !hasPiRig && (input.exists ?? existsSync2)(extensionPath);
1168
+ const hasLegacyBridgeScaffold = !hasPiRig && (input.exists ?? existsSync4)(extensionPath);
589
1169
  return {
590
1170
  extensionPath,
591
1171
  pi: {
@@ -605,7 +1185,7 @@ ${piListResult.stderr}`);
605
1185
  async function ensurePiRigInstalled(input) {
606
1186
  const home = resolvePiHomeDir(input.homeDir);
607
1187
  if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
608
- const status = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
1188
+ const status = await checkPiRigInstall({ homeDir: home, ...input.commandRunner ? { commandRunner: input.commandRunner } : {} });
609
1189
  return { ...status, installedPath: status.extensionPath };
610
1190
  }
611
1191
  const runner = input.commandRunner ?? defaultCommandRunner;
@@ -623,10 +1203,17 @@ async function ensurePiRigInstalled(input) {
623
1203
  }
624
1204
 
625
1205
  // packages/cli-surface-plugin/src/commands/_doctor-checks.ts
626
- import { countDoctorFailures } from "@rig/contracts";
627
- import { loadDoctorService } from "@rig/runtime/control-plane/doctor-service-port";
1206
+ import { DOCTOR } from "@rig/contracts";
1207
+ import { defineCapability } from "@rig/core/capability";
1208
+ import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
1209
+ function doctorLevelToStatus(level) {
1210
+ return level === "ok" ? "pass" : level;
1211
+ }
1212
+ function countDoctorFailures(checks) {
1213
+ return checks.filter((entry) => (entry.status ?? doctorLevelToStatus(entry.level ?? "warn")) === "fail").length;
1214
+ }
628
1215
  async function runRigDoctorChecks(options) {
629
- const service = await loadDoctorService(options.projectRoot);
1216
+ const service = await loadCapabilityForRoot(options.projectRoot, defineCapability(DOCTOR));
630
1217
  if (!service) {
631
1218
  return [
632
1219
  {
@@ -642,64 +1229,6 @@ async function runRigDoctorChecks(options) {
642
1229
  return service.runDoctorChecks(options);
643
1230
  }
644
1231
 
645
- // packages/cli-surface-plugin/src/version.ts
646
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
647
- import { dirname as dirname2, resolve as resolve4 } from "path";
648
- import { fileURLToPath } from "url";
649
- import { readBuildConfig } from "@rig/runtime/build-time-config";
650
- var SOURCE_CLI_VERSION = "0.0.0-alpha.1";
651
- var DEV_CLI_VERSION = "0.0.0-dev";
652
- function resolveInstalledCliVersion() {
653
- const buildConfig = readBuildConfig();
654
- const envVersion = process.env.RIG_CLI_VERSION?.trim();
655
- const buildVersion = buildConfig.RIG_CLI_VERSION?.trim();
656
- let version = envVersion || buildVersion || "";
657
- if (!version) {
658
- try {
659
- const execPath = process.execPath || "";
660
- const moduleDir = dirname2(fileURLToPath(import.meta.url));
661
- const candidates = [
662
- execPath ? resolve4(dirname2(execPath), "..", "manifest.json") : "",
663
- resolve4(moduleDir, "..", "package.json"),
664
- resolve4(moduleDir, "..", "..", "package.json"),
665
- resolve4(process.cwd(), "packages/cli/package.json")
666
- ].filter(Boolean);
667
- for (const candidate of candidates) {
668
- if (!existsSync3(candidate))
669
- continue;
670
- const parsed = JSON.parse(readFileSync2(candidate, "utf-8"));
671
- const candidateVersion = parsed.version?.trim();
672
- if (candidateVersion) {
673
- version = candidateVersion;
674
- break;
675
- }
676
- }
677
- } catch {}
678
- }
679
- return version || DEV_CLI_VERSION;
680
- }
681
- function isPublishedCliVersion(version) {
682
- const normalized = version.trim();
683
- return normalized.length > 0 && normalized !== SOURCE_CLI_VERSION && normalized !== DEV_CLI_VERSION;
684
- }
685
-
686
- // packages/cli-surface-plugin/src/rig-config-package-deps.ts
687
- var REQUIRED_RIG_CONFIG_PACKAGE_NAMES = ["core", "standard-plugin"];
688
- function resolveRigConfigPackageVersion() {
689
- const explicit = process.env.RIG_CONFIG_PACKAGE_VERSION?.trim();
690
- if (explicit)
691
- return explicit;
692
- const installed = resolveInstalledCliVersion();
693
- return isPublishedCliVersion(installed) ? installed : "latest";
694
- }
695
- function rigConfigDevDependencies() {
696
- const version = resolveRigConfigPackageVersion();
697
- return Object.fromEntries(REQUIRED_RIG_CONFIG_PACKAGE_NAMES.map((name) => [`@rig/${name}`, rigPackageSpec(name, version)]));
698
- }
699
- function rigPackageSpec(packageName, version = resolveRigConfigPackageVersion()) {
700
- return `npm:@h-rig/${packageName}@${version}`;
701
- }
702
-
703
1232
  // packages/cli-surface-plugin/src/commands/init.ts
704
1233
  function parseRepoSlugFromRemote(remoteUrl) {
705
1234
  const trimmed = remoteUrl.trim();
@@ -719,20 +1248,20 @@ function parseRepoSlug(value) {
719
1248
  return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
720
1249
  }
721
1250
  function ensureRigPrivateDirs(projectRoot) {
722
- const rigDir = resolve5(projectRoot, ".rig");
723
- mkdirSync2(resolve5(rigDir, "state"), { recursive: true });
724
- mkdirSync2(resolve5(rigDir, "logs"), { recursive: true });
725
- mkdirSync2(resolve5(rigDir, "runs"), { recursive: true });
726
- mkdirSync2(resolve5(rigDir, "tmp"), { recursive: true });
727
- mkdirSync2(resolve5(projectRoot, "artifacts"), { recursive: true });
728
- const taskConfigPath = resolve5(rigDir, "task-config.json");
729
- if (!existsSync4(taskConfigPath))
730
- writeFileSync2(taskConfigPath, `{}
1251
+ const rigDir = resolve6(projectRoot, ".rig");
1252
+ mkdirSync3(resolve6(rigDir, "state"), { recursive: true });
1253
+ mkdirSync3(resolve6(rigDir, "logs"), { recursive: true });
1254
+ mkdirSync3(resolve6(rigDir, "runs"), { recursive: true });
1255
+ mkdirSync3(resolve6(rigDir, "tmp"), { recursive: true });
1256
+ mkdirSync3(resolve6(projectRoot, "artifacts"), { recursive: true });
1257
+ const taskConfigPath = resolve6(rigDir, "task-config.json");
1258
+ if (!existsSync5(taskConfigPath))
1259
+ writeFileSync3(taskConfigPath, `{}
731
1260
  `, "utf-8");
732
1261
  }
733
1262
  function ensureGitignoreEntries(projectRoot) {
734
- const path = resolve5(projectRoot, ".gitignore");
735
- const existing = existsSync4(path) ? readFileSync3(path, "utf8") : "";
1263
+ const path = resolve6(projectRoot, ".gitignore");
1264
+ const existing = existsSync5(path) ? readFileSync3(path, "utf8") : "";
736
1265
  const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
737
1266
  const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
738
1267
  if (missing.length === 0)
@@ -744,36 +1273,53 @@ function ensureGitignoreEntries(projectRoot) {
744
1273
  `)}
745
1274
  `, "utf8");
746
1275
  }
747
- function ensureRigConfigPackageDependencies(projectRoot) {
748
- const path = resolve5(projectRoot, "package.json");
749
- const existing = existsSync4(path) ? JSON.parse(readFileSync3(path, "utf8")) : {};
750
- const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
751
- for (const [name, spec] of Object.entries(rigConfigDevDependencies())) {
752
- devDependencies[name] = spec;
753
- }
754
- const next = {
755
- ...existsSync4(path) ? existing : { name: "rig-project", private: true },
756
- devDependencies
757
- };
758
- writeFileSync2(path, `${JSON.stringify(next, null, 2)}
759
- `, "utf8");
760
- }
761
- function applyGitHubProjectConfig(source, options) {
1276
+ function buildGitHubProjectsOverlay(options) {
762
1277
  if (!options.githubProject || options.githubProject === "off")
763
- return source;
764
- const projectId = JSON.stringify(options.githubProject);
765
- const statusFieldId = JSON.stringify(options.githubProjectStatusField ?? "Status");
766
- const statuses = options.githubProjectStatuses && Object.keys(options.githubProjectStatuses).length > 0 ? `
767
- statuses: ${JSON.stringify(options.githubProjectStatuses, null, 8).replace(/\n/g, `
768
- `)},` : "";
769
- return source.replace(` projects: { enabled: false },`, [
770
- ` projects: {`,
771
- ` enabled: true,`,
772
- ` projectId: ${projectId},`,
773
- ` statusFieldId: ${statusFieldId},${statuses}`,
774
- ` },`
775
- ].join(`
776
- `));
1278
+ return null;
1279
+ const projects = {
1280
+ enabled: true,
1281
+ projectId: options.githubProject,
1282
+ statusFieldId: options.githubProjectStatusField ?? "Status"
1283
+ };
1284
+ if (options.githubProjectStatuses && Object.keys(options.githubProjectStatuses).length > 0) {
1285
+ projects.statuses = options.githubProjectStatuses;
1286
+ }
1287
+ return { github: { projects } };
1288
+ }
1289
+ function mergeOverlay(base, overlay) {
1290
+ if (!overlay)
1291
+ return base;
1292
+ const out = { ...base };
1293
+ for (const [key, value] of Object.entries(overlay)) {
1294
+ const prev = out[key];
1295
+ out[key] = prev && typeof prev === "object" && !Array.isArray(prev) && value && typeof value === "object" && !Array.isArray(value) ? mergeOverlay(prev, value) : value;
1296
+ }
1297
+ return out;
1298
+ }
1299
+ function configAlreadyExists(projectRoot) {
1300
+ return existsSync5(resolve6(projectRoot, "rig.config.ts")) || existsSync5(resolve6(projectRoot, "rig.config.mts")) || existsSync5(resolve6(projectRoot, "rig.config.json")) || existsSync5(rigfigConfigPath(projectRoot));
1301
+ }
1302
+ function buildWizardOverlay(input) {
1303
+ let overlay = {};
1304
+ if (input.taskSource.kind === "github-issues") {
1305
+ const taskSource = {
1306
+ kind: "github-issues",
1307
+ owner: input.taskSource.owner,
1308
+ repo: input.taskSource.repo,
1309
+ state: "open"
1310
+ };
1311
+ if (input.taskSource.assignee?.trim())
1312
+ taskSource.options = { assignee: input.taskSource.assignee.trim() };
1313
+ overlay.taskSource = taskSource;
1314
+ } else {
1315
+ overlay.taskSource = { kind: "files", path: input.taskSource.path };
1316
+ }
1317
+ if (input.sshTarget?.trim()) {
1318
+ overlay = mergeOverlay(overlay, { runtime: { server: { sshTarget: input.sshTarget.trim() } } });
1319
+ }
1320
+ if (input.projects)
1321
+ overlay = mergeOverlay(overlay, input.projects);
1322
+ return overlay;
777
1323
  }
778
1324
  function checkoutForInit(projectRoot, options) {
779
1325
  if (options.server === "remote") {
@@ -964,7 +1510,7 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
964
1510
  ...projects.map((project) => ({
965
1511
  value: String(project.id),
966
1512
  label: `${String(project.title ?? "Untitled project")} (#${String(project.number ?? "?")})`,
967
- hint: typeof project.url === "string" ? project.url : undefined
1513
+ ...typeof project.url === "string" ? { hint: project.url } : {}
968
1514
  })),
969
1515
  { value: "manual", label: "Enter ProjectV2 id manually" }
970
1516
  ]
@@ -995,12 +1541,12 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
995
1541
  return {
996
1542
  githubProject: projectId,
997
1543
  githubProjectStatusField: fieldId,
998
- githubProjectStatuses: Object.keys(statuses).length > 0 ? statuses : undefined,
1544
+ ...Object.keys(statuses).length > 0 ? { githubProjectStatuses: statuses } : {},
999
1545
  ...activeToken ? { githubToken: activeToken } : {}
1000
1546
  };
1001
1547
  }
1002
1548
  function sleep(ms) {
1003
- return new Promise((resolve6) => setTimeout(resolve6, ms));
1549
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
1004
1550
  }
1005
1551
  function positiveIntFromEnv(name, fallback) {
1006
1552
  const value = Number.parseInt(process.env[name] ?? "", 10);
@@ -1042,24 +1588,18 @@ function runLocalFilesInit(context, options) {
1042
1588
  ensureRigPrivateDirs(projectRoot);
1043
1589
  ensureGitignoreEntries(projectRoot);
1044
1590
  writeRepoConnection(projectRoot, { selected: "local", linkedAt: new Date().toISOString() });
1045
- const configTsPath = resolve5(projectRoot, "rig.config.ts");
1046
- const configExists = existsSync4(configTsPath) || existsSync4(resolve5(projectRoot, "rig.config.json"));
1047
- if (configExists && !options.repair) {
1591
+ if (configAlreadyExists(projectRoot) && !options.repair) {
1048
1592
  if (context.outputMode !== "json")
1049
- console.log("rig.config already exists; leaving it unchanged. Pass --repair to rewrite it.");
1593
+ console.log("rig config already exists; leaving it unchanged. Pass --repair to rewrite it.");
1050
1594
  } else {
1051
- const projectName = basename(projectRoot) || "rig-project";
1052
- writeFileSync2(configTsPath, buildRigInitConfigSource({
1053
- projectName,
1054
- taskSource: { kind: "files", path: "tasks" },
1055
- useStandardPlugin: true
1056
- }), "utf-8");
1057
- }
1058
- ensureRigConfigPackageDependencies(projectRoot);
1059
- const tasksDir = resolve5(projectRoot, "tasks");
1060
- if (!existsSync4(tasksDir)) {
1061
- mkdirSync2(tasksDir, { recursive: true });
1062
- writeFileSync2(resolve5(tasksDir, "T-1.json"), `${JSON.stringify({ id: "T-1", title: "My first Rig task", body: "Describe the change you want an agent to make." }, null, 2)}
1595
+ composeAndWriteRigfig(projectRoot, {
1596
+ overlay: buildWizardOverlay({ taskSource: { kind: "files", path: "tasks" } })
1597
+ });
1598
+ }
1599
+ const tasksDir = resolve6(projectRoot, "tasks");
1600
+ if (!existsSync5(tasksDir)) {
1601
+ mkdirSync3(tasksDir, { recursive: true });
1602
+ writeFileSync3(resolve6(tasksDir, "T-1.json"), `${JSON.stringify({ id: "T-1", title: "My first Rig task", body: "Describe the change you want an agent to make." }, null, 2)}
1063
1603
  `, "utf-8");
1064
1604
  }
1065
1605
  if (context.outputMode !== "json") {
@@ -1114,37 +1654,32 @@ function runDemoInit(context, options) {
1114
1654
  ensureRigPrivateDirs(projectRoot);
1115
1655
  ensureGitignoreEntries(projectRoot);
1116
1656
  writeRepoConnection(projectRoot, { selected: "local", linkedAt: new Date().toISOString() });
1117
- const configTsPath = resolve5(projectRoot, "rig.config.ts");
1118
- const configExists = existsSync4(configTsPath) || existsSync4(resolve5(projectRoot, "rig.config.json"));
1119
1657
  let configWritten = false;
1120
- if (configExists && !options.repair) {
1658
+ if (configAlreadyExists(projectRoot) && !options.repair) {
1121
1659
  if (context.outputMode !== "json") {
1122
- console.log("rig.config already exists; leaving it unchanged. Pass --repair to rewrite it for the demo.");
1660
+ console.log("rig config already exists; leaving it unchanged. Pass --repair to rewrite it for the demo.");
1123
1661
  }
1124
1662
  } else {
1125
- writeFileSync2(configTsPath, buildRigInitConfigSource({
1126
- projectName: basename(projectRoot) || "rig-demo",
1127
- taskSource: { kind: "files", path: DEMO_TASKS_RELATIVE_DIR },
1128
- useStandardPlugin: true
1129
- }), "utf-8");
1663
+ composeAndWriteRigfig(projectRoot, {
1664
+ overlay: buildWizardOverlay({ taskSource: { kind: "files", path: DEMO_TASKS_RELATIVE_DIR } })
1665
+ });
1130
1666
  configWritten = true;
1131
1667
  }
1132
- ensureRigConfigPackageDependencies(projectRoot);
1133
- const demoTasksDir = resolve5(projectRoot, DEMO_TASKS_RELATIVE_DIR);
1134
- mkdirSync2(demoTasksDir, { recursive: true });
1668
+ const demoTasksDir = resolve6(projectRoot, DEMO_TASKS_RELATIVE_DIR);
1669
+ mkdirSync3(demoTasksDir, { recursive: true });
1135
1670
  const taskIds = [];
1136
1671
  for (const task of DEMO_TASKS) {
1137
1672
  const id = String(task.id);
1138
1673
  taskIds.push(id);
1139
- const taskPath = resolve5(demoTasksDir, `${id}.json`);
1140
- if (!existsSync4(taskPath)) {
1141
- writeFileSync2(taskPath, `${JSON.stringify(task, null, 2)}
1674
+ const taskPath = resolve6(demoTasksDir, `${id}.json`);
1675
+ if (!existsSync5(taskPath)) {
1676
+ writeFileSync3(taskPath, `${JSON.stringify(task, null, 2)}
1142
1677
  `, "utf-8");
1143
1678
  }
1144
1679
  }
1145
1680
  if (context.outputMode !== "json") {
1146
1681
  console.log(`Demo Rig project ready (offline, no GitHub).`);
1147
- console.log(` config: rig.config.ts (files task source -> ${DEMO_TASKS_RELATIVE_DIR}/)`);
1682
+ console.log(` config: .rig/rigfig.toml (files task source -> ${DEMO_TASKS_RELATIVE_DIR}/)`);
1148
1683
  console.log(` tasks: ${taskIds.join(", ")}`);
1149
1684
  console.log("Next steps:");
1150
1685
  console.log(" 1. run bare `rig`");
@@ -1198,25 +1733,21 @@ async function runControlPlaneInit(context, options) {
1198
1733
  });
1199
1734
  ensureRigPrivateDirs(projectRoot);
1200
1735
  ensureGitignoreEntries(projectRoot);
1201
- const configTsPath = resolve5(projectRoot, "rig.config.ts");
1202
- const configJsonPath = resolve5(projectRoot, "rig.config.json");
1203
- const configExists = existsSync4(configTsPath) || existsSync4(configJsonPath);
1204
1736
  if (!options.privateStateOnly) {
1205
- if (configExists && !options.repair) {
1737
+ if (configAlreadyExists(projectRoot) && !options.repair) {
1206
1738
  if (context.outputMode !== "json")
1207
- console.log("rig.config already exists; leaving it unchanged. Pass --repair to rewrite it.");
1739
+ console.log("rig config already exists; leaving it unchanged. Pass --repair to rewrite it.");
1208
1740
  } else {
1209
- const source = applyGitHubProjectConfig(buildRigInitConfigSource({
1210
- projectName: repo.slug,
1211
- projectRepo: repo.slug,
1212
- taskSource: { kind: "github-issues", owner: repo.owner, repo: repo.repo },
1213
- useStandardPlugin: true
1214
- }), options);
1215
- writeFileSync2(configTsPath, source, "utf-8");
1741
+ composeAndWriteRigfig(projectRoot, {
1742
+ repoSlug: repo.slug,
1743
+ overlay: buildWizardOverlay({
1744
+ taskSource: { kind: "github-issues", owner: repo.owner, repo: repo.repo },
1745
+ projects: buildGitHubProjectsOverlay(options)
1746
+ })
1747
+ });
1216
1748
  }
1217
- ensureRigConfigPackageDependencies(projectRoot);
1218
1749
  }
1219
- writeFileSync2(resolve5(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
1750
+ writeFileSync3(resolve6(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
1220
1751
  `, "utf8");
1221
1752
  const checkout = checkoutForInit(projectRoot, options);
1222
1753
  let githubAuth = null;
@@ -1255,7 +1786,7 @@ async function runControlPlaneInit(context, options) {
1255
1786
  skipped: true,
1256
1787
  reason: "retired-server-label-bootstrap"
1257
1788
  };
1258
- const pi = await ensurePiRigInstalled({ projectRoot, homeDir: process.env.RIG_PI_HOME_DIR }).catch((error) => ({
1789
+ const pi = await ensurePiRigInstalled({ projectRoot, ...process.env.RIG_PI_HOME_DIR !== undefined ? { homeDir: process.env.RIG_PI_HOME_DIR } : {} }).catch((error) => ({
1259
1790
  pi: { ok: false, label: "pi", hint: error instanceof Error ? error.message : String(error) },
1260
1791
  piRig: { ok: false, label: "pi-rig global extension", hint: "Local pi-rig installation failed." },
1261
1792
  extensionPath: null,
@@ -1346,7 +1877,7 @@ function parseInitOptions(args) {
1346
1877
  async function runInteractiveControlPlaneInit(context, prompts) {
1347
1878
  prompts.intro?.("Initialize a Rig control-plane project");
1348
1879
  const projectRoot = context.projectRoot;
1349
- const existingConfig = existsSync4(resolve5(projectRoot, "rig.config.ts")) || existsSync4(resolve5(projectRoot, "rig.config.json"));
1880
+ const existingConfig = configAlreadyExists(projectRoot);
1350
1881
  let repair = false;
1351
1882
  let privateStateOnly = false;
1352
1883
  if (existingConfig) {
@@ -1370,7 +1901,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
1370
1901
  const repoSlug = await promptRequiredText(prompts, {
1371
1902
  message: "GitHub repo slug",
1372
1903
  placeholder: "owner/repo",
1373
- defaultValue: detectedRepo
1904
+ ...detectedRepo !== undefined ? { defaultValue: detectedRepo } : {}
1374
1905
  });
1375
1906
  const placement = await promptSelect(prompts, {
1376
1907
  message: "Execution placement",
@@ -1459,7 +1990,6 @@ export {
1459
1990
  runInteractiveControlPlaneInit,
1460
1991
  runDemoInit,
1461
1992
  executeInit,
1462
- buildRigInitConfigSource,
1463
1993
  DEMO_TASKS_RELATIVE_DIR,
1464
1994
  DEMO_TASKS
1465
1995
  };