@h-rig/cli-surface-plugin 0.0.6-alpha.156 → 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
@@ -1,32 +1,661 @@
1
1
  // @bun
2
2
  // packages/cli-surface-plugin/src/commands/inspect.ts
3
- import {
4
- changedFilesForTask,
5
- taskArtifactRead,
6
- taskArtifacts,
7
- taskDeps
8
- } from "@rig/runtime/control-plane/native/task-ops";
9
- import {
10
- extractRunLogs,
11
- getRun,
12
- listRuns,
13
- runsForTask,
14
- summarizeRunFailures
15
- } from "@rig/run-worker/runs";
3
+ import { RUN_READ_MODEL, TASK_DATA_SERVICE_CAPABILITY } from "@rig/contracts";
4
+ import { defineCapability } from "@rig/core/capability";
5
+ import { requireCapabilityForRoot, requireInstalledCapability } from "@rig/core/capability-loaders";
16
6
 
17
7
  // packages/cli-surface-plugin/src/runner.ts
18
- import { EventBus } from "@rig/runtime/control-plane/runtime/events";
19
- import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
20
- import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
21
- import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
8
+ import { EventBus } from "@rig/core/runtime-events";
9
+ import { CliError as RuntimeCliError } from "@rig/contracts";
10
+
11
+ // packages/cli-surface-plugin/src/control-plane/guard.ts
12
+ import { optimizeNextInvocation } from "bun:jsc";
13
+ import { existsSync, readFileSync, statSync } from "fs";
14
+ import { resolve } from "path";
15
+
16
+ // packages/cli-surface-plugin/src/control-plane/scope.ts
17
+ import { getScopeRules } from "@rig/core/scope-rules";
18
+ var scopeRegexCache = new Map;
19
+ function unique(values) {
20
+ return [...new Set(values)];
21
+ }
22
+ function normalizeRelativeScopePath(inputPath) {
23
+ let normalized = inputPath.replace(/^\.\//, "");
24
+ const rules = getScopeRules();
25
+ if (rules?.stripPrefixes) {
26
+ for (const prefix of rules.stripPrefixes) {
27
+ if (normalized.startsWith(prefix)) {
28
+ normalized = normalized.slice(prefix.length);
29
+ }
30
+ }
31
+ }
32
+ return normalized;
33
+ }
34
+ function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
35
+ let normalized = inputPath.replace(/^\.\//, "");
36
+ if (normalized.startsWith(projectRoot + "/")) {
37
+ normalized = normalized.slice(projectRoot.length + 1);
38
+ }
39
+ if (normalized.startsWith(monorepoRoot + "/")) {
40
+ normalized = normalized.slice(monorepoRoot.length + 1);
41
+ }
42
+ return normalizeRelativeScopePath(normalized);
43
+ }
44
+ function scopeGlobToRegex(glob) {
45
+ const cached = scopeRegexCache.get(glob);
46
+ if (cached) {
47
+ return cached;
48
+ }
49
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*");
50
+ const compiled = new RegExp(`^${escaped}$`);
51
+ scopeRegexCache.set(glob, compiled);
52
+ return compiled;
53
+ }
54
+ function scopeMatches(path, scopes) {
55
+ const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
56
+ for (const scope of scopes) {
57
+ const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
58
+ for (const candidatePath of pathVariants) {
59
+ for (const candidateScope of scopeVariants) {
60
+ if (candidatePath === candidateScope || scopeGlobToRegex(candidateScope).test(candidatePath)) {
61
+ return true;
62
+ }
63
+ }
64
+ }
65
+ }
66
+ return false;
67
+ }
22
68
 
69
+ // packages/cli-surface-plugin/src/control-plane/guard.ts
70
+ import {
71
+ POLICY_VERSION
72
+ } from "@rig/contracts";
73
+ var DEFAULT_SCOPE = {
74
+ fail_closed: true,
75
+ harness_paths_exempt: true,
76
+ runtime_paths_exempt: true
77
+ };
78
+ var DEFAULT_SANDBOX = {
79
+ mode: "enforce",
80
+ network: true,
81
+ read_deny: [],
82
+ write_allow_from_runtime: true
83
+ };
84
+ var DEFAULT_ISOLATION = {
85
+ default_mode: "worktree",
86
+ repo_symlink_fallback: false,
87
+ strict_provisioning: true,
88
+ fail_closed_on_provision_error: true
89
+ };
90
+ var DEFAULT_COMPLETION = {
91
+ derive_checks_from_scope: true,
92
+ checks: [],
93
+ typescript_config_probe: ["tsconfig.json"],
94
+ eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
95
+ };
96
+ var DEFAULT_RUNTIME_IMAGE = {
97
+ deps: {
98
+ monorepo_install: false,
99
+ hp_next_install: false
100
+ },
101
+ plugins_require_binaries: true
102
+ };
103
+ var DEFAULT_RUNTIME_SNAPSHOT = {
104
+ enabled: true
105
+ };
106
+ function defaultPolicy() {
107
+ return {
108
+ version: POLICY_VERSION,
109
+ mode: "enforce",
110
+ scope: { ...DEFAULT_SCOPE },
111
+ rules: [],
112
+ sandbox: { ...DEFAULT_SANDBOX },
113
+ isolation: { ...DEFAULT_ISOLATION },
114
+ completion: { ...DEFAULT_COMPLETION },
115
+ runtime_image: {
116
+ deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
117
+ plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
118
+ },
119
+ runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
120
+ };
121
+ }
122
+ var policyCache = null;
123
+ var policyCachePath = null;
124
+ var seededPolicyConfig = null;
125
+ var compiledRegexCache = new Map;
126
+ function loadPolicy(projectRoot) {
127
+ if (seededPolicyConfig) {
128
+ return seededPolicyConfig;
129
+ }
130
+ const configPath = resolve(projectRoot, "rig/policy/policy.json");
131
+ if (!existsSync(configPath)) {
132
+ return defaultPolicy();
133
+ }
134
+ let mtimeMs;
135
+ try {
136
+ mtimeMs = statSync(configPath).mtimeMs;
137
+ } catch {
138
+ return defaultPolicy();
139
+ }
140
+ if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
141
+ return policyCache.config;
142
+ }
143
+ let parsed;
144
+ try {
145
+ parsed = JSON.parse(readFileSync(configPath, "utf-8"));
146
+ } catch {
147
+ return defaultPolicy();
148
+ }
149
+ const config = mergeWithDefaults(parsed);
150
+ policyCache = { mtimeMs, config };
151
+ policyCachePath = configPath;
152
+ return config;
153
+ }
154
+ function mergeWithDefaults(parsed) {
155
+ const base = defaultPolicy();
156
+ if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
157
+ base.mode = parsed.mode;
158
+ }
159
+ if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
160
+ const s = parsed.scope;
161
+ if (typeof s.fail_closed === "boolean")
162
+ base.scope.fail_closed = s.fail_closed;
163
+ if (typeof s.harness_paths_exempt === "boolean")
164
+ base.scope.harness_paths_exempt = s.harness_paths_exempt;
165
+ if (typeof s.runtime_paths_exempt === "boolean")
166
+ base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
167
+ }
168
+ if (Array.isArray(parsed.rules)) {
169
+ base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
170
+ }
171
+ if (Array.isArray(parsed.deny) && base.rules.length === 0) {
172
+ base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
173
+ }
174
+ if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
175
+ const sb = parsed.sandbox;
176
+ if (typeof sb.mode === "string" && isValidMode(sb.mode))
177
+ base.sandbox.mode = sb.mode;
178
+ if (typeof sb.network === "boolean")
179
+ base.sandbox.network = sb.network;
180
+ if (Array.isArray(sb.read_deny))
181
+ base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
182
+ if (typeof sb.write_allow_from_runtime === "boolean")
183
+ base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
184
+ }
185
+ if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
186
+ const iso = parsed.isolation;
187
+ if (iso.default_mode === "worktree")
188
+ base.isolation.default_mode = iso.default_mode;
189
+ if (typeof iso.repo_symlink_fallback === "boolean")
190
+ base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
191
+ if (typeof iso.strict_provisioning === "boolean")
192
+ base.isolation.strict_provisioning = iso.strict_provisioning;
193
+ if (typeof iso.fail_closed_on_provision_error === "boolean")
194
+ base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
195
+ }
196
+ if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
197
+ const comp = parsed.completion;
198
+ if (typeof comp.derive_checks_from_scope === "boolean")
199
+ base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
200
+ if (Array.isArray(comp.checks))
201
+ base.completion.checks = comp.checks.filter((v) => typeof v === "string");
202
+ if (Array.isArray(comp.typescript_config_probe))
203
+ base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
204
+ if (Array.isArray(comp.eslint_config_probe))
205
+ base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
206
+ }
207
+ if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
208
+ const runtimeImage = parsed.runtime_image;
209
+ if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
210
+ const deps = runtimeImage.deps;
211
+ if (typeof deps.monorepo_install === "boolean") {
212
+ base.runtime_image.deps.monorepo_install = deps.monorepo_install;
213
+ }
214
+ if (typeof deps.hp_next_install === "boolean") {
215
+ base.runtime_image.deps.hp_next_install = deps.hp_next_install;
216
+ }
217
+ }
218
+ if (typeof runtimeImage.plugins_require_binaries === "boolean") {
219
+ base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
220
+ }
221
+ }
222
+ if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
223
+ const runtimeSnapshot = parsed.runtime_snapshot;
224
+ if (typeof runtimeSnapshot.enabled === "boolean") {
225
+ base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
226
+ }
227
+ }
228
+ return base;
229
+ }
230
+ function isValidMode(value) {
231
+ return value === "off" || value === "observe" || value === "enforce";
232
+ }
233
+ function isValidRule(value) {
234
+ if (!value || typeof value !== "object" || Array.isArray(value))
235
+ return false;
236
+ const r = value;
237
+ return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
238
+ }
239
+ function migrateLegacyDeny(deny) {
240
+ const rules = [];
241
+ for (const entry of deny) {
242
+ if (typeof entry.id !== "string")
243
+ continue;
244
+ const match = {};
245
+ if (typeof entry.pattern === "string")
246
+ match.pattern = entry.pattern;
247
+ if (typeof entry.regex === "string")
248
+ match.regex = entry.regex;
249
+ if (!match.pattern && !match.regex)
250
+ continue;
251
+ rules.push({
252
+ id: entry.id,
253
+ category: "command",
254
+ match,
255
+ action: "block",
256
+ ...typeof entry.description === "string" ? { description: entry.description } : {}
257
+ });
258
+ }
259
+ return rules;
260
+ }
261
+ function precompilePolicyRuleRegexes(rules) {
262
+ return rules.map((rule) => {
263
+ const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
264
+ const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
265
+ return {
266
+ ...rule,
267
+ ...compiledRegex ? { compiledRegex } : {},
268
+ ...compiledUnlessRegex ? { compiledUnlessRegex } : {}
269
+ };
270
+ });
271
+ }
272
+ function getRegexUnsafeReason(pattern) {
273
+ if (pattern.length > 512) {
274
+ return "pattern exceeds max safe length (512 chars)";
275
+ }
276
+ if (/\\[1-9]/.test(pattern)) {
277
+ return "pattern uses backreferences";
278
+ }
279
+ if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
280
+ return "pattern contains nested quantifiers";
281
+ }
282
+ if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
283
+ return "pattern contains nested broad quantifiers";
284
+ }
285
+ return null;
286
+ }
287
+ function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
288
+ const cached = compiledRegexCache.get(pattern);
289
+ if (cached !== undefined) {
290
+ return cached ?? undefined;
291
+ }
292
+ const unsafeReason = getRegexUnsafeReason(pattern);
293
+ if (unsafeReason) {
294
+ if (logOnFailure) {
295
+ console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
296
+ }
297
+ compiledRegexCache.set(pattern, null);
298
+ return;
299
+ }
300
+ try {
301
+ const compiled = new RegExp(pattern);
302
+ compiledRegexCache.set(pattern, compiled);
303
+ return compiled;
304
+ } catch (error) {
305
+ if (logOnFailure) {
306
+ const message = error instanceof Error ? error.message : String(error);
307
+ console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
308
+ }
309
+ compiledRegexCache.set(pattern, null);
310
+ return;
311
+ }
312
+ }
313
+ function matchRule(rule, input) {
314
+ const { match } = rule;
315
+ if (match.pattern && input.includes(match.pattern)) {
316
+ return true;
317
+ }
318
+ if (match.regex) {
319
+ const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
320
+ if (!compiled) {
321
+ return false;
322
+ }
323
+ try {
324
+ return compiled.test(input);
325
+ } catch {
326
+ return false;
327
+ }
328
+ }
329
+ return false;
330
+ }
331
+ function matchRuleUnless(rule, command, taskId) {
332
+ if (!rule.unless)
333
+ return false;
334
+ if (rule.unless.regex) {
335
+ const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
336
+ if (!compiled) {
337
+ return false;
338
+ }
339
+ try {
340
+ if (compiled.test(command))
341
+ return true;
342
+ } catch {}
343
+ }
344
+ if (rule.unless.task_in && taskId) {
345
+ if (rule.unless.task_in.includes(taskId))
346
+ return true;
347
+ }
348
+ return false;
349
+ }
350
+ function resolveAction(mode, matched) {
351
+ if (matched.length === 0)
352
+ return "allow";
353
+ if (mode === "off")
354
+ return "allow";
355
+ if (mode === "observe")
356
+ return "warn";
357
+ return "block";
358
+ }
359
+ function resolveAbsolutePath(projectRoot, rawPath) {
360
+ if (rawPath.startsWith("/"))
361
+ return resolve(rawPath);
362
+ return resolve(projectRoot, rawPath);
363
+ }
364
+ function isHarnessPath(projectRoot, rawPath) {
365
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
366
+ const managedRoots = [
367
+ resolve(projectRoot, "rig"),
368
+ resolve(projectRoot, ".rig"),
369
+ resolve(projectRoot, "artifacts")
370
+ ];
371
+ return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
372
+ }
373
+ function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
374
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
375
+ if (taskWorkspace) {
376
+ const workspaceRigRoot = resolve(taskWorkspace, ".rig");
377
+ const workspaceArtifactsRoot = resolve(taskWorkspace, "artifacts");
378
+ if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
379
+ return true;
380
+ }
381
+ }
382
+ const runtimeRoot = resolve(projectRoot, ".rig/runtime/agents");
383
+ return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
384
+ }
385
+ function isTestFile(path) {
386
+ return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
387
+ }
388
+ function evaluate(context) {
389
+ const policy = loadPolicy(context.projectRoot);
390
+ switch (context.evaluation.type) {
391
+ case "tool-call":
392
+ return evaluateToolCall(policy, context);
393
+ case "command":
394
+ return evaluateCommand(policy, context);
395
+ case "content-write":
396
+ return evaluateContent(policy, context);
397
+ case "file-access":
398
+ return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
399
+ }
400
+ }
401
+ function evaluateScope(policy, context, filePath, access) {
402
+ const allowed = () => ({
403
+ allowed: true,
404
+ matchedRules: [],
405
+ action: "allow",
406
+ failClosed: false
407
+ });
408
+ if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
409
+ return allowed();
410
+ }
411
+ if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
412
+ return allowed();
413
+ }
414
+ if (!context.taskId) {
415
+ if (access === "write" && policy.scope.fail_closed) {
416
+ return {
417
+ allowed: false,
418
+ matchedRules: [],
419
+ action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
420
+ failClosed: true
421
+ };
422
+ }
423
+ return allowed();
424
+ }
425
+ const scopes = context.taskScopes || [];
426
+ if (scopes.length === 0) {
427
+ return allowed();
428
+ }
429
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
430
+ const absPath = resolve(filePath);
431
+ if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
432
+ const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
433
+ const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
434
+ return {
435
+ allowed: policy.mode !== "enforce",
436
+ matchedRules: matched2,
437
+ action: resolveAction(policy.mode, matched2),
438
+ failClosed: false
439
+ };
440
+ }
441
+ }
442
+ const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
443
+ let normalizedPath = filePath;
444
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
445
+ normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
446
+ }
447
+ normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
448
+ if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
449
+ return allowed();
450
+ }
451
+ const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
452
+ const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
453
+ return {
454
+ allowed: policy.mode !== "enforce",
455
+ matchedRules: matched,
456
+ action: resolveAction(policy.mode, matched),
457
+ failClosed: false
458
+ };
459
+ }
460
+ function evaluateCommand(policy, context) {
461
+ const evaluation = context.evaluation;
462
+ if (evaluation.type !== "command") {
463
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
464
+ }
465
+ const command = evaluation.command;
466
+ const matchedRules = [];
467
+ for (const rule of policy.rules) {
468
+ if (rule.category !== "command")
469
+ continue;
470
+ if (!matchRule(rule, command))
471
+ continue;
472
+ if (matchRuleUnless(rule, command, context.taskId))
473
+ continue;
474
+ matchedRules.push({
475
+ id: rule.id,
476
+ category: rule.category,
477
+ ...rule.description !== undefined ? { description: rule.description } : {},
478
+ reason: rule.description || `Matched rule ${rule.id}`
479
+ });
480
+ }
481
+ const writeTarget = extractWriteTarget(command);
482
+ if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
483
+ const scopeResult = evaluateScope(policy, context, writeTarget, "write");
484
+ if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
485
+ matchedRules.push(...scopeResult.matchedRules);
486
+ }
487
+ }
488
+ const action = resolveAction(policy.mode, matchedRules);
489
+ return {
490
+ allowed: action !== "block",
491
+ matchedRules,
492
+ action,
493
+ failClosed: false
494
+ };
495
+ }
496
+ function extractWriteTarget(command) {
497
+ const redirect = command.match(/>>?\s+([^\s;|&]+)/);
498
+ if (redirect?.[1])
499
+ return redirect[1];
500
+ const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
501
+ if (tee?.[2])
502
+ return tee[2];
503
+ return "";
504
+ }
505
+ function evaluateContent(policy, context) {
506
+ const evaluation = context.evaluation;
507
+ if (evaluation.type !== "content-write") {
508
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
509
+ }
510
+ const { content, file_path } = evaluation;
511
+ const matchedRules = [];
512
+ const scopeResult = evaluateScope(policy, context, file_path, "write");
513
+ if (scopeResult.matchedRules.length > 0) {
514
+ matchedRules.push(...scopeResult.matchedRules);
515
+ }
516
+ for (const rule of policy.rules) {
517
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
518
+ continue;
519
+ if (rule.applies_to === "test-files" && !isTestFile(file_path))
520
+ continue;
521
+ if (!matchRule(rule, content))
522
+ continue;
523
+ if (matchRuleUnless(rule, content, context.taskId))
524
+ continue;
525
+ matchedRules.push({
526
+ id: rule.id,
527
+ category: rule.category,
528
+ ...rule.description !== undefined ? { description: rule.description } : {},
529
+ reason: rule.description || `Matched rule ${rule.id}`
530
+ });
531
+ }
532
+ const action = resolveAction(policy.mode, matchedRules);
533
+ return {
534
+ allowed: action !== "block",
535
+ matchedRules,
536
+ action,
537
+ failClosed: false
538
+ };
539
+ }
540
+ function evaluateToolCall(policy, context) {
541
+ const evaluation = context.evaluation;
542
+ if (evaluation.type !== "tool-call") {
543
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
544
+ }
545
+ const { tool_name, tool_input } = evaluation;
546
+ const allMatched = [];
547
+ const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
548
+ for (const fp of filePaths) {
549
+ const access = isWriteTool(tool_name) ? "write" : "read";
550
+ const scopeResult = evaluateScope(policy, context, fp, access);
551
+ if (scopeResult.matchedRules.length > 0) {
552
+ allMatched.push(...scopeResult.matchedRules);
553
+ }
554
+ }
555
+ const content = extractContentFromToolInput(tool_input);
556
+ if (content) {
557
+ const filePath = filePaths[0] || "";
558
+ const contentContext = {
559
+ ...context,
560
+ evaluation: { type: "content-write", file_path: filePath, content }
561
+ };
562
+ const contentPolicy = loadPolicy(context.projectRoot);
563
+ for (const rule of contentPolicy.rules) {
564
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
565
+ continue;
566
+ if (rule.applies_to === "test-files" && !isTestFile(filePath))
567
+ continue;
568
+ if (!matchRule(rule, content))
569
+ continue;
570
+ if (matchRuleUnless(rule, content, context.taskId))
571
+ continue;
572
+ allMatched.push({
573
+ id: rule.id,
574
+ category: rule.category,
575
+ ...rule.description !== undefined ? { description: rule.description } : {},
576
+ reason: rule.description || `Matched rule ${rule.id}`
577
+ });
578
+ }
579
+ }
580
+ if (tool_name === "Bash") {
581
+ const command = String(tool_input.command || tool_input.cmd || "");
582
+ if (command) {
583
+ const cmdContext = {
584
+ ...context,
585
+ evaluation: { type: "command", command }
586
+ };
587
+ const cmdResult = evaluateCommand(policy, cmdContext);
588
+ if (cmdResult.matchedRules.length > 0) {
589
+ allMatched.push(...cmdResult.matchedRules);
590
+ }
591
+ }
592
+ }
593
+ const seen = new Set;
594
+ const deduplicated = [];
595
+ for (const rule of allMatched) {
596
+ if (!seen.has(rule.id)) {
597
+ seen.add(rule.id);
598
+ deduplicated.push(rule);
599
+ }
600
+ }
601
+ const action = resolveAction(policy.mode, deduplicated);
602
+ return {
603
+ allowed: action !== "block",
604
+ matchedRules: deduplicated,
605
+ action,
606
+ failClosed: false
607
+ };
608
+ }
609
+ function isWriteTool(toolName) {
610
+ return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
611
+ }
612
+ function extractFilePathsFromToolInput(toolName, input) {
613
+ const paths = [];
614
+ const add = (value) => {
615
+ if (typeof value === "string" && value.trim()) {
616
+ paths.push(value.trim());
617
+ }
618
+ };
619
+ if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
620
+ add(input.file_path);
621
+ add(input.path);
622
+ } else if (toolName === "Glob") {
623
+ add(input.path);
624
+ } else if (toolName === "Grep") {
625
+ add(input.path);
626
+ } else {
627
+ add(input.file_path);
628
+ add(input.path);
629
+ }
630
+ return paths;
631
+ }
632
+ function extractContentFromToolInput(input) {
633
+ if (typeof input.content === "string")
634
+ return input.content;
635
+ if (typeof input.new_string === "string")
636
+ return input.new_string;
637
+ return "";
638
+ }
639
+ var guardHotPathPrimed = false;
640
+ function primeGuardHotPaths() {
641
+ if (guardHotPathPrimed) {
642
+ return;
643
+ }
644
+ guardHotPathPrimed = true;
645
+ try {
646
+ optimizeNextInvocation(matchRule);
647
+ optimizeNextInvocation(evaluate);
648
+ } catch {}
649
+ }
650
+ primeGuardHotPaths();
651
+
652
+ // packages/cli-surface-plugin/src/control-plane/agent-binary-build.ts
653
+ import { runtimeProvisioningEnv } from "@rig/core/runtime-provisioning-env";
654
+
655
+ // packages/cli-surface-plugin/src/runner.ts
23
656
  class CliError extends RuntimeCliError {
24
- hint;
25
657
  constructor(message, exitCode = 1, options = {}) {
26
- super(message, exitCode);
27
- if (options.hint?.trim()) {
28
- this.hint = options.hint.trim();
29
- }
658
+ super(message, exitCode, options);
30
659
  }
31
660
  }
