@h-rig/core 0.0.6-alpha.16 → 0.0.6-alpha.160
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.
- package/dist/src/agent-role-registry.d.ts +4 -0
- package/dist/src/agent-role-registry.js +27 -0
- package/dist/src/authority-paths.d.ts +15 -0
- package/dist/src/authority-paths.js +80 -0
- package/dist/src/baked-secrets.d.ts +6 -0
- package/dist/src/baked-secrets.js +121 -0
- package/dist/src/build-time-config.d.ts +12 -0
- package/dist/src/build-time-config.js +25 -0
- package/dist/src/build-time-config.macro.d.ts +1 -0
- package/dist/src/capability-loaders.d.ts +51 -0
- package/dist/src/capability-loaders.js +749 -0
- package/dist/src/capability.d.ts +79 -0
- package/dist/src/capability.js +63 -0
- package/dist/src/checkout-root.d.ts +1 -0
- package/dist/src/checkout-root.js +30 -0
- package/dist/src/config-env.d.ts +4 -0
- package/dist/src/config-env.js +23 -0
- package/dist/src/config.d.ts +3 -0
- package/dist/src/config.js +44 -0
- package/dist/src/declarative-config.d.ts +14 -0
- package/dist/src/declarative-config.js +82 -0
- package/dist/src/define-config.d.ts +20 -0
- package/dist/src/define-config.js +28 -15
- package/dist/src/define-plugin.d.ts +13 -0
- package/dist/src/define-plugin.js +4 -43
- package/dist/src/embedded-plugins.d.ts +59 -0
- package/dist/src/embedded-plugins.js +22 -0
- package/dist/src/exec.d.ts +13 -0
- package/dist/src/exec.js +101 -0
- package/dist/src/harness-paths.d.ts +18 -0
- package/dist/src/harness-paths.js +141 -0
- package/dist/src/hook-materializer.d.ts +72 -0
- package/dist/src/hook-materializer.js +274 -0
- package/dist/src/hook-runner.d.ts +48 -0
- package/dist/src/hook-runner.js +752 -0
- package/dist/src/hook-runtime.d.ts +11 -0
- package/dist/src/hook-runtime.js +307 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +210 -2499
- package/dist/src/json-files.d.ts +9 -0
- package/dist/src/json-files.js +125 -0
- package/dist/src/kernel-entrypoint.d.ts +22 -0
- package/dist/src/kernel-entrypoint.js +537 -0
- package/dist/src/layout.d.ts +10 -0
- package/dist/src/layout.js +144 -0
- package/dist/src/load-config.d.ts +2 -0
- package/dist/src/load-config.js +421 -28
- package/dist/src/placement.d.ts +50 -0
- package/dist/src/placement.js +996 -0
- package/dist/src/plugin-host-context.d.ts +66 -0
- package/dist/src/plugin-host-context.js +1276 -0
- package/dist/src/plugin-host-registries.d.ts +31 -0
- package/dist/src/plugin-host-registries.js +79 -0
- package/dist/src/plugin-host.d.ts +77 -0
- package/dist/src/plugin-host.js +127 -63
- package/dist/src/plugin-runtime.d.ts +173 -0
- package/dist/src/profile-ops.d.ts +9 -0
- package/dist/src/profile-ops.js +252 -0
- package/dist/src/project-plugins.d.ts +63 -0
- package/dist/src/project-plugins.js +783 -0
- package/dist/src/remote-config.d.ts +183 -0
- package/dist/src/remote-config.js +574 -0
- package/dist/src/root-resolver.d.ts +5 -0
- package/dist/src/root-resolver.js +69 -0
- package/dist/src/run-provisioning.d.ts +58 -0
- package/dist/src/run-provisioning.js +128 -0
- package/dist/src/runtime-context.d.ts +20 -0
- package/dist/src/runtime-context.js +257 -0
- package/dist/src/runtime-events.d.ts +44 -0
- package/dist/src/runtime-events.js +212 -0
- package/dist/src/runtime-overlay.d.ts +11 -0
- package/dist/src/runtime-overlay.js +71 -0
- package/dist/src/runtime-paths.d.ts +21 -0
- package/dist/src/runtime-paths.js +181 -0
- package/dist/src/runtime-provisioning-env.d.ts +5 -0
- package/dist/src/runtime-provisioning-env.js +217 -0
- package/dist/src/runtime-runner-context.d.ts +12 -0
- package/dist/src/runtime-runner-context.js +1 -0
- package/dist/src/safe-identifiers.d.ts +44 -0
- package/dist/src/safe-identifiers.js +96 -0
- package/dist/src/scope-rules.d.ts +4 -0
- package/dist/src/scope-rules.js +21 -0
- package/dist/src/server-paths.d.ts +26 -0
- package/dist/src/server-paths.js +308 -0
- package/dist/src/setup-version.d.ts +3 -0
- package/dist/src/setup-version.js +14 -0
- package/dist/src/task-record-reader.d.ts +3 -0
- package/dist/src/task-record-reader.js +9 -0
- package/dist/src/validator-registry.d.ts +27 -0
- package/dist/src/validator-registry.js +64 -0
- package/package.json +146 -10
- package/dist/src/engineReadModelReducer.js +0 -1780
- package/dist/src/rig-init-builder.js +0 -57
- package/dist/src/rigSelectors.js +0 -293
- package/dist/src/taskGraph.js +0 -64
- package/dist/src/taskGraphCodes.js +0 -26
- package/dist/src/taskGraphLayout.js +0 -374
|
@@ -0,0 +1,1276 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/project-plugins.ts
|
|
3
|
+
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
4
|
+
import { resolve as resolve2 } from "path";
|
|
5
|
+
|
|
6
|
+
// packages/core/src/plugin-host.ts
|
|
7
|
+
function indexById(contributions, kind) {
|
|
8
|
+
const map = new Map;
|
|
9
|
+
const registrant = new Map;
|
|
10
|
+
for (const { item, pluginName } of contributions) {
|
|
11
|
+
if (map.has(item.id)) {
|
|
12
|
+
throw new Error(`duplicate ${kind} id "${item.id}": registered by plugins "${registrant.get(item.id)}" and "${pluginName}"`);
|
|
13
|
+
}
|
|
14
|
+
map.set(item.id, item);
|
|
15
|
+
registrant.set(item.id, pluginName);
|
|
16
|
+
}
|
|
17
|
+
return map;
|
|
18
|
+
}
|
|
19
|
+
function assertUniquePluginNames(plugins) {
|
|
20
|
+
const seen = new Set;
|
|
21
|
+
for (const plugin of plugins) {
|
|
22
|
+
if (seen.has(plugin.name)) {
|
|
23
|
+
throw new Error(`duplicate plugin name "${plugin.name}"`);
|
|
24
|
+
}
|
|
25
|
+
seen.add(plugin.name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createPluginHost(plugins) {
|
|
29
|
+
assertUniquePluginNames(plugins);
|
|
30
|
+
const validators = [];
|
|
31
|
+
const hooks = [];
|
|
32
|
+
const skills = [];
|
|
33
|
+
const repoSources = [];
|
|
34
|
+
const agentRoles = [];
|
|
35
|
+
const taskFieldExtensions = [];
|
|
36
|
+
const taskSources = [];
|
|
37
|
+
const cliCommands = [];
|
|
38
|
+
const capabilities = [];
|
|
39
|
+
const panels = [];
|
|
40
|
+
const blockerClassifiers = [];
|
|
41
|
+
const sessionExtensions = [];
|
|
42
|
+
const seedEntrypoints = [];
|
|
43
|
+
const stages = [];
|
|
44
|
+
const stageMutations = [];
|
|
45
|
+
const stageExecutors = {};
|
|
46
|
+
for (const plugin of plugins) {
|
|
47
|
+
const c = plugin.contributes;
|
|
48
|
+
if (!c)
|
|
49
|
+
continue;
|
|
50
|
+
const pluginName = plugin.name;
|
|
51
|
+
if (c.validators)
|
|
52
|
+
validators.push(...c.validators.map((item) => ({ item, pluginName })));
|
|
53
|
+
if (c.hooks)
|
|
54
|
+
hooks.push(...c.hooks.map((item) => ({ item, pluginName })));
|
|
55
|
+
if (c.skills)
|
|
56
|
+
skills.push(...c.skills.map((item) => ({ item, pluginName })));
|
|
57
|
+
if (c.repoSources)
|
|
58
|
+
repoSources.push(...c.repoSources.map((item) => ({ item, pluginName })));
|
|
59
|
+
if (c.agentRoles)
|
|
60
|
+
agentRoles.push(...c.agentRoles.map((item) => ({ item, pluginName })));
|
|
61
|
+
if (c.taskFieldSchemas)
|
|
62
|
+
taskFieldExtensions.push(...c.taskFieldSchemas.map((item) => ({ item, pluginName })));
|
|
63
|
+
if (c.taskSources)
|
|
64
|
+
taskSources.push(...c.taskSources.map((item) => ({ item, pluginName })));
|
|
65
|
+
if (c.cliCommands)
|
|
66
|
+
cliCommands.push(...c.cliCommands.map((item) => ({ item, pluginName })));
|
|
67
|
+
if (c.capabilities)
|
|
68
|
+
capabilities.push(...c.capabilities.map((item) => ({ item, pluginName })));
|
|
69
|
+
if (c.panels)
|
|
70
|
+
panels.push(...c.panels.map((item) => ({ item, pluginName })));
|
|
71
|
+
if (c.blockerClassifiers)
|
|
72
|
+
blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
|
|
73
|
+
if (c.sessionExtensions)
|
|
74
|
+
sessionExtensions.push(...c.sessionExtensions.map((item) => ({ item, pluginName })));
|
|
75
|
+
if (c.seedEntrypoints)
|
|
76
|
+
seedEntrypoints.push(...c.seedEntrypoints.map((item) => ({ item, pluginName })));
|
|
77
|
+
if (c.stageMutations)
|
|
78
|
+
stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
|
|
79
|
+
if (c.stages) {
|
|
80
|
+
for (const stage of c.stages) {
|
|
81
|
+
stages.push({ item: stage, pluginName });
|
|
82
|
+
if (stage.run)
|
|
83
|
+
stageExecutors[stage.id] = stage.run;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const validatorMap = indexById(validators, "validator");
|
|
88
|
+
const hookMap = indexById(hooks, "hook");
|
|
89
|
+
const skillMap = indexById(skills, "skill");
|
|
90
|
+
const repoSourceMap = indexById(repoSources, "repoSource");
|
|
91
|
+
const agentRoleMap = indexById(agentRoles, "agentRole");
|
|
92
|
+
const taskFieldExtMap = indexById(taskFieldExtensions, "taskFieldExtension");
|
|
93
|
+
const taskSourceMap = indexById(taskSources, "taskSource");
|
|
94
|
+
const cliCommandMap = indexById(cliCommands, "cliCommand");
|
|
95
|
+
const capabilityMap = indexById(capabilities, "capability");
|
|
96
|
+
const panelMap = indexById(panels, "panel");
|
|
97
|
+
const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
|
|
98
|
+
indexById(sessionExtensions, "sessionExtension");
|
|
99
|
+
indexById(seedEntrypoints, "seedEntrypoint");
|
|
100
|
+
const taskSourceFactoryByKind = new Map;
|
|
101
|
+
const taskSourceKindRegistrant = new Map;
|
|
102
|
+
for (const { item, pluginName } of taskSources) {
|
|
103
|
+
if (taskSourceFactoryByKind.has(item.kind)) {
|
|
104
|
+
throw new Error(`duplicate task source kind "${item.kind}": registered by plugins "${taskSourceKindRegistrant.get(item.kind)}" and "${pluginName}"`);
|
|
105
|
+
}
|
|
106
|
+
taskSourceFactoryByKind.set(item.kind, item);
|
|
107
|
+
taskSourceKindRegistrant.set(item.kind, pluginName);
|
|
108
|
+
}
|
|
109
|
+
const seedEntrypointByBasename = new Map;
|
|
110
|
+
const seedEntrypointByWorkerArg = new Map;
|
|
111
|
+
let insidePtySeedEntrypoint;
|
|
112
|
+
const registerSeedEntrypointSelector = (map, selectorKind, selector, contribution) => {
|
|
113
|
+
const existing = map.get(selector);
|
|
114
|
+
if (existing) {
|
|
115
|
+
throw new Error(`duplicate seed entrypoint ${selectorKind} "${selector}": registered by entrypoint "${existing.item.id}" from plugin "${existing.pluginName}" and entrypoint "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
|
|
116
|
+
}
|
|
117
|
+
map.set(selector, contribution);
|
|
118
|
+
};
|
|
119
|
+
for (const contribution of seedEntrypoints) {
|
|
120
|
+
const entrypoint = contribution.item;
|
|
121
|
+
let selectorCount = 0;
|
|
122
|
+
if (entrypoint.basename) {
|
|
123
|
+
selectorCount += 1;
|
|
124
|
+
registerSeedEntrypointSelector(seedEntrypointByBasename, "basename", entrypoint.basename, contribution);
|
|
125
|
+
}
|
|
126
|
+
if (entrypoint.workerArg) {
|
|
127
|
+
selectorCount += 1;
|
|
128
|
+
registerSeedEntrypointSelector(seedEntrypointByWorkerArg, "workerArg", entrypoint.workerArg, contribution);
|
|
129
|
+
}
|
|
130
|
+
if (entrypoint.insidePty) {
|
|
131
|
+
selectorCount += 1;
|
|
132
|
+
if (insidePtySeedEntrypoint) {
|
|
133
|
+
throw new Error(`duplicate seed entrypoint inside-pty selector: registered by entrypoint "${insidePtySeedEntrypoint.item.id}" from plugin "${insidePtySeedEntrypoint.pluginName}" and entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}"`);
|
|
134
|
+
}
|
|
135
|
+
insidePtySeedEntrypoint = contribution;
|
|
136
|
+
}
|
|
137
|
+
if (selectorCount === 0) {
|
|
138
|
+
throw new Error(`seed entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}" must declare at least one selector (basename, workerArg, or insidePty)`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const allValidators = validators.map((c) => c.item);
|
|
142
|
+
const allHooks = hooks.map((c) => c.item);
|
|
143
|
+
const allSkills = skills.map((c) => c.item);
|
|
144
|
+
const allRepoSources = repoSources.map((c) => c.item);
|
|
145
|
+
const allAgentRoles = agentRoles.map((c) => c.item);
|
|
146
|
+
const allTaskFieldExtensions = taskFieldExtensions.map((c) => c.item);
|
|
147
|
+
const allTaskSources = taskSources.map((c) => c.item);
|
|
148
|
+
const allCliCommands = cliCommands.map((c) => c.item);
|
|
149
|
+
const allStageMutations = stageMutations.map((c) => c.item);
|
|
150
|
+
const allStages = stages.map((c) => c.item);
|
|
151
|
+
const allCapabilities = capabilities.map((c) => c.item);
|
|
152
|
+
const allPanels = panels.map((c) => c.item);
|
|
153
|
+
const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
|
|
154
|
+
const allSessionExtensions = sessionExtensions.map((c) => c.item);
|
|
155
|
+
const allSeedEntrypoints = seedEntrypoints.map((c) => c.item);
|
|
156
|
+
const executableCliCommandByName = new Map;
|
|
157
|
+
const registerExecutableCliCommandSelector = (selector, contribution) => {
|
|
158
|
+
const existing = executableCliCommandByName.get(selector);
|
|
159
|
+
if (existing && existing.item.id !== contribution.item.id) {
|
|
160
|
+
throw new Error(`duplicate executable CLI selector "${selector}" registered by command "${existing.item.id}" from plugin "${existing.pluginName}" and command "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
|
|
161
|
+
}
|
|
162
|
+
executableCliCommandByName.set(selector, contribution);
|
|
163
|
+
};
|
|
164
|
+
for (const contribution of cliCommands) {
|
|
165
|
+
const command = contribution.item;
|
|
166
|
+
const family = command.family ?? command.id;
|
|
167
|
+
registerExecutableCliCommandSelector(command.id, contribution);
|
|
168
|
+
registerExecutableCliCommandSelector(family, contribution);
|
|
169
|
+
for (const alias of command.aliases ?? []) {
|
|
170
|
+
registerExecutableCliCommandSelector(alias, contribution);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
let defaultRootCliCommand;
|
|
174
|
+
for (const contribution of cliCommands) {
|
|
175
|
+
if (!contribution.item.rootDefault)
|
|
176
|
+
continue;
|
|
177
|
+
if (defaultRootCliCommand) {
|
|
178
|
+
throw new Error(`duplicate default root CLI command registered by command "${defaultRootCliCommand.item.id}" from plugin "${defaultRootCliCommand.pluginName}" and command "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
|
|
179
|
+
}
|
|
180
|
+
defaultRootCliCommand = contribution;
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
getValidator: (id) => validatorMap.get(id),
|
|
184
|
+
getHook: (id) => hookMap.get(id),
|
|
185
|
+
getSkill: (id) => skillMap.get(id),
|
|
186
|
+
getRepoSource: (id) => repoSourceMap.get(id),
|
|
187
|
+
getAgentRole: (id) => agentRoleMap.get(id),
|
|
188
|
+
getTaskFieldExtension: (id) => taskFieldExtMap.get(id),
|
|
189
|
+
getTaskSource: (id) => taskSourceMap.get(id),
|
|
190
|
+
getCliCommand: (id) => cliCommandMap.get(id),
|
|
191
|
+
getCapability: (id) => capabilityMap.get(id),
|
|
192
|
+
getPanel: (id) => panelMap.get(id),
|
|
193
|
+
getBlockerClassifier: (id) => blockerClassifierMap.get(id),
|
|
194
|
+
listValidators: () => allValidators,
|
|
195
|
+
listHooks: () => allHooks,
|
|
196
|
+
listSkills: () => allSkills,
|
|
197
|
+
listRepoSources: () => allRepoSources,
|
|
198
|
+
listAgentRoles: () => allAgentRoles,
|
|
199
|
+
listTaskFieldExtensions: () => allTaskFieldExtensions,
|
|
200
|
+
listTaskSources: () => allTaskSources,
|
|
201
|
+
listCliCommands: () => allCliCommands,
|
|
202
|
+
listCapabilities: () => allCapabilities,
|
|
203
|
+
listPanels: () => allPanels,
|
|
204
|
+
listBlockerClassifiers: () => allBlockerClassifiers,
|
|
205
|
+
listStages: () => allStages,
|
|
206
|
+
listStageMutations: () => allStageMutations,
|
|
207
|
+
listStageExecutors: () => stageExecutors,
|
|
208
|
+
listExecutableValidators: () => allValidators.filter((v) => typeof v.run === "function"),
|
|
209
|
+
listExecutableTaskSources: () => allTaskSources,
|
|
210
|
+
listExecutableCapabilities: () => allCapabilities.filter((c) => typeof c.run === "function"),
|
|
211
|
+
listExecutablePanels: () => allPanels.filter((p) => typeof p.produce === "function"),
|
|
212
|
+
listExecutableBlockerClassifiers: () => allBlockerClassifiers,
|
|
213
|
+
listExecutableCliCommands: () => allCliCommands,
|
|
214
|
+
listSessionExtensions: () => allSessionExtensions,
|
|
215
|
+
listSeedEntrypoints: () => allSeedEntrypoints,
|
|
216
|
+
resolveSeedEntrypointByBasename: (value) => seedEntrypointByBasename.get(value)?.item,
|
|
217
|
+
resolveSeedEntrypointByWorkerArg: (value) => seedEntrypointByWorkerArg.get(value)?.item,
|
|
218
|
+
resolveInsidePtySeedEntrypoint: () => insidePtySeedEntrypoint?.item,
|
|
219
|
+
resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
|
|
220
|
+
resolveDefaultRootCliCommand: () => defaultRootCliCommand?.item,
|
|
221
|
+
resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// packages/core/src/load-config.ts
|
|
226
|
+
import { existsSync as existsSync2, mkdirSync, mkdtempSync, readFileSync as readFileSync2, readdirSync, rmSync, statSync } from "fs";
|
|
227
|
+
import { isBuiltin } from "module";
|
|
228
|
+
import { dirname, isAbsolute, join as join2, relative, resolve } from "path";
|
|
229
|
+
import { pathToFileURL } from "url";
|
|
230
|
+
import { Schema as Schema3 } from "effect";
|
|
231
|
+
import { RigConfig as RigConfig3 } from "@rig/contracts";
|
|
232
|
+
|
|
233
|
+
// packages/core/src/define-config.ts
|
|
234
|
+
import { Schema } from "effect";
|
|
235
|
+
import { RigConfig } from "@rig/contracts";
|
|
236
|
+
function normalizeWorkspaceConfig(raw) {
|
|
237
|
+
const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : {};
|
|
238
|
+
if (workspace.mainRepo === undefined)
|
|
239
|
+
workspace.mainRepo = ".";
|
|
240
|
+
if (workspace.checkout === undefined && workspace.isolation !== undefined)
|
|
241
|
+
workspace.checkout = workspace.isolation;
|
|
242
|
+
if (workspace.isolation === undefined && workspace.checkout !== undefined)
|
|
243
|
+
workspace.isolation = workspace.checkout;
|
|
244
|
+
return workspace;
|
|
245
|
+
}
|
|
246
|
+
function applyConfigDefaults(raw) {
|
|
247
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
248
|
+
return raw;
|
|
249
|
+
const record = raw;
|
|
250
|
+
return {
|
|
251
|
+
...record,
|
|
252
|
+
plugins: Array.isArray(record.plugins) ? record.plugins.flat() : [],
|
|
253
|
+
workspace: normalizeWorkspaceConfig(record.workspace)
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// packages/core/src/declarative-config.ts
|
|
258
|
+
import { existsSync, readFileSync } from "fs";
|
|
259
|
+
import { join } from "path";
|
|
260
|
+
import { parse as parseToml } from "smol-toml";
|
|
261
|
+
import { Schema as Schema2 } from "effect";
|
|
262
|
+
import { RigConfig as RigConfig2 } from "@rig/contracts";
|
|
263
|
+
|
|
264
|
+
// packages/core/src/embedded-plugins.ts
|
|
265
|
+
var registered = null;
|
|
266
|
+
function getStandardPluginsResolver() {
|
|
267
|
+
return registered;
|
|
268
|
+
}
|
|
269
|
+
var bakedProjectConfig = null;
|
|
270
|
+
function getEmbeddedProjectConfig() {
|
|
271
|
+
return bakedProjectConfig;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// packages/core/src/declarative-config.ts
|
|
275
|
+
var DECLARATIVE_CONFIG_NAMES = ["rigfig.toml", "rigfig.json"];
|
|
276
|
+
function findDeclarativeConfigPath(cwd) {
|
|
277
|
+
const dir = join(cwd, ".rig");
|
|
278
|
+
for (const name of DECLARATIVE_CONFIG_NAMES) {
|
|
279
|
+
const candidate = join(dir, name);
|
|
280
|
+
if (existsSync(candidate))
|
|
281
|
+
return candidate;
|
|
282
|
+
}
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
function parseDeclarativeFile(path) {
|
|
286
|
+
const raw = readFileSync(path, "utf8");
|
|
287
|
+
const parsed = path.endsWith(".json") ? JSON.parse(raw) : parseToml(raw);
|
|
288
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
289
|
+
throw new Error(`Declarative config ${path} must parse to an object.`);
|
|
290
|
+
}
|
|
291
|
+
return parsed;
|
|
292
|
+
}
|
|
293
|
+
function loadDeclarativeConfig(path) {
|
|
294
|
+
const data = parseDeclarativeFile(path);
|
|
295
|
+
const standardSection = data.standard && typeof data.standard === "object" && !Array.isArray(data.standard) ? data.standard : {};
|
|
296
|
+
const useStandard = standardSection.enabled !== false;
|
|
297
|
+
let plugins = [];
|
|
298
|
+
if (useStandard) {
|
|
299
|
+
const resolver = getStandardPluginsResolver();
|
|
300
|
+
if (!resolver) {
|
|
301
|
+
throw new Error(`Declarative config ${path} needs the embedded standard plugins, but none were registered. ` + `This is a seed/boot wiring error (the binary entrypoint must import the standard-plugin registration).`);
|
|
302
|
+
}
|
|
303
|
+
plugins = resolver(data);
|
|
304
|
+
}
|
|
305
|
+
const { standard: _standardDirective, ...configFields } = data;
|
|
306
|
+
const withDefaults = applyConfigDefaults({ ...configFields, plugins });
|
|
307
|
+
const explicitPlugins = Array.isArray(withDefaults.plugins) ? [...withDefaults.plugins] : [];
|
|
308
|
+
const decoded = Schema2.decodeUnknownSync(RigConfig2)({
|
|
309
|
+
...withDefaults,
|
|
310
|
+
plugins: explicitPlugins
|
|
311
|
+
});
|
|
312
|
+
return { ...decoded, plugins: explicitPlugins };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// packages/core/src/load-config.ts
|
|
316
|
+
var TS_NAMES = ["rig.config.ts", "rig.config.mts"];
|
|
317
|
+
var JSON_NAMES = ["rig.config.json"];
|
|
318
|
+
function isModuleResolutionError(error) {
|
|
319
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
320
|
+
return message.includes("Cannot find module") || message.includes("Could not resolve");
|
|
321
|
+
}
|
|
322
|
+
function runningFromCompiledBinary() {
|
|
323
|
+
return import.meta.url.includes("$bunfs");
|
|
324
|
+
}
|
|
325
|
+
function packageNameAndSubpath(specifier) {
|
|
326
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || /^[a-zA-Z]+:/.test(specifier))
|
|
327
|
+
return null;
|
|
328
|
+
const parts = specifier.split("/");
|
|
329
|
+
const packageName = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
330
|
+
if (!packageName)
|
|
331
|
+
return null;
|
|
332
|
+
const rest = parts.slice(specifier.startsWith("@") ? 2 : 1).join("/");
|
|
333
|
+
return { packageName, subpath: rest ? `./${rest}` : "." };
|
|
334
|
+
}
|
|
335
|
+
function exportTargetFromEntry(entry) {
|
|
336
|
+
if (typeof entry === "string")
|
|
337
|
+
return entry;
|
|
338
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
339
|
+
const conditions = entry;
|
|
340
|
+
for (const key of ["bun", "import", "default", "require"]) {
|
|
341
|
+
if (typeof conditions[key] === "string")
|
|
342
|
+
return conditions[key];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
function patternExportTarget(record, subpath) {
|
|
348
|
+
for (const [pattern, entry] of Object.entries(record)) {
|
|
349
|
+
if (!pattern.includes("*"))
|
|
350
|
+
continue;
|
|
351
|
+
const [prefix = "", suffix = ""] = pattern.split("*");
|
|
352
|
+
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
|
|
353
|
+
continue;
|
|
354
|
+
const replacement = subpath.slice(prefix.length, subpath.length - suffix.length);
|
|
355
|
+
const target = exportTargetFromEntry(entry);
|
|
356
|
+
if (target)
|
|
357
|
+
return target.replace("*", replacement);
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
function exportTargetFromPackageJson(pkg, subpath) {
|
|
362
|
+
const exportsField = pkg.exports;
|
|
363
|
+
const target = (() => {
|
|
364
|
+
if (typeof exportsField === "string" && subpath === ".")
|
|
365
|
+
return exportsField;
|
|
366
|
+
if (!exportsField || typeof exportsField !== "object" || Array.isArray(exportsField))
|
|
367
|
+
return null;
|
|
368
|
+
const record = exportsField;
|
|
369
|
+
return exportTargetFromEntry(record[subpath] ?? (subpath === "." ? record["."] : undefined)) ?? patternExportTarget(record, subpath);
|
|
370
|
+
})();
|
|
371
|
+
if (target)
|
|
372
|
+
return target;
|
|
373
|
+
return subpath === "." && typeof pkg.module === "string" ? pkg.module : subpath === "." && typeof pkg.main === "string" ? pkg.main : null;
|
|
374
|
+
}
|
|
375
|
+
function resolvePackageDirFromBunStore(packageName, nodeModulesDir) {
|
|
376
|
+
const storeDir = join2(nodeModulesDir, ".bun");
|
|
377
|
+
if (!existsSync2(storeDir))
|
|
378
|
+
return null;
|
|
379
|
+
const encoded = packageName.replace("/", "+");
|
|
380
|
+
try {
|
|
381
|
+
const candidates = readdirSync(storeDir).filter((entry) => entry.startsWith(`${encoded}@`)).map((entry) => {
|
|
382
|
+
const candidateDir = join2(storeDir, entry, "node_modules", packageName);
|
|
383
|
+
const packageJsonPath = join2(candidateDir, "package.json");
|
|
384
|
+
if (!existsSync2(packageJsonPath))
|
|
385
|
+
return null;
|
|
386
|
+
try {
|
|
387
|
+
const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
388
|
+
return {
|
|
389
|
+
dir: candidateDir,
|
|
390
|
+
sortKey: pkg.version?.trim() || entry
|
|
391
|
+
};
|
|
392
|
+
} catch {
|
|
393
|
+
return {
|
|
394
|
+
dir: candidateDir,
|
|
395
|
+
sortKey: entry
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
}).filter((candidate) => candidate !== null).sort((a, b) => b.sortKey.localeCompare(a.sortKey, undefined, { numeric: true, sensitivity: "base" }));
|
|
399
|
+
return candidates[0]?.dir ?? null;
|
|
400
|
+
} catch {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function resolveDirectoryModulePath(directoryPath) {
|
|
405
|
+
const packageJsonPath = join2(directoryPath, "package.json");
|
|
406
|
+
if (existsSync2(packageJsonPath)) {
|
|
407
|
+
try {
|
|
408
|
+
const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
409
|
+
const target = exportTargetFromPackageJson(pkg, ".");
|
|
410
|
+
if (target) {
|
|
411
|
+
const resolved = resolveModulePath(join2(directoryPath, target));
|
|
412
|
+
if (resolved)
|
|
413
|
+
return resolved;
|
|
414
|
+
}
|
|
415
|
+
} catch {}
|
|
416
|
+
}
|
|
417
|
+
for (const candidate of ["index.js", "index.mjs", "index.cjs", "index.ts", "index.json"]) {
|
|
418
|
+
const resolved = resolveModulePath(join2(directoryPath, candidate));
|
|
419
|
+
if (resolved)
|
|
420
|
+
return resolved;
|
|
421
|
+
}
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
function resolveModulePath(candidatePath) {
|
|
425
|
+
if (!existsSync2(candidatePath)) {
|
|
426
|
+
for (const extension of [".ts", ".mts", ".tsx", ".js", ".mjs", ".cjs", ".json"]) {
|
|
427
|
+
const withExtension = `${candidatePath}${extension}`;
|
|
428
|
+
if (existsSync2(withExtension))
|
|
429
|
+
return resolveModulePath(withExtension);
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
const stat = statSync(candidatePath);
|
|
435
|
+
if (stat.isFile())
|
|
436
|
+
return candidatePath;
|
|
437
|
+
if (stat.isDirectory())
|
|
438
|
+
return resolveDirectoryModulePath(candidatePath);
|
|
439
|
+
} catch {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
function resolvePackageExportFromDir(packageDir, subpath) {
|
|
445
|
+
const packageJsonPath = join2(packageDir, "package.json");
|
|
446
|
+
if (existsSync2(packageJsonPath)) {
|
|
447
|
+
try {
|
|
448
|
+
const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
449
|
+
const target = exportTargetFromPackageJson(pkg, subpath);
|
|
450
|
+
if (target) {
|
|
451
|
+
const resolved = resolveModulePath(join2(packageDir, target));
|
|
452
|
+
if (resolved)
|
|
453
|
+
return resolved;
|
|
454
|
+
}
|
|
455
|
+
} catch {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (subpath !== ".") {
|
|
460
|
+
const legacySubpath = subpath.replace(/^\.\//, "");
|
|
461
|
+
for (const candidate of [
|
|
462
|
+
join2(packageDir, legacySubpath),
|
|
463
|
+
join2(packageDir, `${legacySubpath}.js`),
|
|
464
|
+
join2(packageDir, `${legacySubpath}.mjs`),
|
|
465
|
+
join2(packageDir, `${legacySubpath}.cjs`),
|
|
466
|
+
join2(packageDir, `${legacySubpath}.ts`),
|
|
467
|
+
join2(packageDir, `${legacySubpath}.json`)
|
|
468
|
+
]) {
|
|
469
|
+
const resolved = resolveModulePath(candidate);
|
|
470
|
+
if (resolved)
|
|
471
|
+
return resolved;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return subpath === "." ? resolveDirectoryModulePath(packageDir) : null;
|
|
475
|
+
}
|
|
476
|
+
var runtimeBundleQueue = Promise.resolve();
|
|
477
|
+
function enqueueRuntimeBundle(operation) {
|
|
478
|
+
const next = runtimeBundleQueue.then(operation, operation);
|
|
479
|
+
runtimeBundleQueue = next.then(() => {
|
|
480
|
+
return;
|
|
481
|
+
}, () => {
|
|
482
|
+
return;
|
|
483
|
+
});
|
|
484
|
+
return next;
|
|
485
|
+
}
|
|
486
|
+
function isWithinDir(candidatePath, rootPath) {
|
|
487
|
+
const rel = relative(resolve(rootPath), resolve(candidatePath));
|
|
488
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
489
|
+
}
|
|
490
|
+
function resolvedFilePath(path, rootPath) {
|
|
491
|
+
if (!path || !isAbsolute(path))
|
|
492
|
+
return null;
|
|
493
|
+
const resolved = resolveModulePath(path);
|
|
494
|
+
if (!resolved)
|
|
495
|
+
return null;
|
|
496
|
+
return rootPath && !isWithinDir(resolved, rootPath) ? null : resolved;
|
|
497
|
+
}
|
|
498
|
+
function resolveProjectPackageImport(specifier, configDir) {
|
|
499
|
+
const parsed = packageNameAndSubpath(specifier);
|
|
500
|
+
if (!parsed)
|
|
501
|
+
return null;
|
|
502
|
+
const nodeModulesDir = join2(configDir, "node_modules");
|
|
503
|
+
const directPackageDir = join2(nodeModulesDir, parsed.packageName);
|
|
504
|
+
const packageDir = existsSync2(join2(directPackageDir, "package.json")) ? directPackageDir : resolvePackageDirFromBunStore(parsed.packageName, nodeModulesDir);
|
|
505
|
+
return packageDir ? resolvePackageExportFromDir(packageDir, parsed.subpath) : null;
|
|
506
|
+
}
|
|
507
|
+
function canImportRigWorkspacePackagesDirectly(source, configDir) {
|
|
508
|
+
const packageNames = new Set;
|
|
509
|
+
for (const match of source.matchAll(/(?:from\s*|import\s*\(\s*|import\s*)["'](@rig\/[^"']+)["']/g)) {
|
|
510
|
+
const parsed = packageNameAndSubpath(match[1] ?? "");
|
|
511
|
+
if (parsed?.packageName)
|
|
512
|
+
packageNames.add(parsed.packageName);
|
|
513
|
+
}
|
|
514
|
+
if (packageNames.size === 0)
|
|
515
|
+
return false;
|
|
516
|
+
for (const packageName of packageNames) {
|
|
517
|
+
const shortName = packageName.replace(/^@rig\//, "");
|
|
518
|
+
const manifestPath = join2(configDir, "packages", shortName, "package.json");
|
|
519
|
+
if (!existsSync2(manifestPath))
|
|
520
|
+
return false;
|
|
521
|
+
try {
|
|
522
|
+
const parsed = JSON.parse(readFileSync2(manifestPath, "utf8"));
|
|
523
|
+
if (parsed.name !== packageName)
|
|
524
|
+
return false;
|
|
525
|
+
} catch {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
async function importConfigViaRuntimeBundleUnserialized(configPath) {
|
|
532
|
+
const bun = globalThis.Bun;
|
|
533
|
+
if (!bun?.build) {
|
|
534
|
+
throw new Error(`Failed to import ${configPath}: bare imports could not be resolved and no Bun.build runtime bundler is available.`);
|
|
535
|
+
}
|
|
536
|
+
const RUNTIME_ONLY_EXTERNAL_PACKAGES = new Set(["effect", "mupdf", "fastembed", "onnxruntime-node", "markit-ai"]);
|
|
537
|
+
const configDir = dirname(configPath);
|
|
538
|
+
const UNRESOLVED_NAMESPACE = "rig-config-unresolved";
|
|
539
|
+
const unresolvedLocalPlugin = {
|
|
540
|
+
name: "rig-config-unresolved-local",
|
|
541
|
+
setup(build) {
|
|
542
|
+
build.onLoad({ filter: /\.(?:html|txt)$/ }, (args) => ({
|
|
543
|
+
loader: "js",
|
|
544
|
+
contents: `export default ${JSON.stringify(readFileSync2(args.path, "utf8"))};
|
|
545
|
+
`
|
|
546
|
+
}));
|
|
547
|
+
build.onResolve({ filter: /.*/ }, (args) => {
|
|
548
|
+
const directFilePath = resolvedFilePath(args.path, configDir);
|
|
549
|
+
if (directFilePath)
|
|
550
|
+
return { path: directFilePath };
|
|
551
|
+
const packageImport = packageNameAndSubpath(args.path);
|
|
552
|
+
if (packageImport && (packageImport.packageName.startsWith("@rig/") || RUNTIME_ONLY_EXTERNAL_PACKAGES.has(packageImport.packageName))) {
|
|
553
|
+
return { path: args.path, external: true };
|
|
554
|
+
}
|
|
555
|
+
const projectPackagePath = resolveProjectPackageImport(args.path, configDir);
|
|
556
|
+
if (projectPackagePath)
|
|
557
|
+
return { path: projectPackagePath };
|
|
558
|
+
if (/^(?:node|bun):/.test(args.path) || isBuiltin(args.path))
|
|
559
|
+
return;
|
|
560
|
+
if (packageImport)
|
|
561
|
+
return { path: args.path, external: true };
|
|
562
|
+
const parentCandidates = args.path.startsWith(".") ? [args.importer && isAbsolute(args.importer) ? dirname(args.importer) : null, configDir].filter((value) => Boolean(value)) : [args.importer && isAbsolute(args.importer) ? dirname(args.importer) : configDir];
|
|
563
|
+
for (const parent2 of parentCandidates) {
|
|
564
|
+
const filePath = resolvedFilePath(resolve(parent2, args.path), configDir);
|
|
565
|
+
if (filePath)
|
|
566
|
+
return { path: filePath };
|
|
567
|
+
}
|
|
568
|
+
const parent = parentCandidates[0] ?? configDir;
|
|
569
|
+
try {
|
|
570
|
+
const resolved = bun.resolveSync?.(args.path, parent) ?? resolve(parent, args.path);
|
|
571
|
+
const filePath = resolvedFilePath(resolved, configDir);
|
|
572
|
+
if (filePath)
|
|
573
|
+
return { path: filePath };
|
|
574
|
+
} catch {}
|
|
575
|
+
return { path: args.path, namespace: UNRESOLVED_NAMESPACE };
|
|
576
|
+
});
|
|
577
|
+
build.onLoad({ filter: /.*/, namespace: UNRESOLVED_NAMESPACE }, (args) => ({
|
|
578
|
+
loader: "js",
|
|
579
|
+
contents: `module.exports = {};
|
|
580
|
+
throw new Error(${JSON.stringify(`Failed to bundle ${configPath}: Could not resolve local import "${args.path}". Maybe you need to fix a relative import in rig.config.ts or install project deps.`)});
|
|
581
|
+
`
|
|
582
|
+
}));
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
const result = await bun.build({
|
|
586
|
+
entrypoints: [configPath],
|
|
587
|
+
target: "bun",
|
|
588
|
+
external: ["effect", "mupdf", "fastembed", "onnxruntime-node", "markit-ai"],
|
|
589
|
+
format: "esm",
|
|
590
|
+
throw: false,
|
|
591
|
+
packages: "bundle",
|
|
592
|
+
plugins: [unresolvedLocalPlugin]
|
|
593
|
+
});
|
|
594
|
+
if (!result.success || !result.outputs[0]) {
|
|
595
|
+
const detail = result.logs.map((log) => String(log)).join(`
|
|
596
|
+
`);
|
|
597
|
+
throw new Error(`Failed to bundle ${configPath}: ${detail || "unknown bundler error"}`);
|
|
598
|
+
}
|
|
599
|
+
const bundleParentDir = join2(configDir, ".rig", "tmp");
|
|
600
|
+
mkdirSync(bundleParentDir, { recursive: true });
|
|
601
|
+
const dir = mkdtempSync(join2(bundleParentDir, "rig-config-bundle-"));
|
|
602
|
+
try {
|
|
603
|
+
const bundledPath = join2(dir, "rig.config.bundled.js");
|
|
604
|
+
await bun.write(bundledPath, await result.outputs[0].text());
|
|
605
|
+
return await import(pathToFileURL(bundledPath).href);
|
|
606
|
+
} finally {
|
|
607
|
+
try {
|
|
608
|
+
rmSync(dir, { recursive: true, force: true });
|
|
609
|
+
} catch {}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async function loadConfig(cwd) {
|
|
613
|
+
const baked = getEmbeddedProjectConfig();
|
|
614
|
+
if (baked && resolve(baked.projectRoot) === resolve(cwd)) {
|
|
615
|
+
return decodeExplicitPluginConfig(baked.raw);
|
|
616
|
+
}
|
|
617
|
+
for (const name of TS_NAMES) {
|
|
618
|
+
const p = join2(cwd, name);
|
|
619
|
+
if (existsSync2(p)) {
|
|
620
|
+
const mod = await enqueueRuntimeBundle(async () => {
|
|
621
|
+
if (runningFromCompiledBinary()) {
|
|
622
|
+
return importConfigViaRuntimeBundleUnserialized(p);
|
|
623
|
+
}
|
|
624
|
+
const source = readFileSync2(p, "utf8");
|
|
625
|
+
const importsRigHostPackages = /(?:import\s+[^;]*?from\s*|import\s*\()\s*["']@rig\//.test(source);
|
|
626
|
+
if (importsRigHostPackages && !canImportRigWorkspacePackagesDirectly(source, cwd)) {
|
|
627
|
+
return importConfigViaRuntimeBundleUnserialized(p);
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
return await import(pathToFileURL(p).href);
|
|
631
|
+
} catch (error) {
|
|
632
|
+
if (!isModuleResolutionError(error))
|
|
633
|
+
throw error;
|
|
634
|
+
return importConfigViaRuntimeBundleUnserialized(p);
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
const raw = mod.default ?? mod.config;
|
|
638
|
+
return decodeExplicitPluginConfig(raw);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
const declarativePath = findDeclarativeConfigPath(cwd);
|
|
642
|
+
if (declarativePath) {
|
|
643
|
+
return loadDeclarativeConfig(declarativePath);
|
|
644
|
+
}
|
|
645
|
+
for (const name of JSON_NAMES) {
|
|
646
|
+
const p = join2(cwd, name);
|
|
647
|
+
if (existsSync2(p)) {
|
|
648
|
+
const raw = JSON.parse(readFileSync2(p, "utf-8"));
|
|
649
|
+
return decodeExplicitPluginConfig(raw);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
throw new Error(`no rig.config.{ts,mts,json} or .rig/rigfig.{toml,json} found in ${cwd}`);
|
|
653
|
+
}
|
|
654
|
+
function decodeExplicitPluginConfig(raw) {
|
|
655
|
+
const withDefaults = applyConfigDefaults(raw);
|
|
656
|
+
const explicitPlugins = Array.isArray(withDefaults.plugins) ? [...withDefaults.plugins] : [];
|
|
657
|
+
const decoded = Schema3.decodeUnknownSync(RigConfig3)({
|
|
658
|
+
...withDefaults,
|
|
659
|
+
plugins: explicitPlugins
|
|
660
|
+
});
|
|
661
|
+
return { ...decoded, plugins: explicitPlugins };
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// packages/core/src/project-plugins.ts
|
|
665
|
+
var RIG_CONFIG_FILENAMES = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
666
|
+
function findProjectConfigPath(projectRoot) {
|
|
667
|
+
for (const name of RIG_CONFIG_FILENAMES) {
|
|
668
|
+
const path = resolve2(projectRoot, name);
|
|
669
|
+
if (existsSync3(path))
|
|
670
|
+
return path;
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
var pluginHostCache = new Map;
|
|
675
|
+
function configFileMtimeMs(normalizedRoot) {
|
|
676
|
+
const path = findProjectConfigPath(normalizedRoot);
|
|
677
|
+
if (!path)
|
|
678
|
+
return 0;
|
|
679
|
+
try {
|
|
680
|
+
return statSync2(path).mtimeMs;
|
|
681
|
+
} catch {
|
|
682
|
+
return 0;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function resolvePluginHost(projectRoot, options = {}) {
|
|
686
|
+
const normalizedRoot = resolve2(projectRoot);
|
|
687
|
+
const build = (load) => load(normalizedRoot).then((config) => ({
|
|
688
|
+
host: createPluginHost(config.plugins),
|
|
689
|
+
config
|
|
690
|
+
}));
|
|
691
|
+
if (options.load)
|
|
692
|
+
return build(options.load);
|
|
693
|
+
const mtimeMs = configFileMtimeMs(normalizedRoot);
|
|
694
|
+
const cached = pluginHostCache.get(normalizedRoot);
|
|
695
|
+
if (cached && cached.mtimeMs === mtimeMs)
|
|
696
|
+
return cached.promise;
|
|
697
|
+
const promise = build(loadConfig);
|
|
698
|
+
promise.catch(() => {
|
|
699
|
+
if (pluginHostCache.get(normalizedRoot)?.promise === promise) {
|
|
700
|
+
pluginHostCache.delete(normalizedRoot);
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
pluginHostCache.set(normalizedRoot, { mtimeMs, promise });
|
|
704
|
+
return promise;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// packages/core/src/plugin-host-registries.ts
|
|
708
|
+
function createTaskSourceRegistry() {
|
|
709
|
+
const byId = new Map;
|
|
710
|
+
const order = [];
|
|
711
|
+
return {
|
|
712
|
+
register(s) {
|
|
713
|
+
if (byId.has(s.id))
|
|
714
|
+
throw new Error(`task source already registered: ${s.id}`);
|
|
715
|
+
byId.set(s.id, s);
|
|
716
|
+
order.push(s);
|
|
717
|
+
},
|
|
718
|
+
resolveById(id) {
|
|
719
|
+
const s = byId.get(id);
|
|
720
|
+
if (!s)
|
|
721
|
+
throw new Error(`task source not registered: ${id}`);
|
|
722
|
+
return s;
|
|
723
|
+
},
|
|
724
|
+
resolveByKind(kind) {
|
|
725
|
+
for (const s of order)
|
|
726
|
+
if (s.kind === kind)
|
|
727
|
+
return s;
|
|
728
|
+
throw new Error(`no task source registered for kind: ${kind}`);
|
|
729
|
+
},
|
|
730
|
+
list: () => order
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
function formatRegisteredKinds(pluginHost) {
|
|
734
|
+
const kinds = pluginHost ? pluginHost.listExecutableTaskSources().map((source) => source.kind) : [];
|
|
735
|
+
return kinds.length > 0 ? kinds.join(", ") : "none";
|
|
736
|
+
}
|
|
737
|
+
function buildTaskSourceRegistry(config, pluginHost, context) {
|
|
738
|
+
const registry = createTaskSourceRegistry();
|
|
739
|
+
const taskSourceConfig = config.taskSource;
|
|
740
|
+
const factory = pluginHost?.resolveTaskSourceFactoryByKind(taskSourceConfig.kind);
|
|
741
|
+
if (!factory) {
|
|
742
|
+
throw new Error(`No task source factory registered for kind "${taskSourceConfig.kind}". ` + `Registered kinds: ${formatRegisteredKinds(pluginHost)}. ` + "Load a plugin that contributes an executable task source factory for this kind.");
|
|
743
|
+
}
|
|
744
|
+
registry.register(factory.factory(taskSourceConfig, context ? { ...context, rigConfig: config } : undefined));
|
|
745
|
+
return registry;
|
|
746
|
+
}
|
|
747
|
+
function createTaskFieldRegistry(extensions) {
|
|
748
|
+
const byId = new Map;
|
|
749
|
+
for (const e of extensions) {
|
|
750
|
+
if (byId.has(e.id))
|
|
751
|
+
throw new Error(`task field extension already registered: ${e.id}`);
|
|
752
|
+
byId.set(e.id, e);
|
|
753
|
+
}
|
|
754
|
+
return {
|
|
755
|
+
get: (id) => byId.get(id),
|
|
756
|
+
list: () => Array.from(byId.values()),
|
|
757
|
+
fieldNames: () => Array.from(byId.values()).map((e) => e.fieldName),
|
|
758
|
+
validateTaskFields(task) {
|
|
759
|
+
const errors = [];
|
|
760
|
+
for (const ext of byId.values()) {
|
|
761
|
+
let schema;
|
|
762
|
+
try {
|
|
763
|
+
schema = JSON.parse(ext.schemaJson);
|
|
764
|
+
} catch {
|
|
765
|
+
errors.push(`task field "${ext.id}": schemaJson is not valid JSON`);
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
const isRequired = typeof schema === "object" && schema !== null && schema.required === true;
|
|
769
|
+
if (!isRequired)
|
|
770
|
+
continue;
|
|
771
|
+
const value = task[ext.fieldName];
|
|
772
|
+
if (value === undefined || value === null || value === "") {
|
|
773
|
+
errors.push(`task field "${ext.fieldName}" (from extension "${ext.id}") is required but missing`);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return errors.length === 0 ? { ok: true } : { ok: false, errors };
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// packages/core/src/plugin-host-context.ts
|
|
782
|
+
import {
|
|
783
|
+
MANAGED_REPO_SERVICE_CAPABILITY,
|
|
784
|
+
SESSION_ASSET_MATERIALIZER,
|
|
785
|
+
TASK_DATA_SERVICE_CAPABILITY,
|
|
786
|
+
TOOL_MATERIALIZER
|
|
787
|
+
} from "@rig/contracts";
|
|
788
|
+
|
|
789
|
+
// packages/core/src/capability.ts
|
|
790
|
+
import { resolveCapability } from "@rig/kernel-seed";
|
|
791
|
+
|
|
792
|
+
class CapabilityProviderMissingError extends Error {
|
|
793
|
+
capabilityId;
|
|
794
|
+
name = "CapabilityProviderMissingError";
|
|
795
|
+
constructor(capabilityId) {
|
|
796
|
+
super(`No provider resolved for capability "${capabilityId}"`);
|
|
797
|
+
this.capabilityId = capabilityId;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function isFactory(impl) {
|
|
801
|
+
return typeof impl === "function";
|
|
802
|
+
}
|
|
803
|
+
function defineCapability(id) {
|
|
804
|
+
const idString = String(id);
|
|
805
|
+
const tag = idString;
|
|
806
|
+
async function resolve3(host) {
|
|
807
|
+
const providers = host.listExecutableCapabilities().filter((c) => c.id === idString && typeof c.run === "function").map((c) => ({
|
|
808
|
+
name: c.id,
|
|
809
|
+
provides: [c.id],
|
|
810
|
+
capabilityProviders: { [c.id]: c.run }
|
|
811
|
+
}));
|
|
812
|
+
const resolution = resolveCapability(providers, tag);
|
|
813
|
+
const run = resolution?.value;
|
|
814
|
+
if (!run)
|
|
815
|
+
return null;
|
|
816
|
+
return await run(undefined);
|
|
817
|
+
}
|
|
818
|
+
return {
|
|
819
|
+
id,
|
|
820
|
+
provide(impl, opts) {
|
|
821
|
+
return {
|
|
822
|
+
id: idString,
|
|
823
|
+
title: opts?.title ?? idString,
|
|
824
|
+
...opts?.description !== undefined ? { description: opts.description } : {},
|
|
825
|
+
run: isFactory(impl) ? () => impl() : () => impl
|
|
826
|
+
};
|
|
827
|
+
},
|
|
828
|
+
resolve: resolve3,
|
|
829
|
+
async require(host) {
|
|
830
|
+
const value = await resolve3(host);
|
|
831
|
+
if (value === null)
|
|
832
|
+
throw new CapabilityProviderMissingError(idString);
|
|
833
|
+
return value;
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// packages/core/src/capability-loaders.ts
|
|
839
|
+
var INSTALLED = new Map;
|
|
840
|
+
function installCapability(capability, impl) {
|
|
841
|
+
INSTALLED.set(String(capability.id), impl);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// packages/core/src/agent-role-registry.ts
|
|
845
|
+
function createAgentRoleRegistry(pluginRoles, configOverrides) {
|
|
846
|
+
const map = new Map;
|
|
847
|
+
for (const r of pluginRoles) {
|
|
848
|
+
if (map.has(r.id))
|
|
849
|
+
throw new Error(`agent role already registered: ${r.id}`);
|
|
850
|
+
const override = configOverrides?.[r.id];
|
|
851
|
+
const model = override?.model ?? r.defaultModel;
|
|
852
|
+
if (!model) {
|
|
853
|
+
throw new Error(`agent role "${r.id}" has no model \u2014 provide defaultModel in plugin or model in config.runtime.agentRoles.${r.id}`);
|
|
854
|
+
}
|
|
855
|
+
map.set(r.id, { ...r, model });
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
resolve(id) {
|
|
859
|
+
const r = map.get(id);
|
|
860
|
+
if (!r)
|
|
861
|
+
throw new Error(`agent role not registered: ${id}`);
|
|
862
|
+
return r;
|
|
863
|
+
},
|
|
864
|
+
list: () => Array.from(map.values())
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// packages/core/src/validator-registry.ts
|
|
869
|
+
import { existsSync as existsSync4 } from "fs";
|
|
870
|
+
import { join as join3 } from "path";
|
|
871
|
+
function createValidatorRegistry() {
|
|
872
|
+
const map = new Map;
|
|
873
|
+
const order = [];
|
|
874
|
+
const registry = {
|
|
875
|
+
register(v) {
|
|
876
|
+
if (map.has(v.id))
|
|
877
|
+
throw new Error(`validator already registered: ${v.id}`);
|
|
878
|
+
map.set(v.id, v);
|
|
879
|
+
order.push(v);
|
|
880
|
+
},
|
|
881
|
+
resolve(id) {
|
|
882
|
+
const v = map.get(id);
|
|
883
|
+
if (!v)
|
|
884
|
+
throw new Error(`validator not registered: ${id}`);
|
|
885
|
+
return v;
|
|
886
|
+
},
|
|
887
|
+
list: () => order
|
|
888
|
+
};
|
|
889
|
+
registerBuiltInValidators(registry);
|
|
890
|
+
return registry;
|
|
891
|
+
}
|
|
892
|
+
function registerBuiltInValidators(registry) {
|
|
893
|
+
registry.register({
|
|
894
|
+
id: "std:typecheck",
|
|
895
|
+
category: "custom",
|
|
896
|
+
description: "Runs the package typecheck script when present.",
|
|
897
|
+
run: async (ctx) => runStdTypecheck(ctx)
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
async function runStdTypecheck(ctx) {
|
|
901
|
+
const packageJsonPath = join3(ctx.workspaceRoot, "package.json");
|
|
902
|
+
if (!existsSync4(packageJsonPath)) {
|
|
903
|
+
return {
|
|
904
|
+
id: "std:typecheck",
|
|
905
|
+
passed: false,
|
|
906
|
+
summary: `package.json not found at ${packageJsonPath}`
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
const proc = Bun.spawn(["bun", "run", "typecheck"], {
|
|
910
|
+
cwd: ctx.workspaceRoot,
|
|
911
|
+
env: process.env,
|
|
912
|
+
stdout: "pipe",
|
|
913
|
+
stderr: "pipe"
|
|
914
|
+
});
|
|
915
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
916
|
+
proc.exited,
|
|
917
|
+
new Response(proc.stdout).text(),
|
|
918
|
+
new Response(proc.stderr).text()
|
|
919
|
+
]);
|
|
920
|
+
const output = `${stdout}${stderr}`.trim();
|
|
921
|
+
return {
|
|
922
|
+
id: "std:typecheck",
|
|
923
|
+
passed: exitCode === 0,
|
|
924
|
+
summary: exitCode === 0 ? "typecheck passed" : "typecheck failed",
|
|
925
|
+
...output ? { details: output.slice(0, 4000) } : {}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// packages/core/src/scope-rules.ts
|
|
930
|
+
var SCOPE_RULES_KEY = Symbol.for("rig.scope-normalization-rules");
|
|
931
|
+
function scopeRulesState() {
|
|
932
|
+
const global = globalThis;
|
|
933
|
+
return global[SCOPE_RULES_KEY] ??= { rules: null };
|
|
934
|
+
}
|
|
935
|
+
function setScopeRules(rules) {
|
|
936
|
+
scopeRulesState().rules = rules ?? null;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// packages/core/src/hook-materializer.ts
|
|
940
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
941
|
+
import { dirname as dirname2, resolve as resolve4 } from "path";
|
|
942
|
+
|
|
943
|
+
// packages/core/src/hook-runtime.ts
|
|
944
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, realpathSync, writeSync } from "fs";
|
|
945
|
+
import { resolve as resolve3 } from "path";
|
|
946
|
+
import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME } from "@rig/contracts";
|
|
947
|
+
function normalizeBuildConfig(value) {
|
|
948
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
949
|
+
return {};
|
|
950
|
+
}
|
|
951
|
+
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
952
|
+
}
|
|
953
|
+
function readBuildConfigForCoreHooks() {
|
|
954
|
+
if (typeof __RIG_BUILD_CONFIG__ !== "undefined") {
|
|
955
|
+
return normalizeBuildConfig(__RIG_BUILD_CONFIG__);
|
|
956
|
+
}
|
|
957
|
+
const raw = process.env.RIG_BUILD_CONFIG_JSON?.trim();
|
|
958
|
+
if (!raw) {
|
|
959
|
+
return {};
|
|
960
|
+
}
|
|
961
|
+
try {
|
|
962
|
+
return normalizeBuildConfig(JSON.parse(raw));
|
|
963
|
+
} catch {
|
|
964
|
+
return {};
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
var BUILD_CONFIG = readBuildConfigForCoreHooks();
|
|
968
|
+
var BAKED_PROJECT_ROOT = BUILD_CONFIG.AGENT_PROJECT_ROOT ?? "";
|
|
969
|
+
var BAKED_TASK_ID = BUILD_CONFIG.AGENT_TASK_ID ?? "";
|
|
970
|
+
var BAKED_BUN_PATH = BUILD_CONFIG.AGENT_BUN_PATH ?? "";
|
|
971
|
+
function normalizeExecutablePath(candidate) {
|
|
972
|
+
if (!candidate) {
|
|
973
|
+
return "";
|
|
974
|
+
}
|
|
975
|
+
const normalized = resolve3(candidate);
|
|
976
|
+
if (!existsSync5(normalized)) {
|
|
977
|
+
return "";
|
|
978
|
+
}
|
|
979
|
+
try {
|
|
980
|
+
return realpathSync(normalized);
|
|
981
|
+
} catch {
|
|
982
|
+
return normalized;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
function looksLikeRuntimeGateway(candidate) {
|
|
986
|
+
const normalized = resolve3(candidate).replace(/\\/g, "/");
|
|
987
|
+
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
988
|
+
}
|
|
989
|
+
function resolveBunBinaryPath() {
|
|
990
|
+
const explicit = normalizeExecutablePath(process.env.RIG_BUN_PATH?.trim());
|
|
991
|
+
if (explicit) {
|
|
992
|
+
return explicit;
|
|
993
|
+
}
|
|
994
|
+
const bunGlobal = globalThis.Bun;
|
|
995
|
+
const bunWhich = bunGlobal?.which?.("bun");
|
|
996
|
+
const pathBun = normalizeExecutablePath(bunWhich?.trim());
|
|
997
|
+
if (pathBun && !looksLikeRuntimeGateway(pathBun)) {
|
|
998
|
+
return pathBun;
|
|
999
|
+
}
|
|
1000
|
+
const home = process.env.HOME?.trim();
|
|
1001
|
+
const fallbackCandidates = [
|
|
1002
|
+
home ? resolve3(home, ".bun/bin/bun") : "",
|
|
1003
|
+
"/opt/homebrew/bin/bun",
|
|
1004
|
+
"/usr/local/bin/bun",
|
|
1005
|
+
"/usr/bin/bun"
|
|
1006
|
+
];
|
|
1007
|
+
for (const candidate of fallbackCandidates) {
|
|
1008
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
1009
|
+
if (normalized) {
|
|
1010
|
+
return normalized;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const execPath = normalizeExecutablePath(process.execPath?.trim());
|
|
1014
|
+
if (execPath && !looksLikeRuntimeGateway(execPath)) {
|
|
1015
|
+
return execPath;
|
|
1016
|
+
}
|
|
1017
|
+
throw new Error("bun not found in PATH");
|
|
1018
|
+
}
|
|
1019
|
+
function resolveBunCliInvocation() {
|
|
1020
|
+
if (BAKED_BUN_PATH) {
|
|
1021
|
+
return {
|
|
1022
|
+
command: BAKED_BUN_PATH,
|
|
1023
|
+
env: {}
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
if (process.env.RIG_BUN_PATH?.trim()) {
|
|
1027
|
+
return {
|
|
1028
|
+
command: process.env.RIG_BUN_PATH.trim(),
|
|
1029
|
+
env: {}
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
try {
|
|
1033
|
+
const systemBun = resolveBunBinaryPath();
|
|
1034
|
+
return {
|
|
1035
|
+
command: systemBun,
|
|
1036
|
+
env: {}
|
|
1037
|
+
};
|
|
1038
|
+
} catch {}
|
|
1039
|
+
if (process.execPath?.trim()) {
|
|
1040
|
+
return {
|
|
1041
|
+
command: process.execPath,
|
|
1042
|
+
env: { BUN_BE_BUN: "1" }
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
return { command: "bun", env: {} };
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// packages/core/src/hook-materializer.ts
|
|
1049
|
+
var MARKER_PLUGIN = "_rigPlugin";
|
|
1050
|
+
var MARKER_HOOK_ID = "_rigHookId";
|
|
1051
|
+
function matcherToString(matcher) {
|
|
1052
|
+
if (matcher.kind === "all")
|
|
1053
|
+
return;
|
|
1054
|
+
if (matcher.kind === "tool")
|
|
1055
|
+
return matcher.name;
|
|
1056
|
+
return matcher.pattern;
|
|
1057
|
+
}
|
|
1058
|
+
function isPluginOwned(cmd) {
|
|
1059
|
+
return typeof cmd[MARKER_PLUGIN] === "string";
|
|
1060
|
+
}
|
|
1061
|
+
function shellQuote(value) {
|
|
1062
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
1063
|
+
}
|
|
1064
|
+
function resolveHookRunnerPath() {
|
|
1065
|
+
const sibling = resolve4(import.meta.dirname, "hook-runner.ts");
|
|
1066
|
+
if (existsSync6(sibling)) {
|
|
1067
|
+
return sibling;
|
|
1068
|
+
}
|
|
1069
|
+
return "$CLAUDE_PROJECT_DIR/node_modules/@rig/core/src/hook-runner.ts";
|
|
1070
|
+
}
|
|
1071
|
+
function buildTypedHookShimCommand(pluginName, hook, projectRoot) {
|
|
1072
|
+
const runnerPath = resolveHookRunnerPath();
|
|
1073
|
+
const runnerArg = runnerPath.startsWith("$CLAUDE_PROJECT_DIR") ? `"${runnerPath}"` : shellQuote(runnerPath);
|
|
1074
|
+
const bun = resolveBunCliInvocation();
|
|
1075
|
+
const envPrefix = Object.entries(bun.env).map(([key, value]) => `${key}=${shellQuote(value)}`).join(" ");
|
|
1076
|
+
const parts = [
|
|
1077
|
+
envPrefix,
|
|
1078
|
+
shellQuote(bun.command),
|
|
1079
|
+
runnerArg,
|
|
1080
|
+
"--plugin",
|
|
1081
|
+
shellQuote(pluginName),
|
|
1082
|
+
"--hook",
|
|
1083
|
+
shellQuote(hook.id),
|
|
1084
|
+
"--event",
|
|
1085
|
+
shellQuote(hook.event),
|
|
1086
|
+
"--project-root",
|
|
1087
|
+
shellQuote(projectRoot)
|
|
1088
|
+
].filter(Boolean);
|
|
1089
|
+
return parts.join(" ");
|
|
1090
|
+
}
|
|
1091
|
+
function createPiNoopSessionHookAdapter() {
|
|
1092
|
+
return {
|
|
1093
|
+
id: "pi",
|
|
1094
|
+
materialize() {
|
|
1095
|
+
return {
|
|
1096
|
+
adapterId: "pi",
|
|
1097
|
+
status: "skipped",
|
|
1098
|
+
reason: "Pi sessions do not consume Claude Code settings hooks."
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
function createClaudeCodeSessionHookAdapter() {
|
|
1104
|
+
return {
|
|
1105
|
+
id: "claude-code",
|
|
1106
|
+
materialize(projectRoot, entries) {
|
|
1107
|
+
return writeClaudeCodeHookSettings(projectRoot, entries);
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
function defaultAgentSessionHookAdapters(env = process.env) {
|
|
1112
|
+
if (env.RIG_AGENT_SESSION_HOOK_ADAPTER === "claude-code") {
|
|
1113
|
+
return [createClaudeCodeSessionHookAdapter()];
|
|
1114
|
+
}
|
|
1115
|
+
if (env.RIG_AGENT_SESSION_HOOK_ADAPTER === "pi" || typeof env.PI_CODING_AGENT_SESSION_DIR === "string" && env.PI_CODING_AGENT_SESSION_DIR.trim().length > 0 || env.RIG_RUN_PROCESS === "1") {
|
|
1116
|
+
return [createPiNoopSessionHookAdapter()];
|
|
1117
|
+
}
|
|
1118
|
+
return [createClaudeCodeSessionHookAdapter()];
|
|
1119
|
+
}
|
|
1120
|
+
function materializeSessionHookAdapters(projectRoot, entries, adapters = [createClaudeCodeSessionHookAdapter()]) {
|
|
1121
|
+
return adapters.map((adapter) => adapter.materialize(projectRoot, entries));
|
|
1122
|
+
}
|
|
1123
|
+
function applyClaudeCodeSessionHooksToSettings(existing, entries, projectRoot, options = {}) {
|
|
1124
|
+
const hooks = typeof existing.hooks === "object" && existing.hooks !== null && !Array.isArray(existing.hooks) ? existing.hooks : {};
|
|
1125
|
+
const replacePluginOwned = options.replacePluginOwned ?? true;
|
|
1126
|
+
if (replacePluginOwned) {
|
|
1127
|
+
for (const event of Object.keys(hooks)) {
|
|
1128
|
+
const groups = hooks[event] ?? [];
|
|
1129
|
+
const cleaned = [];
|
|
1130
|
+
for (const group of groups) {
|
|
1131
|
+
const operatorHooks = group.hooks.filter((h) => !isPluginOwned(h));
|
|
1132
|
+
if (operatorHooks.length > 0) {
|
|
1133
|
+
cleaned.push({ ...group, hooks: operatorHooks });
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
if (cleaned.length > 0) {
|
|
1137
|
+
hooks[event] = cleaned;
|
|
1138
|
+
} else {
|
|
1139
|
+
delete hooks[event];
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
const materializedEvents = new Set;
|
|
1144
|
+
for (const { pluginName, hook, typed } of entries) {
|
|
1145
|
+
const command = hook.command ?? (typed ? buildTypedHookShimCommand(pluginName, hook, projectRoot) : undefined);
|
|
1146
|
+
if (!command) {
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
const event = hook.event;
|
|
1150
|
+
materializedEvents.add(event);
|
|
1151
|
+
const matcherString = matcherToString(hook.matcher);
|
|
1152
|
+
const groups = hooks[event] ??= [];
|
|
1153
|
+
let group = groups.find((g) => g.matcher === matcherString);
|
|
1154
|
+
if (!group) {
|
|
1155
|
+
group = matcherString === undefined ? { hooks: [] } : { matcher: matcherString, hooks: [] };
|
|
1156
|
+
groups.push(group);
|
|
1157
|
+
}
|
|
1158
|
+
const alreadyPresent = group.hooks.some((candidate) => candidate[MARKER_PLUGIN] === pluginName && candidate[MARKER_HOOK_ID] === hook.id);
|
|
1159
|
+
if (alreadyPresent) {
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
group.hooks.push({
|
|
1163
|
+
type: "command",
|
|
1164
|
+
command,
|
|
1165
|
+
[MARKER_PLUGIN]: pluginName,
|
|
1166
|
+
[MARKER_HOOK_ID]: hook.id
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
const next = { ...existing };
|
|
1170
|
+
if (Object.keys(hooks).length > 0) {
|
|
1171
|
+
next.hooks = hooks;
|
|
1172
|
+
} else {
|
|
1173
|
+
delete next.hooks;
|
|
1174
|
+
}
|
|
1175
|
+
return { settings: next, events: [...materializedEvents].sort() };
|
|
1176
|
+
}
|
|
1177
|
+
function writeClaudeCodeHookSettings(projectRoot, entries) {
|
|
1178
|
+
const settingsPath = resolve4(projectRoot, ".claude", "settings.json");
|
|
1179
|
+
const existing = existsSync6(settingsPath) ? safeReadJson(settingsPath) : {};
|
|
1180
|
+
const { settings, events } = applyClaudeCodeSessionHooksToSettings(existing, entries, projectRoot);
|
|
1181
|
+
mkdirSync2(dirname2(settingsPath), { recursive: true });
|
|
1182
|
+
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
1183
|
+
`, "utf-8");
|
|
1184
|
+
return {
|
|
1185
|
+
adapterId: "claude-code",
|
|
1186
|
+
status: "materialized",
|
|
1187
|
+
path: settingsPath,
|
|
1188
|
+
events
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
function safeReadJson(path) {
|
|
1192
|
+
try {
|
|
1193
|
+
return JSON.parse(readFileSync4(path, "utf-8"));
|
|
1194
|
+
} catch {
|
|
1195
|
+
return {};
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// packages/core/src/plugin-host-context.ts
|
|
1200
|
+
var ManagedRepoCap = defineCapability(MANAGED_REPO_SERVICE_CAPABILITY);
|
|
1201
|
+
var SessionAssetMaterializerCap = defineCapability(SESSION_ASSET_MATERIALIZER);
|
|
1202
|
+
var TaskDataCap = defineCapability(TASK_DATA_SERVICE_CAPABILITY);
|
|
1203
|
+
var ToolMaterializerCap = defineCapability(TOOL_MATERIALIZER);
|
|
1204
|
+
async function buildPluginHostContext(projectRoot) {
|
|
1205
|
+
let config;
|
|
1206
|
+
let pluginHost;
|
|
1207
|
+
try {
|
|
1208
|
+
({ config, host: pluginHost } = await resolvePluginHost(projectRoot));
|
|
1209
|
+
} catch (err) {
|
|
1210
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1211
|
+
if (msg.includes("no rig.config")) {
|
|
1212
|
+
return null;
|
|
1213
|
+
}
|
|
1214
|
+
throw err;
|
|
1215
|
+
}
|
|
1216
|
+
setScopeRules(config.workspace.scopeNormalization);
|
|
1217
|
+
if (config.workspace.sandbox && !process.env.RIG_RUNTIME_SANDBOX?.trim()) {
|
|
1218
|
+
process.env.RIG_RUNTIME_SANDBOX = config.workspace.sandbox;
|
|
1219
|
+
}
|
|
1220
|
+
const validatorRegistry = createValidatorRegistry();
|
|
1221
|
+
for (const impl of pluginHost.listExecutableValidators()) {
|
|
1222
|
+
validatorRegistry.register(impl);
|
|
1223
|
+
}
|
|
1224
|
+
const taskSourceRegistry = buildTaskSourceRegistry(config, pluginHost, { projectRoot });
|
|
1225
|
+
const taskDataService = await TaskDataCap.resolve(pluginHost);
|
|
1226
|
+
if (taskDataService) {
|
|
1227
|
+
installCapability(TaskDataCap, taskDataService);
|
|
1228
|
+
}
|
|
1229
|
+
const toolMaterializer = await ToolMaterializerCap.resolve(pluginHost);
|
|
1230
|
+
if (toolMaterializer) {
|
|
1231
|
+
installCapability(ToolMaterializerCap, toolMaterializer);
|
|
1232
|
+
}
|
|
1233
|
+
const managedRepoService = await ManagedRepoCap.resolve(pluginHost);
|
|
1234
|
+
let repoRegistry;
|
|
1235
|
+
if (managedRepoService) {
|
|
1236
|
+
installCapability(ManagedRepoCap, managedRepoService);
|
|
1237
|
+
repoRegistry = managedRepoService.createRepoRegistry(pluginHost.listRepoSources());
|
|
1238
|
+
const managedEntries = pluginHost.listRepoSources().map((reg) => managedRepoService.repoRegistrationToManagedEntry(reg)).filter((e) => e !== null);
|
|
1239
|
+
managedRepoService.setManagedRepos(managedEntries);
|
|
1240
|
+
} else {
|
|
1241
|
+
repoRegistry = { getById: () => {
|
|
1242
|
+
return;
|
|
1243
|
+
}, list: () => [] };
|
|
1244
|
+
}
|
|
1245
|
+
const configRoleOverrides = config.runtime?.agentRoles;
|
|
1246
|
+
const agentRoleRegistry = createAgentRoleRegistry(pluginHost.listAgentRoles(), configRoleOverrides);
|
|
1247
|
+
const taskFieldRegistry = createTaskFieldRegistry(pluginHost.listTaskFieldExtensions());
|
|
1248
|
+
try {
|
|
1249
|
+
const hookEntries = config.plugins.flatMap((plugin) => (plugin.contributes?.hooks ?? []).map((hook) => ({
|
|
1250
|
+
pluginName: plugin.name,
|
|
1251
|
+
hook,
|
|
1252
|
+
typed: Boolean(hook.handler)
|
|
1253
|
+
})));
|
|
1254
|
+
if (hookEntries.length > 0) {
|
|
1255
|
+
materializeSessionHookAdapters(projectRoot, hookEntries, defaultAgentSessionHookAdapters());
|
|
1256
|
+
}
|
|
1257
|
+
} catch (err) {
|
|
1258
|
+
console.warn(`[plugin-host] hook materialization failed: ${err instanceof Error ? err.message : err}`);
|
|
1259
|
+
}
|
|
1260
|
+
const sessionAssetMaterializer = await SessionAssetMaterializerCap.resolve(pluginHost);
|
|
1261
|
+
if (sessionAssetMaterializer) {
|
|
1262
|
+
await sessionAssetMaterializer.materializeSessionAssets(projectRoot, config);
|
|
1263
|
+
}
|
|
1264
|
+
return {
|
|
1265
|
+
config,
|
|
1266
|
+
pluginHost,
|
|
1267
|
+
validatorRegistry,
|
|
1268
|
+
taskSourceRegistry,
|
|
1269
|
+
repoRegistry,
|
|
1270
|
+
agentRoleRegistry,
|
|
1271
|
+
taskFieldRegistry
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
export {
|
|
1275
|
+
buildPluginHostContext
|
|
1276
|
+
};
|