@open-agent-toolkit/cli 0.1.45 → 0.1.46

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 (69) hide show
  1. package/assets/agents/oat-reviewer.md +2 -2
  2. package/assets/docs/cli-utilities/config-and-local-state.md +7 -0
  3. package/assets/docs/cli-utilities/configuration.md +51 -11
  4. package/assets/docs/provider-sync/config.md +5 -1
  5. package/assets/docs/provider-sync/manifest-and-drift.md +6 -1
  6. package/assets/docs/provider-sync/providers.md +6 -3
  7. package/assets/docs/provider-sync/scope-and-surface.md +2 -1
  8. package/assets/docs/reference/oat-directory-structure.md +2 -2
  9. package/assets/docs/workflows/projects/dispatch-ceiling.md +41 -28
  10. package/assets/docs/workflows/projects/implementation-execution.md +20 -13
  11. package/assets/public-package-versions.json +4 -4
  12. package/assets/skills/oat-project-implement/SKILL.md +114 -59
  13. package/assets/skills/oat-project-quick-start/SKILL.md +32 -20
  14. package/dist/commands/config/index.d.ts +2 -2
  15. package/dist/commands/config/index.d.ts.map +1 -1
  16. package/dist/commands/config/index.js +71 -20
  17. package/dist/commands/doctor/index.d.ts +2 -2
  18. package/dist/commands/doctor/index.d.ts.map +1 -1
  19. package/dist/commands/doctor/index.js +83 -27
  20. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  21. package/dist/commands/project/dispatch-ceiling/index.js +137 -10
  22. package/dist/commands/providers/codex/index.d.ts +4 -0
  23. package/dist/commands/providers/codex/index.d.ts.map +1 -0
  24. package/dist/commands/providers/codex/index.js +7 -0
  25. package/dist/commands/providers/codex/materialize.d.ts +5 -0
  26. package/dist/commands/providers/codex/materialize.d.ts.map +1 -0
  27. package/dist/commands/providers/codex/materialize.js +152 -0
  28. package/dist/commands/providers/index.d.ts +2 -2
  29. package/dist/commands/providers/index.d.ts.map +1 -1
  30. package/dist/commands/providers/index.js +4 -2
  31. package/dist/commands/providers/providers.types.d.ts +23 -0
  32. package/dist/commands/providers/providers.types.d.ts.map +1 -1
  33. package/dist/commands/shared/codex-strays.d.ts.map +1 -1
  34. package/dist/commands/shared/codex-strays.js +11 -1
  35. package/dist/commands/status/index.d.ts +4 -1
  36. package/dist/commands/status/index.d.ts.map +1 -1
  37. package/dist/commands/status/index.js +1 -1
  38. package/dist/commands/sync/index.d.ts.map +1 -1
  39. package/dist/commands/sync/index.js +1 -1
  40. package/dist/commands/sync/sync.types.d.ts +4 -1
  41. package/dist/commands/sync/sync.types.d.ts.map +1 -1
  42. package/dist/config/dispatch-ceiling-preset.d.ts +4 -0
  43. package/dist/config/dispatch-ceiling-preset.d.ts.map +1 -1
  44. package/dist/config/dispatch-ceiling-preset.js +3 -0
  45. package/dist/config/dispatch-policy-options.d.ts +20 -0
  46. package/dist/config/dispatch-policy-options.d.ts.map +1 -0
  47. package/dist/config/dispatch-policy-options.js +96 -0
  48. package/dist/config/oat-config.d.ts +6 -0
  49. package/dist/config/oat-config.d.ts.map +1 -1
  50. package/dist/config/oat-config.js +15 -0
  51. package/dist/providers/ceiling/registry.d.ts +7 -5
  52. package/dist/providers/ceiling/registry.d.ts.map +1 -1
  53. package/dist/providers/ceiling/registry.js +16 -5
  54. package/dist/providers/codex/codec/config-merge.d.ts +6 -0
  55. package/dist/providers/codex/codec/config-merge.d.ts.map +1 -1
  56. package/dist/providers/codex/codec/config-merge.js +7 -0
  57. package/dist/providers/codex/codec/materialize.d.ts +16 -0
  58. package/dist/providers/codex/codec/materialize.d.ts.map +1 -0
  59. package/dist/providers/codex/codec/materialize.js +57 -0
  60. package/dist/providers/codex/codec/shared.d.ts +1 -0
  61. package/dist/providers/codex/codec/shared.d.ts.map +1 -1
  62. package/dist/providers/codex/codec/shared.js +9 -1
  63. package/dist/providers/codex/codec/sync-extension.d.ts +7 -1
  64. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  65. package/dist/providers/codex/codec/sync-extension.js +183 -46
  66. package/dist/providers/identity/availability.d.ts +24 -1
  67. package/dist/providers/identity/availability.d.ts.map +1 -1
  68. package/dist/providers/identity/availability.js +253 -19
  69. package/package.json +2 -2