32
661
  function takeOption(args, option) {
@@ -65,10 +694,13 @@ Usage: ${usage}`);
65
694
 
66
695
  // packages/cli-surface-plugin/src/commands/_cli-format.ts
67
696
  import pc from "picocolors";
68
- import { runStatusColorRole, runStatusText, statusColorRole } from "@rig/run-worker/runs";
69
697
  var dim = pc.dim;
70
698
  var faintBar = pc.dim("\u2502");
71
699
  var accent = pc.cyan;
700
+ function numberField(record, key) {
701
+ const value = record[key];
702
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
703
+ }
72
704
  function truncate(value, width) {
73
705
  return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
74
706
  }
@@ -90,6 +722,52 @@ function colorForRole(role) {
90
722
  return pc.dim;
91
723
  }
92
724
  }
725
+ function normalizeStatus(status) {
726
+ const normalized = String(status ?? "").trim().toLowerCase().replace(/[\s_]+/g, "-");
727
+ return normalized === "waiting-input" ? "waiting-user-input" : normalized;
728
+ }
729
+ function statusColorRole(status) {
730
+ switch (normalizeStatus(status)) {
731
+ case "done":
732
+ case "completed":
733
+ case "ready":
734
+ case "healthy":
735
+ case "approved":
736
+ case "merged":
737
+ return "success";
738
+ case "needs-attention":
739
+ case "waiting-approval":
740
+ case "waiting-user-input":
741
+ case "blocked":
742
+ case "paused":
743
+ return "action-yellow";
744
+ case "running":
745
+ case "adopted":
746
+ case "preparing":
747
+ case "created":
748
+ case "queued":
749
+ case "starting":
750
+ case "pending":
751
+ case "in-progress":
752
+ case "active":
753
+ case "booting":
754
+ case "validating":
755
+ case "reviewing":
756
+ case "closing-out":
757
+ return "active-cyan";
758
+ case "failed":
759
+ case "error":
760
+ case "rejected":
761
+ return "failure";
762
+ case "stopped":
763
+ case "cancelled":
764
+ case "canceled":
765
+ case "stale":
766
+ return "muted";
767
+ default:
768
+ return "neutral";
769
+ }
770
+ }
93
771
  function firstString(record, keys, fallback = "") {
94
772
  for (const key of keys) {
95
773
  const value = record[key];
@@ -108,10 +786,20 @@ function runTitleOf(run) {
108
786
  return firstString(run, ["title", "summary", "name"], taskIdOf(run) || "(untitled)");
109
787
  }
110
788
  function runLikeStatusText(run) {
111
- return runStatusText(run);
789
+ const record = run;
790
+ const normalized = normalizeStatus(record.status);
791
+ return normalized || (record.live === true && record.stale !== true ? "starting" : "unknown");
112
792
  }
113
793
  function runLikeStatusColor(run) {
114
- return colorForRole(runStatusColorRole(run));
794
+ const record = run;
795
+ const pendingApprovals = numberField(record, "pendingApprovals") ?? 0;
796
+ const pendingInputs = numberField(record, "pendingInputs") ?? 0;
797
+ const stallCount = numberField(record, "stallCount") ?? 0;
798
+ const status = runLikeStatusText(run);
799
+ if (status !== "completed" && status !== "failed" && status !== "stopped" && (status === "needs-attention" || pendingApprovals + pendingInputs > 0 || stallCount > 0)) {
800
+ return colorForRole("action-yellow");
801
+ }
802
+ return colorForRole(statusColorRole(status));
115
803
  }
116
804
  function printFormattedOutput(message) {
117
805
  console.log(message);
@@ -148,6 +836,9 @@ function formatRunList(runs, options = {}) {
148
836
  }
149
837
 
150
838
  // packages/cli-surface-plugin/src/commands/inspect.ts
839
+ var RunReadModelCap = defineCapability(RUN_READ_MODEL);
840
+ var TaskDataCap = defineCapability(TASK_DATA_SERVICE_CAPABILITY);
841
+ var taskData = () => requireInstalledCapability(TaskDataCap, "task-data capability unavailable: load @rig/task-sources-plugin (default bundle) before inspecting task artifacts.");
151
842
  var INSPECT_USAGE = "rig inspect <logs|artifact|artifacts|run-logs|runs|failures|graph|diff>";
152
843
  function inspectHelpText() {
153
844
  return [
@@ -194,18 +885,41 @@ function requireRunId(value, usage) {
194
885
  throw new CliError(`${usage} requires a run id.`, 2, { hint: "Run `rig inspect runs` to find run ids." });
195
886
  return value.trim();
196
887
  }
888
+ async function loadRunReadModel(projectRoot) {
889
+ return requireCapabilityForRoot(projectRoot, RunReadModelCap, "No run-worker plugin provides run read model for this project root.");
890
+ }
891
+ async function listRunRecords(projectRoot, deps) {
892
+ return deps.listRunProjections ? deps.listRunProjections(projectRoot) : (await loadRunReadModel(projectRoot)).listRuns({ projectRoot });
893
+ }
894
+ async function getRunRecord(projectRoot, runId, deps) {
895
+ return deps.getRunProjection ? deps.getRunProjection(projectRoot, runId) : (await loadRunReadModel(projectRoot)).getRun({ projectRoot, selector: { id: runId, kind: "any" } });
896
+ }
897
+ async function getRunDetails(projectRoot, runId, deps, options = {}) {
898
+ return deps.getRunDetails ? deps.getRunDetails(projectRoot, runId, options) : (await loadRunReadModel(projectRoot)).getRunDetails({ projectRoot, selector: { id: runId, kind: "any" }, ...options.includeLogs === undefined ? {} : { includeLogs: options.includeLogs } });
899
+ }
900
+ async function runsForTaskId(projectRoot, taskId, deps) {
901
+ try {
902
+ const runs = await listRunRecords(projectRoot, deps);
903
+ const filtered = runs.filter((run2) => run2.taskId === taskId || run2.runId === taskId);
904
+ if (filtered.length > 0)
905
+ return filtered;
906
+ } catch {}
907
+ const run = await getRunRecord(projectRoot, taskId, deps);
908
+ return run ? [run] : [];
909
+ }
910
+ function failureLine(failure) {
911
+ return failure.detail ? `${failure.summary} \u2014 ${failure.detail}` : failure.summary;
912
+ }
197
913
  async function executeLogs(context, args, deps) {
198
914
  const taskResult = takeOption(args, "--task");
199
915
  requireNoExtraArgs(taskResult.rest, "rig inspect logs --task <id>");
200
916
  const taskId = requireTask(taskResult.value, "rig inspect logs --task <id>");
201
- const runs = await runsForTask(context.projectRoot, taskId, {
202
- listRuns: deps.listRunProjections ?? listRuns,
203
- getRun: deps.getRunProjection ?? getRun
204
- });
917
+ const runs = await runsForTaskId(context.projectRoot, taskId, deps);
205
918
  const projection = runs[0];
206
919
  if (!projection)
207
920
  throw new CliError(`No projected runs found for task ${taskId}.`, 1, { hint: "Run `rig run list` to confirm the run journal is discoverable." });
208
- const lines = extractRunLogs(projection, { readSessionRunEntries: deps.readSessionRunEntries });
921
+ const details = await getRunDetails(context.projectRoot, projection.runId, deps, { includeLogs: true });
922
+ const lines = [...details?.logs ?? []];
209
923
  if (context.outputMode === "text") {
210
924
  if (lines.length === 0)
211
925
  console.log(`No log entries found for task ${taskId}.`);
@@ -223,7 +937,7 @@ async function executeArtifact(context, args, deps) {
223
937
  const filename = fileResult.value?.trim();
224
938
  if (!filename)
225
939
  throw new CliError("rig inspect artifact --task <id> --file <name> requires an artifact file name.", 2);
226
- const preview = await (deps.taskArtifactRead ?? taskArtifactRead)(context.projectRoot, filename, { taskId, maxBytes: 64 * 1024 });
940
+ const preview = await (deps.taskArtifactRead ?? taskData().taskArtifactRead)(context.projectRoot, filename, { taskId, maxBytes: 64 * 1024 });
227
941
  if (context.outputMode === "text") {
228
942
  console.log(preview.contents);
229
943
  if (preview.truncated)
@@ -236,10 +950,11 @@ async function executeRunLogs(context, args, deps) {
236
950
  const runResult = takeOption(args, "--run");
237
951
  requireNoExtraArgs(runResult.rest, "rig inspect run-logs --run <id>");
238
952
  const runId = requireRunId(runResult.value, "rig inspect run-logs --run <id>");
239
- const projection = await (deps.getRunProjection ?? getRun)(context.projectRoot, runId);
953
+ const projection = await getRunRecord(context.projectRoot, runId, deps);
240
954
  if (!projection)
241
955
  throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig inspect runs` to see runs." });
242
- const lines = extractRunLogs(projection, { readSessionRunEntries: deps.readSessionRunEntries });
956
+ const details = await getRunDetails(context.projectRoot, projection.runId, deps, { includeLogs: true });
957
+ const lines = [...details?.logs ?? []];
243
958
  if (context.outputMode === "text") {
244
959
  if (lines.length === 0)
245
960
  console.log(`No log entries found for run ${projection.runId}.`);
@@ -251,7 +966,7 @@ async function executeRunLogs(context, args, deps) {
251
966
  }
252
967
  async function executeRuns(context, args, deps) {
253
968
  requireNoExtraArgs(args, "rig inspect runs");
254
- const runs = await (deps.listRunProjections ?? listRuns)(context.projectRoot);
969
+ const runs = await listRunRecords(context.projectRoot, deps);
255
970
  const formatted = runs.map(toRunLike);
256
971
  if (context.outputMode === "text")
257
972
  printFormattedOutput(formatRunList(formatted));
@@ -262,14 +977,14 @@ async function executeArtifacts(context, args, deps) {
262
977
  requireNoExtraArgs(taskResult.rest, "rig inspect artifacts --task <id>");
263
978
  const taskId = requireTask(taskResult.value, "rig inspect artifacts --task <id>");
264
979
  if (context.outputMode === "text")
265
- await (deps.taskArtifacts ?? taskArtifacts)(context.projectRoot, taskId);
980
+ await (deps.taskArtifacts ?? taskData().taskArtifacts)(context.projectRoot, taskId);
266
981
  return { ok: true, group: "inspect", command: "artifacts", details: { taskId, printed: context.outputMode === "text" } };
267
982
  }
268
983
  async function executeDiff(context, args, deps) {
269
984
  const taskResult = takeOption(args, "--task");
270
985
  requireNoExtraArgs(taskResult.rest, "rig inspect diff --task <id>");
271
986
  const taskId = requireTask(taskResult.value, "rig inspect diff --task <id>");
272
- const files = (deps.changedFilesForTask ?? changedFilesForTask)(context.projectRoot, taskId, true);
987
+ const files = (deps.changedFilesForTask ?? taskData().changedFilesForTask)(context.projectRoot, taskId, true);
273
988
  if (context.outputMode === "text")
274
989
  for (const file of files)
275
990
  console.log(file);
@@ -279,18 +994,15 @@ async function executeGraph(context, args, deps) {
279
994
  const taskResult = takeOption(args, "--task");
280
995
  requireNoExtraArgs(taskResult.rest, "rig inspect graph [--task <id>]");
281
996
  if (context.outputMode === "text")
282
- await (deps.taskDeps ?? taskDeps)(context.projectRoot, taskResult.value);
997
+ await (deps.taskDeps ?? taskData().taskDeps)(context.projectRoot, taskResult.value);
283
998
  return { ok: true, group: "inspect", command: "graph", details: { taskId: taskResult.value ?? null, printed: context.outputMode === "text" } };
284
999
  }
285
1000
  async function executeFailures(context, args, deps) {
286
1001
  const taskResult = takeOption(args, "--task");
287
1002
  requireNoExtraArgs(taskResult.rest, "rig inspect failures --task <id>");
288
1003
  const taskId = requireTask(taskResult.value, "rig inspect failures --task <id>");
289
- const runs = await runsForTask(context.projectRoot, taskId, {
290
- listRuns: deps.listRunProjections ?? listRuns,
291
- getRun: deps.getRunProjection ?? getRun
292
- });
293
- const failures = runs.flatMap(summarizeRunFailures);
1004
+ const runs = await runsForTaskId(context.projectRoot, taskId, deps);
1005
+ const failures = (await Promise.all(runs.map(async (run) => (await getRunDetails(context.projectRoot, run.runId, deps))?.failures ?? []))).flat().map(failureLine);
294
1006
  if (context.outputMode === "text") {
295
1007
  if (failures.length === 0)
296
1008
  console.log(`No failures recorded for task ${taskId}.`);