@h-rig/core 0.0.6-alpha.157 → 0.0.6-alpha.159
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/{task-io.js → capability-loaders.js} +237 -195
- 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 +1 -1
- package/dist/src/config.js +7 -4
- package/dist/src/declarative-config.d.ts +14 -0
- package/dist/src/declarative-config.js +82 -0
- package/dist/src/define-config.d.ts +2 -1
- package/dist/src/define-config.js +7 -4
- 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 +3 -1
- package/dist/src/index.js +119 -5
- 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.js +149 -50
- 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 +1270 -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 +6 -1
- package/dist/src/plugin-host.js +51 -0
- package/dist/src/plugin-runtime.d.ts +45 -6
- package/dist/src/profile-ops.d.ts +9 -0
- package/dist/src/profile-ops.js +252 -0
- package/dist/src/project-plugins.d.ts +9 -12
- package/dist/src/project-plugins.js +248 -61
- 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 +136 -6
- package/dist/src/task-io.d.ts +0 -54
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RegisteredTaskSource, RigConfig, TaskFieldExtension } from "@rig/contracts";
|
|
2
|
+
import type { PluginHost } from "./plugin-host";
|
|
3
|
+
import type { TaskSourceFactoryContext } from "./plugin-runtime";
|
|
4
|
+
export interface TaskSourceRegistry {
|
|
5
|
+
register(s: RegisteredTaskSource): void;
|
|
6
|
+
resolveById(id: string): RegisteredTaskSource;
|
|
7
|
+
resolveByKind(kind: string): RegisteredTaskSource;
|
|
8
|
+
list(): readonly RegisteredTaskSource[];
|
|
9
|
+
}
|
|
10
|
+
export declare function createTaskSourceRegistry(): TaskSourceRegistry;
|
|
11
|
+
/**
|
|
12
|
+
* Instantiate the task-source adapter selected by `config.taskSource.kind`
|
|
13
|
+
* from executable task-source factories contributed by loaded plugins.
|
|
14
|
+
*
|
|
15
|
+
* First-party adapters are not special-cased here: projects that use the
|
|
16
|
+
* standard GitHub Issues or files sources get those factories by loading
|
|
17
|
+
* `standard()` in `rig.config.ts`, exactly like any third-party adapter.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildTaskSourceRegistry(config: RigConfig, pluginHost?: PluginHost, context?: TaskSourceFactoryContext): TaskSourceRegistry;
|
|
20
|
+
export interface TaskFieldRegistry {
|
|
21
|
+
get(id: string): TaskFieldExtension | undefined;
|
|
22
|
+
list(): readonly TaskFieldExtension[];
|
|
23
|
+
fieldNames(): readonly string[];
|
|
24
|
+
validateTaskFields(task: Record<string, unknown>): {
|
|
25
|
+
ok: true;
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
errors: string[];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export declare function createTaskFieldRegistry(extensions: readonly TaskFieldExtension[]): TaskFieldRegistry;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/plugin-host-registries.ts
|
|
3
|
+
function createTaskSourceRegistry() {
|
|
4
|
+
const byId = new Map;
|
|
5
|
+
const order = [];
|
|
6
|
+
return {
|
|
7
|
+
register(s) {
|
|
8
|
+
if (byId.has(s.id))
|
|
9
|
+
throw new Error(`task source already registered: ${s.id}`);
|
|
10
|
+
byId.set(s.id, s);
|
|
11
|
+
order.push(s);
|
|
12
|
+
},
|
|
13
|
+
resolveById(id) {
|
|
14
|
+
const s = byId.get(id);
|
|
15
|
+
if (!s)
|
|
16
|
+
throw new Error(`task source not registered: ${id}`);
|
|
17
|
+
return s;
|
|
18
|
+
},
|
|
19
|
+
resolveByKind(kind) {
|
|
20
|
+
for (const s of order)
|
|
21
|
+
if (s.kind === kind)
|
|
22
|
+
return s;
|
|
23
|
+
throw new Error(`no task source registered for kind: ${kind}`);
|
|
24
|
+
},
|
|
25
|
+
list: () => order
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function formatRegisteredKinds(pluginHost) {
|
|
29
|
+
const kinds = pluginHost ? pluginHost.listExecutableTaskSources().map((source) => source.kind) : [];
|
|
30
|
+
return kinds.length > 0 ? kinds.join(", ") : "none";
|
|
31
|
+
}
|
|
32
|
+
function buildTaskSourceRegistry(config, pluginHost, context) {
|
|
33
|
+
const registry = createTaskSourceRegistry();
|
|
34
|
+
const taskSourceConfig = config.taskSource;
|
|
35
|
+
const factory = pluginHost?.resolveTaskSourceFactoryByKind(taskSourceConfig.kind);
|
|
36
|
+
if (!factory) {
|
|
37
|
+
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.");
|
|
38
|
+
}
|
|
39
|
+
registry.register(factory.factory(taskSourceConfig, context ? { ...context, rigConfig: config } : undefined));
|
|
40
|
+
return registry;
|
|
41
|
+
}
|
|
42
|
+
function createTaskFieldRegistry(extensions) {
|
|
43
|
+
const byId = new Map;
|
|
44
|
+
for (const e of extensions) {
|
|
45
|
+
if (byId.has(e.id))
|
|
46
|
+
throw new Error(`task field extension already registered: ${e.id}`);
|
|
47
|
+
byId.set(e.id, e);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
get: (id) => byId.get(id),
|
|
51
|
+
list: () => Array.from(byId.values()),
|
|
52
|
+
fieldNames: () => Array.from(byId.values()).map((e) => e.fieldName),
|
|
53
|
+
validateTaskFields(task) {
|
|
54
|
+
const errors = [];
|
|
55
|
+
for (const ext of byId.values()) {
|
|
56
|
+
let schema;
|
|
57
|
+
try {
|
|
58
|
+
schema = JSON.parse(ext.schemaJson);
|
|
59
|
+
} catch {
|
|
60
|
+
errors.push(`task field "${ext.id}": schemaJson is not valid JSON`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const isRequired = typeof schema === "object" && schema !== null && schema.required === true;
|
|
64
|
+
if (!isRequired)
|
|
65
|
+
continue;
|
|
66
|
+
const value = task[ext.fieldName];
|
|
67
|
+
if (value === undefined || value === null || value === "") {
|
|
68
|
+
errors.push(`task field "${ext.fieldName}" (from extension "${ext.id}") is required but missing`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return errors.length === 0 ? { ok: true } : { ok: false, errors };
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
createTaskSourceRegistry,
|
|
77
|
+
createTaskFieldRegistry,
|
|
78
|
+
buildTaskSourceRegistry
|
|
79
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentRoleRegistration, BlockerClassifierRegistration, CliCommandRegistration, HookRegistration, PanelRegistration, ProductCapabilityRegistration, RepoSourceRegistration, SkillRegistration, Stage, StageMutation, StageRun, TaskFieldExtension, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
|
|
2
|
-
import type { BlockerClassifierEntry, CliCommand, FeatureCapability, Panel, RigPlugin, SessionExtensionEntry, TaskSource, Validator } from "./plugin-runtime";
|
|
2
|
+
import type { BlockerClassifierEntry, CliCommand, FeatureCapability, Panel, RigPlugin, SessionExtensionEntry, SeedEntrypoint, TaskSource, Validator } from "./plugin-runtime";
|
|
3
3
|
export interface PluginHost {
|
|
4
4
|
getValidator(id: string): ValidatorRegistration | undefined;
|
|
5
5
|
getHook(id: string): HookRegistration | undefined;
|
|
@@ -61,7 +61,12 @@ export interface PluginHost {
|
|
|
61
61
|
* rather than injected directly by a leaf.
|
|
62
62
|
*/
|
|
63
63
|
listSessionExtensions(): readonly SessionExtensionEntry[];
|
|
64
|
+
listSeedEntrypoints(): readonly SeedEntrypoint[];
|
|
65
|
+
resolveSeedEntrypointByBasename(basename: string): SeedEntrypoint | undefined;
|
|
66
|
+
resolveSeedEntrypointByWorkerArg(workerArg: string): SeedEntrypoint | undefined;
|
|
67
|
+
resolveInsidePtySeedEntrypoint(): SeedEntrypoint | undefined;
|
|
64
68
|
resolveExecutableCliCommand(requested: string): CliCommand | undefined;
|
|
69
|
+
resolveDefaultRootCliCommand(): CliCommand | undefined;
|
|
65
70
|
/**
|
|
66
71
|
* Look up an executable task source factory by its `kind` (the value in
|
|
67
72
|
* `config.taskSource.kind`). Returns undefined when no plugin provides
|
package/dist/src/plugin-host.js
CHANGED
|
@@ -35,6 +35,7 @@ function createPluginHost(plugins) {
|
|
|
35
35
|
const panels = [];
|
|
36
36
|
const blockerClassifiers = [];
|
|
37
37
|
const sessionExtensions = [];
|
|
38
|
+
const seedEntrypoints = [];
|
|
38
39
|
const stages = [];
|
|
39
40
|
const stageMutations = [];
|
|
40
41
|
const stageExecutors = {};
|
|
@@ -67,6 +68,8 @@ function createPluginHost(plugins) {
|
|
|
67
68
|
blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
|
|
68
69
|
if (c.sessionExtensions)
|
|
69
70
|
sessionExtensions.push(...c.sessionExtensions.map((item) => ({ item, pluginName })));
|
|
71
|
+
if (c.seedEntrypoints)
|
|
72
|
+
seedEntrypoints.push(...c.seedEntrypoints.map((item) => ({ item, pluginName })));
|
|
70
73
|
if (c.stageMutations)
|
|
71
74
|
stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
|
|
72
75
|
if (c.stages) {
|
|
@@ -89,6 +92,7 @@ function createPluginHost(plugins) {
|
|
|
89
92
|
const panelMap = indexById(panels, "panel");
|
|
90
93
|
const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
|
|
91
94
|
indexById(sessionExtensions, "sessionExtension");
|
|
95
|
+
indexById(seedEntrypoints, "seedEntrypoint");
|
|
92
96
|
const taskSourceFactoryByKind = new Map;
|
|
93
97
|
const taskSourceKindRegistrant = new Map;
|
|
94
98
|
for (const { item, pluginName } of taskSources) {
|
|
@@ -98,6 +102,38 @@ function createPluginHost(plugins) {
|
|
|
98
102
|
taskSourceFactoryByKind.set(item.kind, item);
|
|
99
103
|
taskSourceKindRegistrant.set(item.kind, pluginName);
|
|
100
104
|
}
|
|
105
|
+
const seedEntrypointByBasename = new Map;
|
|
106
|
+
const seedEntrypointByWorkerArg = new Map;
|
|
107
|
+
let insidePtySeedEntrypoint;
|
|
108
|
+
const registerSeedEntrypointSelector = (map, selectorKind, selector, contribution) => {
|
|
109
|
+
const existing = map.get(selector);
|
|
110
|
+
if (existing) {
|
|
111
|
+
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}"`);
|
|
112
|
+
}
|
|
113
|
+
map.set(selector, contribution);
|
|
114
|
+
};
|
|
115
|
+
for (const contribution of seedEntrypoints) {
|
|
116
|
+
const entrypoint = contribution.item;
|
|
117
|
+
let selectorCount = 0;
|
|
118
|
+
if (entrypoint.basename) {
|
|
119
|
+
selectorCount += 1;
|
|
120
|
+
registerSeedEntrypointSelector(seedEntrypointByBasename, "basename", entrypoint.basename, contribution);
|
|
121
|
+
}
|
|
122
|
+
if (entrypoint.workerArg) {
|
|
123
|
+
selectorCount += 1;
|
|
124
|
+
registerSeedEntrypointSelector(seedEntrypointByWorkerArg, "workerArg", entrypoint.workerArg, contribution);
|
|
125
|
+
}
|
|
126
|
+
if (entrypoint.insidePty) {
|
|
127
|
+
selectorCount += 1;
|
|
128
|
+
if (insidePtySeedEntrypoint) {
|
|
129
|
+
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}"`);
|
|
130
|
+
}
|
|
131
|
+
insidePtySeedEntrypoint = contribution;
|
|
132
|
+
}
|
|
133
|
+
if (selectorCount === 0) {
|
|
134
|
+
throw new Error(`seed entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}" must declare at least one selector (basename, workerArg, or insidePty)`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
101
137
|
const allValidators = validators.map((c) => c.item);
|
|
102
138
|
const allHooks = hooks.map((c) => c.item);
|
|
103
139
|
const allSkills = skills.map((c) => c.item);
|
|
@@ -112,6 +148,7 @@ function createPluginHost(plugins) {
|
|
|
112
148
|
const allPanels = panels.map((c) => c.item);
|
|
113
149
|
const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
|
|
114
150
|
const allSessionExtensions = sessionExtensions.map((c) => c.item);
|
|
151
|
+
const allSeedEntrypoints = seedEntrypoints.map((c) => c.item);
|
|
115
152
|
const executableCliCommandByName = new Map;
|
|
116
153
|
const registerExecutableCliCommandSelector = (selector, contribution) => {
|
|
117
154
|
const existing = executableCliCommandByName.get(selector);
|
|
@@ -129,6 +166,15 @@ function createPluginHost(plugins) {
|
|
|
129
166
|
registerExecutableCliCommandSelector(alias, contribution);
|
|
130
167
|
}
|
|
131
168
|
}
|
|
169
|
+
let defaultRootCliCommand;
|
|
170
|
+
for (const contribution of cliCommands) {
|
|
171
|
+
if (!contribution.item.rootDefault)
|
|
172
|
+
continue;
|
|
173
|
+
if (defaultRootCliCommand) {
|
|
174
|
+
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}"`);
|
|
175
|
+
}
|
|
176
|
+
defaultRootCliCommand = contribution;
|
|
177
|
+
}
|
|
132
178
|
return {
|
|
133
179
|
getValidator: (id) => validatorMap.get(id),
|
|
134
180
|
getHook: (id) => hookMap.get(id),
|
|
@@ -162,7 +208,12 @@ function createPluginHost(plugins) {
|
|
|
162
208
|
listExecutableBlockerClassifiers: () => allBlockerClassifiers,
|
|
163
209
|
listExecutableCliCommands: () => allCliCommands,
|
|
164
210
|
listSessionExtensions: () => allSessionExtensions,
|
|
211
|
+
listSeedEntrypoints: () => allSeedEntrypoints,
|
|
212
|
+
resolveSeedEntrypointByBasename: (value) => seedEntrypointByBasename.get(value)?.item,
|
|
213
|
+
resolveSeedEntrypointByWorkerArg: (value) => seedEntrypointByWorkerArg.get(value)?.item,
|
|
214
|
+
resolveInsidePtySeedEntrypoint: () => insidePtySeedEntrypoint?.item,
|
|
165
215
|
resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
|
|
216
|
+
resolveDefaultRootCliCommand: () => defaultRootCliCommand?.item,
|
|
166
217
|
resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
|
|
167
218
|
};
|
|
168
219
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentRoleRegistration, BlockerClassifierRegistration, CapabilityReplacementSpec, CapabilityTag, CliCommandRegistration, HookImplementation, HookRegistration, PanelRegistration, ProductCapabilityRegistration, RegisteredTaskSource, RepoSourceRegistration, RigConfig, SessionExtensionRegistration, SkillRegistration, Stage, StageMutation, StageRun, TaskFieldExtension, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
|
|
1
|
+
import type { AgentRoleRegistration, BlockerClassifierRegistration, CapabilityReplacementSpec, CapabilityTag, CliCommandRegistration, HookImplementation, HookRegistration, PanelRegistration, ProductCapabilityRegistration, RegisteredTaskSource, RepoSourceRegistration, RigConfig, SeedEntrypointRegistration, SessionExtensionRegistration, SkillRegistration, Stage, StageMutation, StageRun, TaskFieldExtension, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
|
|
2
2
|
export interface ValidatorResult {
|
|
3
3
|
id: string;
|
|
4
4
|
passed: boolean;
|
|
@@ -41,11 +41,8 @@ export interface TaskSourceFactoryContext {
|
|
|
41
41
|
export interface TaskSource extends TaskSourceRegistration {
|
|
42
42
|
factory(config: TaskSourceConfig, context?: TaskSourceFactoryContext): RegisteredTaskSource;
|
|
43
43
|
}
|
|
44
|
-
export
|
|
45
|
-
run?: (input: unknown) => Promise<unknown> | unknown;
|
|
46
|
-
}
|
|
44
|
+
export type FeatureCapability = ProductCapabilityRegistration;
|
|
47
45
|
/** @deprecated single-channel rename — use {@link FeatureCapability}. */
|
|
48
|
-
export type RuntimeFeatureCapability = FeatureCapability;
|
|
49
46
|
export interface Panel extends PanelRegistration {
|
|
50
47
|
produce?: (context?: unknown) => Promise<unknown> | unknown;
|
|
51
48
|
}
|
|
@@ -53,7 +50,6 @@ export interface BlockerClassifierEntry extends BlockerClassifierRegistration {
|
|
|
53
50
|
classify(input: unknown): Promise<unknown> | unknown;
|
|
54
51
|
}
|
|
55
52
|
/** @deprecated single-channel rename — use {@link BlockerClassifierEntry}. */
|
|
56
|
-
export type RuntimeBlockerClassifier = BlockerClassifierEntry;
|
|
57
53
|
export interface RuntimeCliContext {
|
|
58
54
|
projectRoot: string;
|
|
59
55
|
outputMode?: string;
|
|
@@ -91,11 +87,51 @@ export type SessionExtensionInstall = (api: any) => void | Promise<void>;
|
|
|
91
87
|
export interface SessionExtensionEntry extends SessionExtensionRegistration {
|
|
92
88
|
install: SessionExtensionInstall;
|
|
93
89
|
}
|
|
90
|
+
export type SeedEntrypointRunResult = boolean | void | number;
|
|
91
|
+
export interface SeedEntrypointContext {
|
|
92
|
+
argv: readonly string[];
|
|
93
|
+
execPath?: string;
|
|
94
|
+
basename?: string;
|
|
95
|
+
workerArg?: string;
|
|
96
|
+
parentPort?: unknown;
|
|
97
|
+
}
|
|
98
|
+
export interface SeedEntrypoint extends SeedEntrypointRegistration {
|
|
99
|
+
run(context: SeedEntrypointContext): Promise<SeedEntrypointRunResult> | SeedEntrypointRunResult;
|
|
100
|
+
}
|
|
94
101
|
/**
|
|
95
102
|
* Single-channel contributions. Fn-bearing kinds carry their executable fn on
|
|
96
103
|
* the same entry as their metadata; metadata-only kinds (skills, repoSources,
|
|
97
104
|
* agentRoles, taskFieldSchemas, stageMutations) are pure registrations.
|
|
98
105
|
*/
|
|
106
|
+
/** Context handed to a plugin when it default-fills its config fragment. */
|
|
107
|
+
export interface ConfigDefaultsContext {
|
|
108
|
+
/** Absolute path to the project root being configured. */
|
|
109
|
+
readonly projectRoot: string;
|
|
110
|
+
/**
|
|
111
|
+
* Explicit `owner/repo` slug when a caller already knows it (the init wizard,
|
|
112
|
+
* a setup flow). When absent, a plugin that needs the repo identity derives it
|
|
113
|
+
* from its own domain (e.g. the github plugin reads the git remote). This is
|
|
114
|
+
* the single override seam so autocreate and the wizard share one composer.
|
|
115
|
+
*/
|
|
116
|
+
readonly repoSlug?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A plugin's contribution to the declarative `.rig/rigfig.toml`.
|
|
120
|
+
*
|
|
121
|
+
* Plugins are the things that ENRICH the config: each one owns the config
|
|
122
|
+
* properties IT needs and default-fills them with its OWN domain logic — the
|
|
123
|
+
* github plugin detects owner/repo from the git remote and fills the github
|
|
124
|
+
* task source + workspace id; the pr plugin fills `[pr]`; planning fills
|
|
125
|
+
* `[planning]`; and so on. The autocreate/init composer deep-merges every
|
|
126
|
+
* active plugin's fragment (in plugin order) into the written config. Returning
|
|
127
|
+
* null (or a plugin omitting `config`) contributes nothing.
|
|
128
|
+
*
|
|
129
|
+
* `defaults` is a fn — it rides on the authored RigPlugin and is ignored by the
|
|
130
|
+
* serializable manifest Schema, like every other executable contribution entry.
|
|
131
|
+
*/
|
|
132
|
+
export interface PluginConfigContribution {
|
|
133
|
+
defaults(context: ConfigDefaultsContext): Record<string, unknown> | null;
|
|
134
|
+
}
|
|
99
135
|
export interface PluginContributes {
|
|
100
136
|
validators?: readonly Validator[];
|
|
101
137
|
hooks?: readonly Hook[];
|
|
@@ -111,6 +147,9 @@ export interface PluginContributes {
|
|
|
111
147
|
panels?: readonly Panel[];
|
|
112
148
|
blockerClassifiers?: readonly BlockerClassifierEntry[];
|
|
113
149
|
sessionExtensions?: readonly SessionExtensionEntry[];
|
|
150
|
+
seedEntrypoints?: readonly SeedEntrypoint[];
|
|
151
|
+
/** Declarative config defaults this plugin contributes to `.rig/rigfig.toml`. */
|
|
152
|
+
config?: PluginConfigContribution;
|
|
114
153
|
}
|
|
115
154
|
/**
|
|
116
155
|
* The authored plugin object. Validated against the `RigPlugin` metadata schema
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function showProfile(projectRoot: string, compact?: boolean): Promise<void>;
|
|
2
|
+
export declare function setProfile(projectRoot: string, options: {
|
|
3
|
+
model?: string;
|
|
4
|
+
runtime?: string;
|
|
5
|
+
plugin?: string;
|
|
6
|
+
preset?: string;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
export declare function showReviewProfile(projectRoot: string): Promise<void>;
|
|
9
|
+
export declare function setReviewProfile(projectRoot: string, mode: string, provider?: string): Promise<void>;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/profile-ops.ts
|
|
3
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync } from "fs";
|
|
4
|
+
|
|
5
|
+
// packages/core/src/harness-paths.ts
|
|
6
|
+
import { resolve as resolve3 } from "path";
|
|
7
|
+
|
|
8
|
+
// packages/core/src/layout.ts
|
|
9
|
+
import { resolve as resolve2 } from "path";
|
|
10
|
+
import {
|
|
11
|
+
RIG_ARTIFACTS_DIRNAME,
|
|
12
|
+
RIG_DEFINITION_DIRNAME,
|
|
13
|
+
RIG_STATE_DIRNAME
|
|
14
|
+
} from "@rig/contracts";
|
|
15
|
+
|
|
16
|
+
// packages/core/src/checkout-root.ts
|
|
17
|
+
import { dirname, resolve } from "path";
|
|
18
|
+
import { existsSync } from "fs";
|
|
19
|
+
function findNearestGitCheckoutRoot(startDir) {
|
|
20
|
+
let current = resolve(startDir);
|
|
21
|
+
for (;; ) {
|
|
22
|
+
if (existsSync(resolve(current, ".git")))
|
|
23
|
+
return current;
|
|
24
|
+
const parent = dirname(current);
|
|
25
|
+
if (parent === current)
|
|
26
|
+
return null;
|
|
27
|
+
current = parent;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function resolveCheckoutRoot(projectRoot) {
|
|
31
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
32
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
33
|
+
if (explicit) {
|
|
34
|
+
const explicitRoot = resolve(explicit);
|
|
35
|
+
const gitRoot = findNearestGitCheckoutRoot(explicitRoot);
|
|
36
|
+
if (gitRoot)
|
|
37
|
+
return gitRoot;
|
|
38
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there or above it.`);
|
|
39
|
+
}
|
|
40
|
+
return findNearestGitCheckoutRoot(normalizedProjectRoot) ?? normalizedProjectRoot;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// packages/core/src/layout.ts
|
|
44
|
+
var resolveMonorepoRoot = resolveCheckoutRoot;
|
|
45
|
+
function resolveRuntimeWorkspaceLayout(workspaceDir) {
|
|
46
|
+
const root = resolve2(workspaceDir);
|
|
47
|
+
const rigRoot = resolve2(root, ".rig");
|
|
48
|
+
const logsDir = resolve2(rigRoot, "logs");
|
|
49
|
+
const stateDir = resolve2(rigRoot, "state");
|
|
50
|
+
const runtimeDir = resolve2(rigRoot, "runtime");
|
|
51
|
+
const binDir = resolve2(rigRoot, "bin");
|
|
52
|
+
return {
|
|
53
|
+
workspaceDir: root,
|
|
54
|
+
rigRoot,
|
|
55
|
+
stateDir,
|
|
56
|
+
logsDir,
|
|
57
|
+
artifactsRoot: resolve2(root, RIG_ARTIFACTS_DIRNAME),
|
|
58
|
+
runtimeDir,
|
|
59
|
+
homeDir: resolve2(rigRoot, "home"),
|
|
60
|
+
tmpDir: resolve2(rigRoot, "tmp"),
|
|
61
|
+
cacheDir: resolve2(rigRoot, "cache"),
|
|
62
|
+
sessionDir: resolve2(rigRoot, "session"),
|
|
63
|
+
binDir,
|
|
64
|
+
distDir: resolve2(rigRoot, "dist"),
|
|
65
|
+
pluginBinDir: resolve2(binDir, "plugins"),
|
|
66
|
+
contextPath: resolve2(rigRoot, "runtime-context.json"),
|
|
67
|
+
controlPlaneEventsFile: resolve2(logsDir, "control-plane.events.jsonl")
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function resolveActiveRuntimeWorkspaceRoot(_monorepoRoot) {
|
|
71
|
+
const explicit = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
72
|
+
if (!explicit) {
|
|
73
|
+
throw new Error("No active runtime workspace. Set RIG_TASK_WORKSPACE or provision a task runtime first.");
|
|
74
|
+
}
|
|
75
|
+
return resolve2(explicit);
|
|
76
|
+
}
|
|
77
|
+
function resolveRigLayout(projectRoot) {
|
|
78
|
+
const monorepoRoot = resolveMonorepoRoot(projectRoot);
|
|
79
|
+
const definitionRoot = resolve2(projectRoot, RIG_DEFINITION_DIRNAME);
|
|
80
|
+
const runtimeWorkspaceRoot = resolveActiveRuntimeWorkspaceRoot(monorepoRoot);
|
|
81
|
+
const runtimeLayout = resolveRuntimeWorkspaceLayout(runtimeWorkspaceRoot);
|
|
82
|
+
const policyDir = resolve2(definitionRoot, "policy");
|
|
83
|
+
return {
|
|
84
|
+
projectRoot,
|
|
85
|
+
monorepoRoot,
|
|
86
|
+
definitionRoot,
|
|
87
|
+
runtimeWorkspaceRoot,
|
|
88
|
+
stateRoot: runtimeLayout.rigRoot,
|
|
89
|
+
artifactsRoot: runtimeLayout.artifactsRoot,
|
|
90
|
+
configPath: resolve2(definitionRoot, "config.sh"),
|
|
91
|
+
taskConfigPath: resolve2(runtimeWorkspaceRoot, ".rig", "task-config.json"),
|
|
92
|
+
policyDir,
|
|
93
|
+
policyFile: resolve2(policyDir, "policy.json"),
|
|
94
|
+
pluginsDir: resolve2(definitionRoot, "plugins"),
|
|
95
|
+
hooksDir: resolve2(definitionRoot, "hooks"),
|
|
96
|
+
toolsDir: resolve2(definitionRoot, "tools"),
|
|
97
|
+
templatesDir: resolve2(definitionRoot, "templates"),
|
|
98
|
+
validationDir: resolve2(definitionRoot, "validation"),
|
|
99
|
+
stateDir: runtimeLayout.stateDir,
|
|
100
|
+
logsDir: runtimeLayout.logsDir,
|
|
101
|
+
notificationsDir: resolve2(definitionRoot, "notifications"),
|
|
102
|
+
runtimeDir: runtimeLayout.runtimeDir,
|
|
103
|
+
distDir: runtimeLayout.distDir,
|
|
104
|
+
binDir: runtimeLayout.binDir,
|
|
105
|
+
pluginBinDir: runtimeLayout.pluginBinDir,
|
|
106
|
+
keybindingsPath: resolve2(definitionRoot, "keybindings.json"),
|
|
107
|
+
controlPlaneEventsFile: runtimeLayout.controlPlaneEventsFile
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// packages/core/src/harness-paths.ts
|
|
112
|
+
function resolveHarnessPaths(projectRoot) {
|
|
113
|
+
const hasRuntimeWorkspace = Boolean(process.env.RIG_TASK_WORKSPACE?.trim());
|
|
114
|
+
const monorepoRoot = resolveCheckoutRoot(projectRoot);
|
|
115
|
+
const harnessRoot = resolve3(projectRoot, "rig");
|
|
116
|
+
const stateRoot = resolve3(projectRoot, ".rig");
|
|
117
|
+
const layout = hasRuntimeWorkspace ? resolveRigLayout(projectRoot) : null;
|
|
118
|
+
const stateDir = layout?.stateDir ?? resolve3(stateRoot, "state");
|
|
119
|
+
const logsDir = layout?.logsDir ?? resolve3(stateRoot, "logs");
|
|
120
|
+
const artifactsDir = layout?.artifactsRoot ?? resolve3(monorepoRoot, "artifacts");
|
|
121
|
+
const taskConfigPath = layout?.taskConfigPath ?? resolve3(monorepoRoot, ".rig", "task-config.json");
|
|
122
|
+
const binDir = layout?.binDir ?? resolve3(stateRoot, "bin");
|
|
123
|
+
return {
|
|
124
|
+
harnessRoot,
|
|
125
|
+
stateDir: process.env.RIG_STATE_DIR || stateDir,
|
|
126
|
+
artifactsDir,
|
|
127
|
+
logsDir: process.env.RIG_LOGS_DIR || logsDir,
|
|
128
|
+
binDir,
|
|
129
|
+
hooksDir: resolve3(harnessRoot, "hooks"),
|
|
130
|
+
validationDir: resolve3(harnessRoot, "validation"),
|
|
131
|
+
taskConfigPath,
|
|
132
|
+
sessionPath: process.env.RIG_SESSION_FILE || resolve3(stateRoot, "session", "session.json"),
|
|
133
|
+
monorepoRoot,
|
|
134
|
+
tsApiTestsDir: process.env.TS_API_TESTS_DIR || resolve3(monorepoRoot, "TSAPITests"),
|
|
135
|
+
taskRepoCommitsPath: resolve3(stateDir, "task-repo-commits.json"),
|
|
136
|
+
baseRepoPinsPath: resolve3(stateDir, "base-repo-pins.json"),
|
|
137
|
+
failedApproachesPath: resolve3(stateDir, "failed_approaches.md"),
|
|
138
|
+
agentProfilePath: resolve3(stateDir, "agent-profile.json"),
|
|
139
|
+
reviewProfilePath: resolve3(stateDir, "review-profile.json")
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// packages/core/src/profile-ops.ts
|
|
144
|
+
var DEFAULTS = {
|
|
145
|
+
model: "pi",
|
|
146
|
+
runtime: "pi",
|
|
147
|
+
plugin: "pi",
|
|
148
|
+
reviewMode: parseEnvOrDefault(process.env.AI_REVIEW_MODE, ["off", "advisory", "required"], "advisory"),
|
|
149
|
+
reviewProvider: parseEnvOrDefault(process.env.AI_REVIEW_PROVIDER, ["greptile", "github"], "github")
|
|
150
|
+
};
|
|
151
|
+
function parseEnvOrDefault(value, allowed, fallback) {
|
|
152
|
+
if (!value) {
|
|
153
|
+
return fallback;
|
|
154
|
+
}
|
|
155
|
+
return allowed.includes(value) ? value : fallback;
|
|
156
|
+
}
|
|
157
|
+
async function readProfileFile(path) {
|
|
158
|
+
if (!existsSync2(path)) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
return await Bun.file(path).json();
|
|
163
|
+
} catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function showProfile(projectRoot, compact = false) {
|
|
168
|
+
const paths = resolveHarnessPaths(projectRoot);
|
|
169
|
+
const profile = await loadProfile(paths.agentProfilePath);
|
|
170
|
+
if (compact) {
|
|
171
|
+
console.log(`model=${profile.model} runtime=${profile.runtime} plugin=${profile.agent_plugin}`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
console.log("Execution Profile:");
|
|
175
|
+
console.log(` model: ${profile.model}`);
|
|
176
|
+
console.log(` runtime: ${profile.runtime}`);
|
|
177
|
+
console.log(` plugin: ${profile.agent_plugin}`);
|
|
178
|
+
}
|
|
179
|
+
async function setProfile(projectRoot, options) {
|
|
180
|
+
const invalid = [options.model, options.runtime, options.plugin, options.preset].filter((value) => typeof value === "string" && value.trim().length > 0 && value.trim() !== "pi");
|
|
181
|
+
if (invalid.length > 0) {
|
|
182
|
+
throw new Error("Only the Pi runtime profile is supported by this Rig substrate.");
|
|
183
|
+
}
|
|
184
|
+
const paths = resolveHarnessPaths(projectRoot);
|
|
185
|
+
mkdirSync(paths.stateDir, { recursive: true });
|
|
186
|
+
const next = {
|
|
187
|
+
model: "pi",
|
|
188
|
+
runtime: "pi",
|
|
189
|
+
agent_plugin: "pi",
|
|
190
|
+
updated_at: new Date().toISOString()
|
|
191
|
+
};
|
|
192
|
+
writeFileSync(paths.agentProfilePath, `${JSON.stringify(next, null, 2)}
|
|
193
|
+
`, "utf-8");
|
|
194
|
+
await showProfile(projectRoot, false);
|
|
195
|
+
}
|
|
196
|
+
async function showReviewProfile(projectRoot) {
|
|
197
|
+
const paths = resolveHarnessPaths(projectRoot);
|
|
198
|
+
const profile = await loadReviewProfile(paths.reviewProfilePath);
|
|
199
|
+
console.log("AI Review Profile:");
|
|
200
|
+
console.log(` mode: ${profile.mode}`);
|
|
201
|
+
console.log(` provider: ${profile.provider}`);
|
|
202
|
+
console.log(` file: ${paths.reviewProfilePath}`);
|
|
203
|
+
}
|
|
204
|
+
async function setReviewProfile(projectRoot, mode, provider) {
|
|
205
|
+
if (mode !== "off" && mode !== "advisory" && mode !== "required") {
|
|
206
|
+
throw new Error(`Invalid mode: ${mode}. Use off|advisory|required.`);
|
|
207
|
+
}
|
|
208
|
+
const resolvedProvider = provider || DEFAULTS.reviewProvider;
|
|
209
|
+
if (resolvedProvider !== "greptile" && resolvedProvider !== "github") {
|
|
210
|
+
throw new Error(`Invalid provider: ${resolvedProvider}. Supported: github|greptile.`);
|
|
211
|
+
}
|
|
212
|
+
const paths = resolveHarnessPaths(projectRoot);
|
|
213
|
+
mkdirSync(paths.stateDir, { recursive: true });
|
|
214
|
+
const next = {
|
|
215
|
+
mode,
|
|
216
|
+
provider: resolvedProvider,
|
|
217
|
+
updated_at: new Date().toISOString()
|
|
218
|
+
};
|
|
219
|
+
writeFileSync(paths.reviewProfilePath, `${JSON.stringify(next, null, 2)}
|
|
220
|
+
`, "utf-8");
|
|
221
|
+
await showReviewProfile(projectRoot);
|
|
222
|
+
}
|
|
223
|
+
async function loadProfile(path) {
|
|
224
|
+
const parsed = await readProfileFile(path);
|
|
225
|
+
return {
|
|
226
|
+
model: "pi",
|
|
227
|
+
runtime: "pi",
|
|
228
|
+
agent_plugin: "pi",
|
|
229
|
+
updated_at: parsed?.updated_at || new Date().toISOString()
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async function loadReviewProfile(path) {
|
|
233
|
+
const parsed = await readProfileFile(path);
|
|
234
|
+
if (parsed) {
|
|
235
|
+
return {
|
|
236
|
+
mode: parsed.mode || DEFAULTS.reviewMode,
|
|
237
|
+
provider: parsed.provider || DEFAULTS.reviewProvider,
|
|
238
|
+
updated_at: parsed.updated_at || new Date().toISOString()
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
mode: DEFAULTS.reviewMode,
|
|
243
|
+
provider: DEFAULTS.reviewProvider,
|
|
244
|
+
updated_at: new Date().toISOString()
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
export {
|
|
248
|
+
showReviewProfile,
|
|
249
|
+
showProfile,
|
|
250
|
+
setReviewProfile,
|
|
251
|
+
setProfile
|
|
252
|
+
};
|
|
@@ -9,19 +9,11 @@ export type ProjectConfigPresence = {
|
|
|
9
9
|
readonly status: "missing";
|
|
10
10
|
readonly path: null;
|
|
11
11
|
};
|
|
12
|
-
export type ProjectPluginResolutionWarningCode = never;
|
|
13
|
-
export type ProjectPluginResolutionWarning = {
|
|
14
|
-
readonly code: ProjectPluginResolutionWarningCode;
|
|
15
|
-
readonly level: "warning" | "info";
|
|
16
|
-
readonly message: string;
|
|
17
|
-
};
|
|
18
12
|
export type ProjectPluginResolution = {
|
|
19
13
|
readonly mode: ProjectPluginResolutionMode;
|
|
20
14
|
readonly projectRoot: string;
|
|
21
|
-
readonly surfaceName: string;
|
|
22
15
|
readonly configPresence: ProjectConfigPresence;
|
|
23
16
|
readonly config: RigConfig | null;
|
|
24
|
-
readonly warnings: readonly ProjectPluginResolutionWarning[];
|
|
25
17
|
readonly plugins: readonly RigPlugin[];
|
|
26
18
|
};
|
|
27
19
|
export type ProjectPluginHostResult = {
|
|
@@ -31,15 +23,12 @@ export type ProjectPluginHostResult = {
|
|
|
31
23
|
export type LoadProjectConfig = (projectRoot: string) => Promise<RigConfig>;
|
|
32
24
|
export type LoadProjectPluginSetOptions = {
|
|
33
25
|
readonly mode?: ProjectPluginResolutionMode;
|
|
34
|
-
readonly surfaceName?: string;
|
|
35
26
|
readonly load?: LoadProjectConfig;
|
|
36
27
|
};
|
|
37
|
-
export type ProjectPluginResolutionOptions = Omit<LoadProjectPluginSetOptions, "mode"
|
|
28
|
+
export type ProjectPluginResolutionOptions = Omit<LoadProjectPluginSetOptions, "mode"> & {
|
|
38
29
|
readonly projectRoot: string;
|
|
39
30
|
readonly mode: ProjectPluginResolutionMode;
|
|
40
|
-
readonly surfaceName: string;
|
|
41
31
|
};
|
|
42
|
-
export declare function projectPluginResolutionWarningMessages(resolved: Pick<ProjectPluginResolution, "warnings">): readonly string[];
|
|
43
32
|
export declare function loadProjectPluginSet(options: ProjectPluginResolutionOptions): Promise<ProjectPluginResolution>;
|
|
44
33
|
export declare function loadProjectPluginSet(projectRoot: string, options?: LoadProjectPluginSetOptions): Promise<ProjectPluginResolution>;
|
|
45
34
|
export type ResolvedPluginHost = {
|
|
@@ -62,5 +51,13 @@ export type ResolvePluginHostOptions = {
|
|
|
62
51
|
* cannot poison the canonical on-disk host other consumers read.
|
|
63
52
|
*/
|
|
64
53
|
export declare function resolvePluginHost(projectRoot: string, options?: ResolvePluginHostOptions): Promise<ResolvedPluginHost>;
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the plugin host for a project root, returning the host plus the full
|
|
56
|
+
* resolution shape. This delegates to the single cached `resolvePluginHost`
|
|
57
|
+
* memo so every consumer in a process SHARES one resolved host for a root
|
|
58
|
+
* instead of re-evaluating rig.config.ts and constructing an independent host
|
|
59
|
+
* (architecture-reference §18: one resolved host per process/root). Graceful
|
|
60
|
+
* missing-config degradation and the custom-`load` bypass are preserved.
|
|
61
|
+
*/
|
|
65
62
|
export declare function createProjectPluginHost(options: ProjectPluginResolutionOptions): Promise<ProjectPluginHostResult>;
|
|
66
63
|
export declare function createProjectPluginHost(projectRoot: string, options?: LoadProjectPluginSetOptions): Promise<ProjectPluginHostResult>;
|