@@ -5,11 +5,12 @@ import { resolveProjectsRoot } from '../shared/oat-paths.js';
5
5
  import { confirmAction, } from '../shared/shared.prompts.js';
6
6
  import { readGlobalOptions } from '../shared/shared.utils.js';
7
7
  import { compileDispatchCeilingPreset } from '../../config/dispatch-ceiling-preset.js';
8
- import { readOatConfig, readOatLocalConfig, readUserConfig, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
8
+ import { dispatchPolicyModeDescription, dispatchPolicyPolicyDescription, managedDispatchPolicyValueList, } from '../../config/dispatch-policy-options.js';
9
+ import { VALID_DISPATCH_POLICY_MODES, VALID_MANAGED_DISPATCH_POLICIES, isCodexMaterializedRouteTarget, readOatConfig, readOatLocalConfig, readUserConfig, validateDispatchRouteTarget, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
9
10
  import { resolveEffectiveConfig, } from '../../config/resolve.js';
10
11
  import { resolveAssetsRoot } from '../../fs/assets.js';
11
12
  import { resolveProjectRoot } from '../../fs/paths.js';
12
- import { validateMatrixCell, } from '../../providers/identity/availability.js';
13
+ import { normalizeMatrixCellAvailability, validateMatrixCell, } from '../../providers/identity/availability.js';
13
14
  import { Command } from 'commander';
14
15
  import { createConfigDumpCommand } from './dump.js';
15
16
  const DISPATCH_CEILING_PROVIDER_KEY_PREFIX = 'workflow.dispatchCeiling.providers.';
@@ -478,7 +479,7 @@ const CONFIG_CATALOG = [
478
479
  defaultValue: 'unset',
479
480
  mutability: 'read/write',
480
481
  owningCommand: 'oat config set workflow.dispatchPolicy.mode <value>',
481
- description: 'Dispatch policy mode. "managed" means OAT selects model/effort from workflow.dispatchPolicy.policy; "inherit" means OAT leaves dispatch controls to the host/provider defaults. Set workflow.dispatchPolicy.policy to choose a managed policy. Resolution: local > shared > user > default.',
482
+ description: dispatchPolicyModeDescription(),
482
483
  },
483
484
  {
484
485
  key: 'workflow.dispatchPolicy.policy',
@@ -489,7 +490,7 @@ const CONFIG_CATALOG = [
489
490
  defaultValue: 'unset',
490
491
  mutability: 'read/write',
491
492
  owningCommand: 'oat config set workflow.dispatchPolicy.policy <value>',
492
- description: 'Managed dispatch policy. economy, balanced, high, and frontier are capped managed policies; uncapped keeps OAT-managed preferred selection without provider caps. Setting this key writes workflow.dispatchPolicy.mode=managed. Resolution: local > shared > user > default.',
493
+ description: dispatchPolicyPolicyDescription(),
493
494
  },
494
495
  {
495
496
  key: 'workflow.dispatchCeiling.providers.codex',
@@ -614,14 +615,8 @@ const WORKFLOW_ENUM_VALUES = {
614
615
  'workflow.postImplementSequence': ['wait', 'summary', 'pr', 'docs-pr'],
615
616
  'workflow.reviewExecutionModel': ['subagent', 'inline', 'fresh-session'],
616
617
  'workflow.designMode': ['collaborative', 'selective', 'draft'],
617
- 'workflow.dispatchPolicy.mode': ['managed', 'inherit'],
618
- 'workflow.dispatchPolicy.policy': [
619
- 'economy',
620
- 'balanced',
621
- 'high',
622
- 'frontier',
623
- 'uncapped',
624
- ],
618
+ 'workflow.dispatchPolicy.mode': [...VALID_DISPATCH_POLICY_MODES],
619
+ 'workflow.dispatchPolicy.policy': [...VALID_MANAGED_DISPATCH_POLICIES],
625
620
  'workflow.dispatchCeiling.preset': ['balanced', 'maximum', 'cost-conscious'],
626
621
  'workflow.dispatchCeiling.providers.codex': [
627
622
  'low',
@@ -747,13 +742,15 @@ function isRecord(value) {
747
742
  return typeof value === 'object' && value !== null && !Array.isArray(value);
748
743
  }
749
744
  function matrixCellAvailabilityWarning(path, value, availability) {
750
- if (availability === 'valid') {
745
+ const result = normalizeMatrixCellAvailability(availability);
746
+ if (result.availability === 'valid') {
751
747
  return null;
752
748
  }
753
- if (availability === 'unknown-value') {
754
- return `${path} value '${value}' was not recognized by the provider availability oracle; saving anyway.`;
749
+ const details = result.message ? ` ${result.message}` : '';
750
+ if (result.availability === 'unknown-value') {
751
+ return `${path} value '${value}' was not recognized by the provider availability oracle; saving anyway.${details}`;
755
752
  }
756
- return `${path} value '${value}' could not be validated because the provider availability oracle is unavailable; saving anyway.`;
753
+ return `${path} value '${value}' could not be validated because the provider availability oracle is unavailable; saving anyway.${details}`;
757
754
  }
758
755
  function parseDispatchMatrixRecommendation(raw) {
759
756
  const parsed = JSON.parse(raw);
@@ -775,6 +772,9 @@ function parseDispatchMatrixRecommendation(raw) {
775
772
  function isRouteTarget(entry) {
776
773
  return isRecord(entry);
777
774
  }
775
+ function formatRouteTargetValue(entry) {
776
+ return [entry.model, entry.effort].filter(Boolean).join('/');
777
+ }
778
778
  function addDispatchMatrixCellRefs(refs, provider, path, cell) {
779
779
  if (typeof cell === 'string') {
780
780
  refs.push({ provider, value: cell, path });
@@ -789,11 +789,31 @@ function addDispatchMatrixCellRefs(refs, provider, path, cell) {
789
789
  if (!isRouteTarget(entry)) {
790
790
  continue;
791
791
  }
792
+ const targetProvider = entry.harness ?? provider;
793
+ if (isCodexMaterializedRouteTarget(provider, entry)) {
794
+ if (entry.model && entry.effort) {
795
+ refs.push({
796
+ provider: targetProvider,
797
+ value: formatRouteTargetValue(entry),
798
+ path: entryPath,
799
+ target: entry,
800
+ });
801
+ continue;
802
+ }
803
+ }
792
804
  if (entry.model) {
793
- refs.push({ provider, value: entry.model, path: `${entryPath}.model` });
805
+ refs.push({
806
+ provider: targetProvider,
807
+ value: entry.model,
808
+ path: `${entryPath}.model`,
809
+ });
794
810
  }
795
811
  if (entry.effort) {
796
- refs.push({ provider, value: entry.effort, path: `${entryPath}.effort` });
812
+ refs.push({
813
+ provider: targetProvider,
814
+ value: entry.effort,
815
+ path: `${entryPath}.effort`,
816
+ });
797
817
  }
798
818
  }
799
819
  }
@@ -814,6 +834,30 @@ function collectDispatchMatrixCellRefs(providers) {
814
834
  }
815
835
  return refs;
816
836
  }
837
+ function collectDispatchMatrixTargetValidationErrors(providers) {
838
+ const errors = [];
839
+ for (const [provider, providerValue] of Object.entries(providers)) {
840
+ if (typeof providerValue === 'string') {
841
+ continue;
842
+ }
843
+ const providerPath = `workflow.dispatchCeiling.providers.${provider}`;
844
+ for (const [tier, cell] of Object.entries(providerValue)) {
845
+ if (!Array.isArray(cell)) {
846
+ continue;
847
+ }
848
+ for (const [index, entry] of cell.entries()) {
849
+ if (!isRouteTarget(entry)) {
850
+ continue;
851
+ }
852
+ const validation = validateDispatchRouteTarget(provider, entry);
853
+ if (!validation.valid) {
854
+ errors.push(`${providerPath}.${tier}[${index}]: ${validation.reason}`);
855
+ }
856
+ }
857
+ }
858
+ }
859
+ return errors;
860
+ }
817
861
  function applyWorkflowValue(workflow, key, value) {
818
862
  const subKey = key.slice('workflow.'.length);
819
863
  if (subKey === 'dispatchPolicy.mode') {
@@ -826,7 +870,7 @@ function applyWorkflowValue(workflow, key, value) {
826
870
  }
827
871
  const policy = workflow.dispatchPolicy?.policy;
828
872
  if (!policy) {
829
- throw new Error('Cannot set workflow.dispatchPolicy.mode to managed without an existing workflow.dispatchPolicy.policy. Set workflow.dispatchPolicy.policy <economy|balanced|high|frontier|uncapped> instead.');
873
+ throw new Error(`Cannot set workflow.dispatchPolicy.mode to managed without an existing workflow.dispatchPolicy.policy. Set workflow.dispatchPolicy.policy <${managedDispatchPolicyValueList('|')}> instead.`);
830
874
  }
831
875
  return {
832
876
  ...workflow,
@@ -956,6 +1000,7 @@ async function setConfigValue(repoRoot, userConfigDir, key, rawValue, surface, d
956
1000
  const availability = await dependencies.validateMatrixCell(providerNameFromConfigKey(key), parsedValue, {
957
1001
  cwd: repoRoot,
958
1002
  env: dependencies.processEnv,
1003
+ detailed: true,
959
1004
  });
960
1005
  const warning = dispatchProviderAvailabilityWarning(key, parsedValue, availability);
961
1006
  if (warning) {
@@ -1149,9 +1194,13 @@ async function loadDispatchMatrixRecommendation(dependencies) {
1149
1194
  return parseDispatchMatrixRecommendation(await dependencies.readFile(assetPath));
1150
1195
  }
1151
1196
  async function validateRecommendationCells(repoRoot, recommendation, dependencies, warn) {
1197
+ const targetErrors = collectDispatchMatrixTargetValidationErrors(recommendation.providers);
1198
+ if (targetErrors.length > 0) {
1199
+ throw new Error(targetErrors.join('\n'));
1200
+ }
1152
1201
  for (const ref of collectDispatchMatrixCellRefs(recommendation.providers)) {
1153
1202
  const closedValues = closedDispatchProviderValues(ref.provider);
1154
- if (closedValues && !closedValues.includes(ref.value)) {
1203
+ if (!ref.target && closedValues && !closedValues.includes(ref.value)) {
1155
1204
  throw new Error(`Invalid value for ${ref.path}: expected one of ${closedValues.join(' | ')}, got '${ref.value}'`);
1156
1205
  }
1157
1206
  let availability;
@@ -1159,6 +1208,8 @@ async function validateRecommendationCells(repoRoot, recommendation, dependencie
1159
1208
  availability = await dependencies.validateMatrixCell(ref.provider, ref.value, {
1160
1209
  cwd: repoRoot,
1161
1210
  env: dependencies.processEnv,
1211
+ detailed: true,
1212
+ ...(ref.target ? { target: ref.target } : {}),
1162
1213
  });
1163
1214
  }
1164
1215
  catch {
@@ -2,7 +2,7 @@ import { type CommandContext, type GlobalOptions } from '../../app/command-conte
2
2
  import { type PjmDoctorOptions } from '../pjm/doctor.js';
3
3
  import { type OatConfig, type OatLocalConfig, type UserConfig } from '../../config/oat-config.js';
4
4
  import { type Manifest } from '../../manifest/index.js';
5
- import { type MatrixCellAvailability, type ValidateMatrixCellOptions } from '../../providers/identity/availability.js';
5
+ import { type MatrixCellAvailabilityResponse, type ValidateMatrixCellOptions } from '../../providers/identity/availability.js';
6
6
  import type { ConcreteScope } from '../../shared/types.js';
7
7
  import { type DoctorCheck } from '../../ui/output.js';
8
8
  import { Command } from 'commander';
@@ -22,7 +22,7 @@ interface DoctorDependencies {
22
22
  readOatConfig: (repoRoot: string) => Promise<OatConfig>;
23
23
  readOatLocalConfig: (repoRoot: string) => Promise<OatLocalConfig>;
24
24
  readUserConfig: (userConfigDir: string) => Promise<UserConfig>;
25
- validateMatrixCell: (provider: string, value: string, options: ValidateMatrixCellOptions) => Promise<MatrixCellAvailability>;
25
+ validateMatrixCell: (provider: string, value: string, options: ValidateMatrixCellOptions) => Promise<MatrixCellAvailabilityResponse>;
26
26
  processEnv: NodeJS.ProcessEnv;
27
27
  runPjmDoctorChecks: (repoRoot: string, options?: PjmDoctorOptions) => Promise<DoctorCheck[]>;
28
28
  checkSkillVersions: (scopeRoot: string, assetsRoot: string, pathExists: (path: string) => Promise<boolean>) => Promise<SkillVersionReport>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"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"}
@@ -7,17 +7,18 @@ import { createPjmDisabledCheck, runPjmDoctorChecks, } from '../pjm/doctor.js';
7
7
  import { getSkillVersion } from '../shared/frontmatter.js';
8
8
  import { withScopeOption } from '../shared/scope-option.js';
9
9
  import { readGlobalOptions, resolveConcreteScopes, } from '../shared/shared.utils.js';
10
- import { readOatConfig, readOatLocalConfig, readUserConfig, } from '../../config/oat-config.js';
10
+ import { readOatConfig, readOatLocalConfig, readUserConfig, isCodexMaterializedRouteTarget, } from '../../config/oat-config.js';
11
11
  import { resolveAssetsRoot } from '../../fs/assets.js';
12
12
  import { resolveProjectRoot, resolveScopeRoot } from '../../fs/paths.js';
13
13
  import TOML from '@iarna/toml';
14
14
  import { loadManifest } from '../../manifest/index.js';
15
15
  import { claudeAdapter } from '../../providers/claude/index.js';
16
16
  import { codexAdapter } from '../../providers/codex/index.js';
17
+ import { isOatManagedCodexRoleFile } from '../../providers/codex/codec/shared.js';
17
18
  import { copilotAdapter } from '../../providers/copilot/index.js';
18
19
  import { cursorAdapter } from '../../providers/cursor/index.js';
19
20
  import { geminiAdapter } from '../../providers/gemini/index.js';
20
- import { validateMatrixCell, } from '../../providers/identity/availability.js';
21
+ import { normalizeMatrixCellAvailability, validateMatrixCell, } from '../../providers/identity/availability.js';
21
22
  import { formatDoctorResults } from '../../ui/output.js';
22
23
  import { Command } from 'commander';
23
24
  async function pathExistsDefault(path) {
@@ -143,6 +144,9 @@ function createDependencies() {
143
144
  function isRouteTarget(entry) {
144
145
  return typeof entry === 'object' && entry !== null && !Array.isArray(entry);
145
146
  }
147
+ function formatRouteTargetValue(entry) {
148
+ return [entry.model, entry.effort].filter(Boolean).join('/');
149
+ }
146
150
  function addDispatchMatrixCellRefs(refs, layer, provider, path, cell) {
147
151
  if (typeof cell === 'string') {
148
152
  refs.push({ layer, provider, value: cell, path });
@@ -157,10 +161,23 @@ function addDispatchMatrixCellRefs(refs, layer, provider, path, cell) {
157
161
  if (!isRouteTarget(entry)) {
158
162
  continue;
159
163
  }
164
+ const targetProvider = entry.harness ?? provider;
165
+ if (isCodexMaterializedRouteTarget(provider, entry)) {
166
+ if (entry.model && entry.effort) {
167
+ refs.push({
168
+ layer,
169
+ provider: targetProvider,
170
+ value: formatRouteTargetValue(entry),
171
+ path: entryPath,
172
+ target: entry,
173
+ });
174
+ continue;
175
+ }
176
+ }
160
177
  if (entry.model) {
161
178
  refs.push({
162
179
  layer,
163
- provider,
180
+ provider: targetProvider,
164
181
  value: entry.model,
165
182
  path: `${entryPath}.model`,
166
183
  });
@@ -168,7 +185,7 @@ function addDispatchMatrixCellRefs(refs, layer, provider, path, cell) {
168
185
  if (entry.effort) {
169
186
  refs.push({
170
187
  layer,
171
- provider,
188
+ provider: targetProvider,
172
189
  value: entry.effort,
173
190
  path: `${entryPath}.effort`,
174
191
  });
@@ -203,7 +220,12 @@ function collectDispatchMatrixCellRefs(layers) {
203
220
  }
204
221
  function formatDispatchMatrixIssueList(issues) {
205
222
  return issues
206
- .map((issue) => `${issue.path}=${issue.value} (${issue.layer} config)`)
223
+ .map((issue) => {
224
+ const suffix = 'message' in issue && typeof issue.message === 'string'
225
+ ? `; ${issue.message}`
226
+ : '';
227
+ return `${issue.path}=${issue.value} (${issue.layer} config)${suffix}`;
228
+ })
207
229
  .join(', ');
208
230
  }
209
231
  async function createDispatchMatrixDoctorCheck(scopeRoot, layers, dependencies) {
@@ -218,18 +240,24 @@ async function createDispatchMatrixDoctorCheck(scopeRoot, layers, dependencies)
218
240
  }
219
241
  const issues = [];
220
242
  for (const ref of refs) {
221
- let availability;
243
+ let result;
222
244
  try {
223
- availability = await dependencies.validateMatrixCell(ref.provider, ref.value, {
245
+ result = normalizeMatrixCellAvailability(await dependencies.validateMatrixCell(ref.provider, ref.value, {
224
246
  cwd: scopeRoot,
225
247
  env: dependencies.processEnv,
226
- });
248
+ detailed: true,
249
+ ...(ref.target ? { target: ref.target } : {}),
250
+ }));
227
251
  }
228
252
  catch {
229
- availability = 'unvalidated';
253
+ result = { availability: 'unvalidated' };
230
254
  }
231
- if (availability !== 'valid') {
232
- issues.push({ ...ref, availability });
255
+ if (result.availability !== 'valid') {
256
+ issues.push({
257
+ ...ref,
258
+ availability: result.availability,
259
+ ...(result.message ? { message: result.message } : {}),
260
+ });
233
261
  }
234
262
  }
235
263
  if (issues.length === 0) {
@@ -250,16 +278,45 @@ async function createDispatchMatrixDoctorCheck(scopeRoot, layers, dependencies)
250
278
  messageParts.push(`Unvalidated dispatch matrix cells: ${formatDispatchMatrixIssueList(unvalidated)}`);
251
279
  }
252
280
  const hasUnknown = unknown.length > 0;
281
+ const hasUnvalidated = unvalidated.length > 0;
253
282
  return {
254
283
  name: 'project:dispatch_matrix',
255
284
  description: 'Dispatch matrix cell availability',
256
- status: hasUnknown ? 'warn' : 'pass',
285
+ status: hasUnknown || hasUnvalidated ? 'warn' : 'pass',
257
286
  message: messageParts.join('. '),
258
287
  fix: hasUnknown
259
288
  ? 'Run `oat config set workflow.dispatchCeiling.providers.<provider> <value>` with an available value, or refresh provider assets with `oat sync --scope project`.'
260
289
  : undefined,
261
290
  };
262
291
  }
292
+ function getCodexAgentConfigFile(config) {
293
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
294
+ return null;
295
+ }
296
+ const configFile = config.config_file;
297
+ return typeof configFile === 'string' && configFile.startsWith('agents/')
298
+ ? configFile
299
+ : null;
300
+ }
301
+ function isLikelyOatGeneratedCodexRoleName(roleName) {
302
+ return (roleName === 'oat-phase-implementer' ||
303
+ roleName === 'oat-reviewer' ||
304
+ roleName.startsWith('oat-phase-implementer-') ||
305
+ roleName.startsWith('oat-reviewer-'));
306
+ }
307
+ async function isManagedCodexRoleConfig(scopeRoot, roleName, configFile, dependencies) {
308
+ const absoluteRolePath = join(scopeRoot, '.codex', configFile);
309
+ if (!(await dependencies.pathExists(absoluteRolePath))) {
310
+ return isLikelyOatGeneratedCodexRoleName(roleName);
311
+ }
312
+ try {
313
+ const roleContent = await dependencies.readFile(absoluteRolePath);
314
+ return isOatManagedCodexRoleFile(roleContent, roleName);
315
+ }
316
+ catch {
317
+ return false;
318
+ }
319
+ }
263
320
  async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies) {
264
321
  const checks = [];
265
322
  const skillsPath = join(scopeRoot, '.agents', 'skills');
@@ -419,17 +476,18 @@ async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies)
419
476
  !Array.isArray(parsedConfig.agents)
420
477
  ? parsedConfig.agents
421
478
  : null;
422
- const managedRoles = Object.entries(agents ?? {})
423
- .filter(([, config]) => {
424
- if (!config ||
425
- typeof config !== 'object' ||
426
- Array.isArray(config)) {
427
- return false;
479
+ const managedRoles = [];
480
+ const roleConfigFiles = new Map();
481
+ for (const [roleName, roleConfig] of Object.entries(agents ?? {})) {
482
+ const configFile = getCodexAgentConfigFile(roleConfig);
483
+ if (!configFile) {
484
+ continue;
485
+ }
486
+ roleConfigFiles.set(roleName, configFile);
487
+ if (await isManagedCodexRoleConfig(scopeRoot, roleName, configFile, dependencies)) {
488
+ managedRoles.push(roleName);
428
489
  }
429
- const configFile = config.config_file;
430
- return (typeof configFile === 'string' && configFile.startsWith('agents/'));
431
- })
432
- .map(([roleName]) => roleName);
490
+ }
433
491
  if (managedRoles.length > 0) {
434
492
  const multiAgentEnabled = features?.multi_agent === true || features?.multi_agent === 'true';
435
493
  checks.push({
@@ -445,11 +503,9 @@ async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies)
445
503
  });
446
504
  const missingRoleFiles = [];
447
505
  for (const roleName of managedRoles) {
448
- const roleConfig = agents[roleName];
449
- const configFile = roleConfig.config_file;
450
- if (typeof configFile !== 'string') {
506
+ const configFile = roleConfigFiles.get(roleName);
507
+ if (!configFile)
451
508
  continue;
452
- }
453
509
  const absoluteRolePath = join(scopeRoot, '.codex', configFile);
454
510
  if (!(await dependencies.pathExists(absoluteRolePath))) {
455
511
  missingRoleFiles.push(configFile);
@@ -464,7 +520,7 @@ async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies)
464
520
  : `Missing codex role files: ${missingRoleFiles.join(', ')}`,
465
521
  fix: missingRoleFiles.length === 0
466
522
  ? undefined
467
- : 'Regenerate codex roles with `oat sync --scope project`.',
523
+ : 'Regenerate codex roles with `oat sync --scope project`, or materialize a single role with `oat providers codex materialize ...`.',
468
524
  });
469
525
  }
470
526
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/dispatch-ceiling/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAO9B,OAAO,EAML,KAAK,uBAAuB,EAU7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAazB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8BpC,UAAU,2BAA2B;IACnC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC7E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAs+CD,wBAAgB,mCAAmC,CACjD,SAAS,GAAE,OAAO,CAAC,2BAA2B,CAAM,GACnD,OAAO,CAyDT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/dispatch-ceiling/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAY9B,OAAO,EAOL,KAAK,uBAAuB,EAU7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAazB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCpC,UAAU,2BAA2B;IACnC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC7E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AA4pDD,wBAAgB,mCAAmC,CACjD,SAAS,GAAE,OAAO,CAAC,2BAA2B,CAAM,GACnD,OAAO,CAwET"}