@h-rig/core 0.0.6-alpha.7 → 0.0.6-alpha.70

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.
@@ -2,6 +2,23 @@
2
2
  // packages/core/src/define-config.ts
3
3
  import { Schema } from "effect";
4
4
  import { RigConfig } from "@rig/contracts";
5
+ function normalizeWorkspaceConfig(raw) {
6
+ const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : { mainRepo: "." };
7
+ workspace.checkout = workspace.checkout ?? workspace.isolation ?? "worktree";
8
+ workspace.isolation = workspace.isolation ?? workspace.checkout;
9
+ workspace.sandbox = workspace.sandbox ?? "enforce";
10
+ return workspace;
11
+ }
12
+ function applyConfigDefaults(raw) {
13
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
14
+ return raw;
15
+ const record = raw;
16
+ return {
17
+ ...record,
18
+ plugins: Array.isArray(record.plugins) ? record.plugins : [],
19
+ workspace: normalizeWorkspaceConfig(record.workspace)
20
+ };
21
+ }
5
22
  function defineConfig(cfg) {
6
23
  const runtimeByName = new Map;
7
24
  const plugins = cfg.plugins ?? [];
@@ -10,7 +27,7 @@ function defineConfig(cfg) {
10
27
  runtimeByName.set(plugin.name, plugin.__runtime);
11
28
  }
12
29
  }
13
- const decoded = Schema.decodeUnknownSync(RigConfig)(cfg);
30
+ const decoded = Schema.decodeUnknownSync(RigConfig)(applyConfigDefaults(cfg));
14
31
  const decodedPlugins = decoded.plugins.map((p) => {
15
32
  const runtime = runtimeByName.get(p.name);
16
33
  if (!runtime)
@@ -20,5 +37,6 @@ function defineConfig(cfg) {
20
37
  return { ...decoded, plugins: decodedPlugins };
21
38
  }
22
39
  export {
23
- defineConfig
40
+ defineConfig,
41
+ applyConfigDefaults
24
42
  };
@@ -43,6 +43,23 @@ function definePlugin(meta, runtime) {
43
43
  }
44
44
  }
45
45
  }
46
+ const declaredHooks = new Map((validated.contributes?.hooks ?? []).map((h) => [h.id, h]));
47
+ for (const hookId of Object.keys(runtime.hooks ?? {})) {
48
+ const metadata = declaredHooks.get(hookId);
49
+ if (!metadata) {
50
+ throw new Error(`definePlugin(${validated.name}): typed hook "${hookId}" has no matching metadata entry in contributes.hooks`);
51
+ }
52
+ if (metadata.command) {
53
+ throw new Error(`definePlugin(${validated.name}): hook "${hookId}" has both a typed implementation and a command string \u2014 pick one`);
54
+ }
55
+ }
56
+ if (runtime.hooks) {
57
+ for (const h of declaredHooks.values()) {
58
+ if (!runtime.hooks[h.id] && !h.command) {
59
+ throw new Error(`definePlugin(${validated.name}): hook metadata "${h.id}" has no implementation (typed function or command)`);
60
+ }
61
+ }
62
+ }
46
63
  return { ...validated, __runtime: runtime };
47
64
  }
48
65
  export {
@@ -149,7 +149,7 @@ function mapLegacySessionStatusToRunStatus(status) {
149
149
  case "error":
150
150
  return "failed";
151
151
  case "stopped":
152
- return "cancelled";
152
+ return "stopped";
153
153
  default:
154
154
  return "created";
155
155
  }
@@ -185,12 +185,15 @@ function mapTaskStatusFromRunStatus(status, fallback) {
185
185
  case "paused":
186
186
  return "in_progress";
187
187
  case "reviewing":
188
+ case "closing-out":
188
189
  return "under_review";
190
+ case "needs-attention":
191
+ return "blocked";
189
192
  case "completed":
190
193
  return "completed";
191
194
  case "failed":
192
195
  return "ready";
193
- case "cancelled":
196
+ case "stopped":
194
197
  return "cancelled";
195
198
  }
196
199
  }
