@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.
Files changed (89) hide show
  1. package/dist/src/agent-role-registry.d.ts +4 -0
  2. package/dist/src/agent-role-registry.js +27 -0
  3. package/dist/src/authority-paths.d.ts +15 -0
  4. package/dist/src/authority-paths.js +80 -0
  5. package/dist/src/baked-secrets.d.ts +6 -0
  6. package/dist/src/baked-secrets.js +121 -0
  7. package/dist/src/build-time-config.d.ts +12 -0
  8. package/dist/src/build-time-config.js +25 -0
  9. package/dist/src/build-time-config.macro.d.ts +1 -0
  10. package/dist/src/capability-loaders.d.ts +51 -0
  11. package/dist/src/{task-io.js → capability-loaders.js} +237 -195
  12. package/dist/src/capability.d.ts +79 -0
  13. package/dist/src/capability.js +63 -0
  14. package/dist/src/checkout-root.d.ts +1 -0
  15. package/dist/src/checkout-root.js +30 -0
  16. package/dist/src/config-env.d.ts +4 -0
  17. package/dist/src/config-env.js +23 -0
  18. package/dist/src/config.d.ts +1 -1
  19. package/dist/src/config.js +7 -4
  20. package/dist/src/declarative-config.d.ts +14 -0
  21. package/dist/src/declarative-config.js +82 -0
  22. package/dist/src/define-config.d.ts +2 -1
  23. package/dist/src/define-config.js +7 -4
  24. package/dist/src/embedded-plugins.d.ts +59 -0
  25. package/dist/src/embedded-plugins.js +22 -0
  26. package/dist/src/exec.d.ts +13 -0
  27. package/dist/src/exec.js +101 -0
  28. package/dist/src/harness-paths.d.ts +18 -0
  29. package/dist/src/harness-paths.js +141 -0
  30. package/dist/src/hook-materializer.d.ts +72 -0
  31. package/dist/src/hook-materializer.js +274 -0
  32. package/dist/src/hook-runner.d.ts +48 -0
  33. package/dist/src/hook-runner.js +752 -0
  34. package/dist/src/hook-runtime.d.ts +11 -0
  35. package/dist/src/hook-runtime.js +307 -0
  36. package/dist/src/index.d.ts +3 -1
  37. package/dist/src/index.js +119 -5
  38. package/dist/src/json-files.d.ts +9 -0
  39. package/dist/src/json-files.js +125 -0
  40. package/dist/src/kernel-entrypoint.d.ts +22 -0
  41. package/dist/src/kernel-entrypoint.js +537 -0
  42. package/dist/src/layout.d.ts +10 -0
  43. package/dist/src/layout.js +144 -0
  44. package/dist/src/load-config.js +149 -50
  45. package/dist/src/placement.d.ts +50 -0
  46. package/dist/src/placement.js +996 -0
  47. package/dist/src/plugin-host-context.d.ts +66 -0
  48. package/dist/src/plugin-host-context.js +1270 -0
  49. package/dist/src/plugin-host-registries.d.ts +31 -0
  50. package/dist/src/plugin-host-registries.js +79 -0
  51. package/dist/src/plugin-host.d.ts +6 -1
  52. package/dist/src/plugin-host.js +51 -0
  53. package/dist/src/plugin-runtime.d.ts +45 -6
  54. package/dist/src/profile-ops.d.ts +9 -0
  55. package/dist/src/profile-ops.js +252 -0
  56. package/dist/src/project-plugins.d.ts +9 -12
  57. package/dist/src/project-plugins.js +248 -61
  58. package/dist/src/remote-config.d.ts +183 -0
  59. package/dist/src/remote-config.js +574 -0
  60. package/dist/src/root-resolver.d.ts +5 -0
  61. package/dist/src/root-resolver.js +69 -0
  62. package/dist/src/run-provisioning.d.ts +58 -0
  63. package/dist/src/run-provisioning.js +128 -0
  64. package/dist/src/runtime-context.d.ts +20 -0
  65. package/dist/src/runtime-context.js +257 -0
  66. package/dist/src/runtime-events.d.ts +44 -0
  67. package/dist/src/runtime-events.js +212 -0
  68. package/dist/src/runtime-overlay.d.ts +11 -0
  69. package/dist/src/runtime-overlay.js +71 -0
  70. package/dist/src/runtime-paths.d.ts +21 -0
  71. package/dist/src/runtime-paths.js +181 -0
  72. package/dist/src/runtime-provisioning-env.d.ts +5 -0
  73. package/dist/src/runtime-provisioning-env.js +217 -0
  74. package/dist/src/runtime-runner-context.d.ts +12 -0
  75. package/dist/src/runtime-runner-context.js +1 -0
  76. package/dist/src/safe-identifiers.d.ts +44 -0
  77. package/dist/src/safe-identifiers.js +96 -0
  78. package/dist/src/scope-rules.d.ts +4 -0
  79. package/dist/src/scope-rules.js +21 -0
  80. package/dist/src/server-paths.d.ts +26 -0
  81. package/dist/src/server-paths.js +308 -0
  82. package/dist/src/setup-version.d.ts +3 -0
  83. package/dist/src/setup-version.js +14 -0
  84. package/dist/src/task-record-reader.d.ts +3 -0
  85. package/dist/src/task-record-reader.js +9 -0
  86. package/dist/src/validator-registry.d.ts +27 -0
  87. package/dist/src/validator-registry.js +64 -0
  88. package/package.json +136 -6
  89. 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
@@ -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 interface FeatureCapability extends ProductCapabilityRegistration {
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" | "surfaceName"> & {
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>;