@@ -1040,7 +1043,7 @@ function applyEngineEvent(snapshot, event) {
1040
1043
  ...base,
1041
1044
  runs: patchById(base.runs, payload.runId, (run) => ({
1042
1045
  ...run,
1043
- status: "cancelled",
1046
+ status: "stopped",
1044
1047
  updatedAt: payload.createdAt,
1045
1048
  completedAt: payload.createdAt
1046
1049
  }))
package/dist/src/index.js CHANGED
@@ -43,11 +43,45 @@ function definePlugin(meta, runtime) {
43
43
  }
44
44
  }
45
45
  }
46
+ const declaredHooks = new Map((validated.contributes?.hooks ?? []).map((h) => [h.id, h]));
47
+ for (const hookId of Object.keys(runtime.hooks ?? {})) {
48
+ const metadata = declaredHooks.get(hookId);
49
+ if (!metadata) {
50
+ throw new Error(`definePlugin(${validated.name}): typed hook "${hookId}" has no matching metadata entry in contributes.hooks`);
51
+ }
52
+ if (metadata.command) {
53
+ throw new Error(`definePlugin(${validated.name}): hook "${hookId}" has both a typed implementation and a command string \u2014 pick one`);
54
+ }
55
+ }
56
+ if (runtime.hooks) {
57
+ for (const h of declaredHooks.values()) {
58
+ if (!runtime.hooks[h.id] && !h.command) {
59
+ throw new Error(`definePlugin(${validated.name}): hook metadata "${h.id}" has no implementation (typed function or command)`);
60
+ }
61
+ }
62
+ }
46
63
  return { ...validated, __runtime: runtime };
47
64
  }
48
65
  // packages/core/src/define-config.ts
49
66
  import { Schema as Schema2 } from "effect";
50
67
  import { RigConfig } from "@rig/contracts";
68
+ function normalizeWorkspaceConfig(raw) {
69
+ const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : { mainRepo: "." };
70
+ workspace.checkout = workspace.checkout ?? workspace.isolation ?? "worktree";
71
+ workspace.isolation = workspace.isolation ?? workspace.checkout;
72
+ workspace.sandbox = workspace.sandbox ?? "enforce";
73
+ return workspace;
74
+ }
75
+ function applyConfigDefaults(raw) {
76
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
77
+ return raw;
78
+ const record = raw;
79
+ return {
80
+ ...record,
81
+ plugins: Array.isArray(record.plugins) ? record.plugins : [],
82
+ workspace: normalizeWorkspaceConfig(record.workspace)
83
+ };
84
+ }
51
85
  function defineConfig(cfg) {
52
86
  const runtimeByName = new Map;
53
87
  const plugins = cfg.plugins ?? [];
@@ -56,7 +90,7 @@ function defineConfig(cfg) {
56
90
  runtimeByName.set(plugin.name, plugin.__runtime);
57
91
  }
58
92
  }
59
- const decoded = Schema2.decodeUnknownSync(RigConfig)(cfg);
93
+ const decoded = Schema2.decodeUnknownSync(RigConfig)(applyConfigDefaults(cfg));
60
94
  const decodedPlugins = decoded.plugins.map((p) => {
61
95
  const runtime = runtimeByName.get(p.name);
62
96
  if (!runtime)
@@ -124,6 +158,24 @@ function assertRuntimeMatchesMetadata(plugin) {
124
158
  }
125
159
  }
126
160
  }
161
+ const declaredHooks = new Map((plugin.contributes?.hooks ?? []).map((hook) => [hook.id, hook]));
162
+ const runtimeHooks = plugin.__runtime?.hooks;
163
+ for (const hookId of Object.keys(runtimeHooks ?? {})) {
164
+ const metadata = declaredHooks.get(hookId);
165
+ if (!metadata) {
166
+ throw new Error(`plugin "${plugin.name}" typed hook "${hookId}" has no matching metadata entry in contributes.hooks`);
167
+ }
168
+ if (metadata.command) {
169
+ throw new Error(`plugin "${plugin.name}" hook "${hookId}" has both a typed implementation and a command string \u2014 pick one`);
170
+ }
171
+ }
172
+ if (runtimeHooks) {
173
+ for (const hook of declaredHooks.values()) {
174
+ if (!runtimeHooks[hook.id] && !hook.command) {
175
+ throw new Error(`plugin "${plugin.name}" hook metadata "${hook.id}" has no implementation (typed function or command)`);
176
+ }
177
+ }
178
+ }
127
179
  }
128
180
  function createPluginHost(plugins) {
129
181
  assertUniquePluginNames(plugins);
@@ -263,7 +315,7 @@ function buildRigInitConfigSource(input) {
263
315
  lines.push(` issueUpdates: "lifecycle",`);
264
316
  lines.push(` projects: { enabled: false },`);
265
317
  lines.push(` },`);
266
- lines.push(` automation: { maxValidationAttempts: 30, maxPrFixIterations: 30 },`);
318
+ lines.push(` automation: { maxValidationAttempts: 30, maxPrFixIterations: 100500 },`);
267
319
  lines.push(` pr: { mode: "auto", watchChecks: true, autoFixChecks: true, autoFixReview: true },`);
268
320
  lines.push(` merge: { mode: "auto", method: "repo-default", deleteBranch: "repo-default", bypass: false },`);
269
321
  lines.push(` issueAnalysis: { enabled: true, harness: "pi", mode: "continuous" },`);
@@ -422,7 +474,7 @@ function mapLegacySessionStatusToRunStatus(status) {
422
474
  case "error":
423
475
  return "failed";
424
476
  case "stopped":
425
- return "cancelled";
477
+ return "stopped";
426
478
  default:
427
479
  return "created";
428
480
  }
@@ -458,12 +510,15 @@ function mapTaskStatusFromRunStatus(status, fallback) {
458
510
  case "paused":
459
511
  return "in_progress";
460
512
  case "reviewing":
513
+ case "closing-out":
461
514
  return "under_review";
515
+ case "needs-attention":
516
+ return "blocked";
462
517
  case "completed":
463
518
  return "completed";
464
519
  case "failed":
465
520
  return "ready";
466
- case "cancelled":
521
+ case "stopped":
467
522
  return "cancelled";
468
523
  }
469
524
  }
@@ -1313,7 +1368,7 @@ function applyEngineEvent(snapshot, event) {
1313
1368
  ...base,
1314
1369
  runs: patchById(base.runs, payload.runId, (run) => ({
1315
1370
  ...run,
1316
- status: "cancelled",
1371
+ status: "stopped",
1317
1372
  updatedAt: payload.createdAt,
1318
1373
  completedAt: payload.createdAt
1319
1374
  }))
@@ -1,17 +1,145 @@
1
1
  // @bun
2
2
  // packages/core/src/load-config.ts
3
- import { existsSync, readFileSync } from "fs";
4
- import { join } from "path";
5
- import { pathToFileURL } from "url";
3
+ import { existsSync, mkdtempSync, readFileSync, rmSync } from "fs";
4
+ import { tmpdir } from "os";
5
+ import { dirname, join } from "path";
6
+ import { fileURLToPath, pathToFileURL } from "url";
7
+ import { Schema as Schema2 } from "effect";
8
+ import { RigConfig as RigConfig2 } from "@rig/contracts";
9
+
10
+ // packages/core/src/define-config.ts
6
11
  import { Schema } from "effect";
7
12
  import { RigConfig } from "@rig/contracts";
13
+ function normalizeWorkspaceConfig(raw) {
14
+ const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : { mainRepo: "." };
15
+ workspace.checkout = workspace.checkout ?? workspace.isolation ?? "worktree";
16
+ workspace.isolation = workspace.isolation ?? workspace.checkout;
17
+ workspace.sandbox = workspace.sandbox ?? "enforce";
18
+ return workspace;
19
+ }
20
+ function applyConfigDefaults(raw) {
21
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
22
+ return raw;
23
+ const record = raw;
24
+ return {
25
+ ...record,
26
+ plugins: Array.isArray(record.plugins) ? record.plugins : [],
27
+ workspace: normalizeWorkspaceConfig(record.workspace)
28
+ };
29
+ }
30
+
31
+ // packages/core/src/load-config.ts
8
32
  var TS_NAMES = ["rig.config.ts", "rig.config.mts"];
9
33
  var JSON_NAMES = ["rig.config.json"];
34
+ function isModuleResolutionError(error) {
35
+ const message = error instanceof Error ? error.message : String(error);
36
+ return message.includes("Cannot find module") || message.includes("Could not resolve");
37
+ }
38
+ function runningFromCompiledBinary() {
39
+ return import.meta.url.includes("$bunfs");
40
+ }
41
+ var runtimeBundleQueue = Promise.resolve();
42
+ function enqueueRuntimeBundle(operation) {
43
+ const next = runtimeBundleQueue.then(operation, operation);
44
+ runtimeBundleQueue = next.then(() => {
45
+ return;
46
+ }, () => {
47
+ return;
48
+ });
49
+ return next;
50
+ }
51
+ async function importConfigViaRuntimeBundleUnserialized(configPath) {
52
+ const bun = globalThis.Bun;
53
+ if (!bun?.build) {
54
+ throw new Error(`Failed to import ${configPath}: bare imports could not be resolved and no Bun.build runtime bundler is available.`);
55
+ }
56
+ const hostDir = (() => {
57
+ try {
58
+ return dirname(fileURLToPath(import.meta.url));
59
+ } catch {
60
+ return process.cwd();
61
+ }
62
+ })();
63
+ const configDir = dirname(configPath);
64
+ const UNRESOLVED_NAMESPACE = "rig-config-unresolved";
65
+ const hostResolverPlugin = {
66
+ name: "rig-host-package-resolver",
67
+ setup(build) {
68
+ build.onResolve({ filter: /^@rig\// }, (args) => {
69
+ const candidates = [
70
+ [args.path, configDir],
71
+ [args.path, hostDir],
72
+ [args.path.replace(/^@rig\//, "@h-rig/"), hostDir]
73
+ ];
74
+ for (const [specifier, parent] of candidates) {
75
+ try {
76
+ const resolved = bun.resolveSync?.(specifier, parent);
77
+ if (resolved)
78
+ return { path: resolved };
79
+ } catch {}
80
+ }
81
+ return;
82
+ });
83
+ build.onResolve({ filter: /.*/ }, (args) => {
84
+ if (/^(?:node|bun):/.test(args.path))
85
+ return;
86
+ const parent = args.importer ? dirname(args.importer) : configDir;
87
+ try {
88
+ const resolved = bun.resolveSync?.(args.path, parent);
89
+ if (resolved)
90
+ return { path: resolved };
91
+ } catch {
92
+ return { path: args.path, namespace: UNRESOLVED_NAMESPACE };
93
+ }
94
+ return;
95
+ });
96
+ build.onLoad({ filter: /.*/, namespace: UNRESOLVED_NAMESPACE }, (args) => ({
97
+ loader: "js",
98
+ contents: `module.exports = {};
99
+ throw new Error(${JSON.stringify(`Failed to bundle ${configPath}: Could not resolve: "${args.path}". Maybe you need to "bun install"?`)});
100
+ `
101
+ }));
102
+ }
103
+ };
104
+ const result = await bun.build({
105
+ entrypoints: [configPath],
106
+ target: "bun",
107
+ format: "esm",
108
+ throw: false,
109
+ plugins: [hostResolverPlugin]
110
+ });
111
+ if (!result.success || !result.outputs[0]) {
112
+ const detail = result.logs.map((log) => String(log)).join(`
113
+ `);
114
+ throw new Error(`Failed to bundle ${configPath}: ${detail || "unknown bundler error"}`);
115
+ }
116
+ const dir = mkdtempSync(join(tmpdir(), "rig-config-bundle-"));
117
+ try {
118
+ const bundledPath = join(dir, "rig.config.bundled.js");
119
+ await bun.write(bundledPath, await result.outputs[0].text());
120
+ return await import(pathToFileURL(bundledPath).href);
121
+ } finally {
122
+ try {
123
+ rmSync(dir, { recursive: true, force: true });
124
+ } catch {}
125
+ }
126
+ }
10
127
  async function loadConfig(cwd) {
11
128
  for (const name of TS_NAMES) {
12
129
  const p = join(cwd, name);
13
130
  if (existsSync(p)) {
14
- const mod = await import(pathToFileURL(p).href);
131
+ const mod = await enqueueRuntimeBundle(async () => {
132
+ if (runningFromCompiledBinary()) {
133
+ return importConfigViaRuntimeBundleUnserialized(p);
134
+ }
135
+ try {
136
+ return await import(pathToFileURL(p).href);
137
+ } catch (error) {
138
+ if (!isModuleResolutionError(error))
139
+ throw error;
140
+ return importConfigViaRuntimeBundleUnserialized(p);
141
+ }
142
+ });
15
143
  const raw = mod.default ?? mod.config;
16
144
  return decodePreservingRuntime(raw);
17
145
  }
@@ -35,7 +163,7 @@ function decodePreservingRuntime(raw) {
35
163
  }
36
164
  }
37
165
  }
38
- const decoded = Schema.decodeUnknownSync(RigConfig)(raw);
166
+ const decoded = Schema2.decodeUnknownSync(RigConfig2)(applyConfigDefaults(raw));
39
167
  const plugins = decoded.plugins.map((p) => {
40
168
  const runtime = runtimeByName.get(p.name);
41
169
  if (!runtime)
@@ -58,6 +58,24 @@ function assertRuntimeMatchesMetadata(plugin) {
58
58
  }
59
59
  }
60
60
  }
61
+ const declaredHooks = new Map((plugin.contributes?.hooks ?? []).map((hook) => [hook.id, hook]));
62
+ const runtimeHooks = plugin.__runtime?.hooks;
63
+ for (const hookId of Object.keys(runtimeHooks ?? {})) {
64
+ const metadata = declaredHooks.get(hookId);
65
+ if (!metadata) {
66
+ throw new Error(`plugin "${plugin.name}" typed hook "${hookId}" has no matching metadata entry in contributes.hooks`);
67
+ }
68
+ if (metadata.command) {
69
+ throw new Error(`plugin "${plugin.name}" hook "${hookId}" has both a typed implementation and a command string \u2014 pick one`);
70
+ }
71
+ }
72
+ if (runtimeHooks) {
73
+ for (const hook of declaredHooks.values()) {
74
+ if (!runtimeHooks[hook.id] && !hook.command) {
75
+ throw new Error(`plugin "${plugin.name}" hook metadata "${hook.id}" has no implementation (typed function or command)`);
76
+ }
77
+ }
78
+ }
61
79
  }
62
80
  function createPluginHost(plugins) {
63
81
  assertUniquePluginNames(plugins);
@@ -43,7 +43,7 @@ function buildRigInitConfigSource(input) {
43
43
  lines.push(` issueUpdates: "lifecycle",`);
44
44
  lines.push(` projects: { enabled: false },`);
45
45
  lines.push(` },`);
46
- lines.push(` automation: { maxValidationAttempts: 30, maxPrFixIterations: 30 },`);
46
+ lines.push(` automation: { maxValidationAttempts: 30, maxPrFixIterations: 100500 },`);
47
47
  lines.push(` pr: { mode: "auto", watchChecks: true, autoFixChecks: true, autoFixReview: true },`);
48
48
  lines.push(` merge: { mode: "auto", method: "repo-default", deleteBranch: "repo-default", bypass: false },`);
49
49
  lines.push(` issueAnalysis: { enabled: true, harness: "pi", mode: "continuous" },`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-rig/core",
3
- "version": "0.0.6-alpha.7",
3
+ "version": "0.0.6-alpha.70",
4
4
  "type": "module",
5
5
  "description": "Rig package",
6
6
  "license": "UNLICENSED",
@@ -31,7 +31,7 @@
31
31
  "main": "./dist/src/index.js",
32
32
  "module": "./dist/src/index.js",
33
33
  "dependencies": {
34
- "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.7",
34
+ "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.70",
35
35
  "effect": "4.0.0-beta.78"
36
36
  }
37
37
  }