@open-agent-toolkit/cli 0.1.48 → 0.1.51
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/assets/agents/oat-phase-implementer.md +16 -5
- package/assets/docs/cli-utilities/configuration.md +41 -0
- package/assets/docs/cli-utilities/workflow-gates.md +18 -1
- package/assets/docs/provider-sync/commands.md +29 -1
- package/assets/docs/provider-sync/providers.md +18 -5
- package/assets/docs/provider-sync/scope-and-surface.md +13 -0
- package/assets/docs/workflows/projects/dispatch-ceiling.md +59 -7
- package/assets/docs/workflows/projects/implementation-execution.md +63 -3
- package/assets/docs/workflows/projects/reviews.md +18 -1
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-agent-instructions-analyze/references/docs/provider-reference.md +8 -2
- package/assets/skills/oat-agent-instructions-apply/references/docs/provider-reference.md +8 -2
- package/assets/skills/oat-project-implement/SKILL.md +141 -59
- package/assets/skills/oat-project-review-provide/SKILL.md +44 -10
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +28 -1
- package/dist/commands/config/index.d.ts +3 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +70 -128
- package/dist/commands/doctor/index.d.ts +3 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +205 -195
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +72 -1
- package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
- package/dist/commands/project/dispatch-ceiling/index.js +224 -112
- package/dist/commands/providers/codex/materialize.d.ts.map +1 -1
- package/dist/commands/providers/codex/materialize.js +31 -15
- package/dist/config/dispatch-matrix.d.ts +59 -0
- package/dist/config/dispatch-matrix.d.ts.map +1 -0
- package/dist/config/dispatch-matrix.js +264 -0
- package/dist/config/oat-config.d.ts +2 -28
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +8 -144
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +2 -1
- package/dist/providers/codex/codec/config-merge.d.ts +8 -2
- package/dist/providers/codex/codec/config-merge.d.ts.map +1 -1
- package/dist/providers/codex/codec/config-merge.js +17 -2
- package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
- package/dist/providers/codex/codec/sync-extension.js +14 -4
- package/dist/providers/identity/availability.d.ts +12 -0
- package/dist/providers/identity/availability.d.ts.map +1 -1
- package/dist/providers/identity/availability.js +72 -29
- package/dist/providers/identity/dispatch-report.d.ts +124 -0
- package/dist/providers/identity/dispatch-report.d.ts.map +1 -0
- package/dist/providers/identity/dispatch-report.js +285 -0
- package/dist/providers/identity/dispatch-validation.d.ts +28 -0
- package/dist/providers/identity/dispatch-validation.d.ts.map +1 -0
- package/dist/providers/identity/dispatch-validation.js +149 -0
- package/dist/providers/identity/stamp.d.ts +2 -0
- package/dist/providers/identity/stamp.d.ts.map +1 -1
- package/dist/providers/identity/stamp.js +3 -1
- package/package.json +2 -2
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { access, mkdtemp, readdir, readFile, rm, symlink, } from 'node:fs/promises';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
4
|
import { buildCommandContext, } from '../../app/command-context.js';
|
|
5
5
|
import { compareVersions } from '../init/tools/shared/version.js';
|
|
6
6
|
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 {
|
|
10
|
+
import { walkDispatchMatrix, } from '../../config/dispatch-matrix.js';
|
|
11
|
+
import { readOatConfig, readOatLocalConfig, readUserConfig, } from '../../config/oat-config.js';
|
|
11
12
|
import { resolveAssetsRoot } from '../../fs/assets.js';
|
|
12
13
|
import { resolveProjectRoot, resolveScopeRoot } from '../../fs/paths.js';
|
|
13
14
|
import TOML from '@iarna/toml';
|
|
@@ -18,7 +19,8 @@ import { isOatManagedCodexRoleFile } from '../../providers/codex/codec/shared.js
|
|
|
18
19
|
import { copilotAdapter } from '../../providers/copilot/index.js';
|
|
19
20
|
import { cursorAdapter } from '../../providers/cursor/index.js';
|
|
20
21
|
import { geminiAdapter } from '../../providers/gemini/index.js';
|
|
21
|
-
import {
|
|
22
|
+
import { validateMatrixCell, } from '../../providers/identity/availability.js';
|
|
23
|
+
import { createDispatchValidationPassContext, validateDispatchMatrixRefs, } from '../../providers/identity/dispatch-validation.js';
|
|
22
24
|
import { formatDoctorResults } from '../../ui/output.js';
|
|
23
25
|
import { Command } from 'commander';
|
|
24
26
|
async function pathExistsDefault(path) {
|
|
@@ -134,6 +136,8 @@ function createDependencies() {
|
|
|
134
136
|
readOatLocalConfig,
|
|
135
137
|
readUserConfig,
|
|
136
138
|
validateMatrixCell,
|
|
139
|
+
createDispatchValidationPassContext,
|
|
140
|
+
validateDispatchMatrixRefs,
|
|
137
141
|
processEnv: process.env,
|
|
138
142
|
runPjmDoctorChecks,
|
|
139
143
|
// Default binding remains self-contained, but still honors the caller-
|
|
@@ -141,97 +145,46 @@ function createDependencies() {
|
|
|
141
145
|
checkSkillVersions: (scopeRoot, assetsRoot, pathExists = pathExistsDefault) => checkSkillVersionsDefault(scopeRoot, assetsRoot, pathExists),
|
|
142
146
|
};
|
|
143
147
|
}
|
|
144
|
-
function
|
|
145
|
-
return
|
|
148
|
+
function collectDispatchMatrixCellRefs(layers) {
|
|
149
|
+
return layers.flatMap(({ source, config }) => {
|
|
150
|
+
const providers = config.workflow?.dispatchCeiling?.providers ?? {};
|
|
151
|
+
return walkDispatchMatrix(providers, {
|
|
152
|
+
source,
|
|
153
|
+
pathPrefix: 'workflow.dispatchCeiling.providers',
|
|
154
|
+
});
|
|
155
|
+
});
|
|
146
156
|
}
|
|
147
|
-
function
|
|
148
|
-
|
|
157
|
+
function dispatchMatrixSourceLabel(source) {
|
|
158
|
+
switch (source) {
|
|
159
|
+
case 'user-config':
|
|
160
|
+
return 'user';
|
|
161
|
+
case 'repo-config':
|
|
162
|
+
return 'shared';
|
|
163
|
+
case 'local-config':
|
|
164
|
+
return 'local';
|
|
165
|
+
case 'project-state':
|
|
166
|
+
return 'project-state';
|
|
167
|
+
}
|
|
149
168
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (typeof entry === 'string') {
|
|
157
|
-
refs.push({ layer, provider, value: entry, path: entryPath });
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
if (!isRouteTarget(entry)) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const targetProvider = entry.harness ?? provider;
|
|
164
|
-
if (isCodexMaterializedRouteTarget(provider, entry)) {
|
|
165
|
-
if (entry.model && entry.effort) {
|
|
166
|
-
refs.push({
|
|
167
|
-
layer,
|
|
168
|
-
provider: targetProvider,
|
|
169
|
-
value: formatRouteTargetValue(entry),
|
|
170
|
-
path: entryPath,
|
|
171
|
-
target: entry,
|
|
172
|
-
});
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (entry.model) {
|
|
177
|
-
refs.push({
|
|
178
|
-
layer,
|
|
179
|
-
provider: targetProvider,
|
|
180
|
-
value: entry.model,
|
|
181
|
-
path: `${entryPath}.model`,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
if (entry.effort) {
|
|
185
|
-
refs.push({
|
|
186
|
-
layer,
|
|
187
|
-
provider: targetProvider,
|
|
188
|
-
value: entry.effort,
|
|
189
|
-
path: `${entryPath}.effort`,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
if (isWorkflowDispatchCandidateLadder(cell)) {
|
|
194
|
-
for (const [candidateIndex, candidate] of cell.candidates.entries()) {
|
|
195
|
-
const candidatePath = `${path}.candidates[${candidateIndex}]`;
|
|
196
|
-
if (isWorkflowDispatchFallbackRoute(candidate)) {
|
|
197
|
-
for (const [routeIndex, entry] of candidate.route.entries()) {
|
|
198
|
-
addEntry(entry, `${candidatePath}.route[${routeIndex}]`);
|
|
199
|
-
}
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
addEntry(candidate, candidatePath);
|
|
203
|
-
}
|
|
204
|
-
return;
|
|
169
|
+
function dispatchMatrixRefValue(ref) {
|
|
170
|
+
return ref.value ?? ref.target?.model ?? ref.target?.effort ?? 'unknown';
|
|
171
|
+
}
|
|
172
|
+
function dispatchMatrixAvailabilityRef(ref) {
|
|
173
|
+
if (ref.value !== null) {
|
|
174
|
+
return { provider: ref.provider, value: ref.value, target: null };
|
|
205
175
|
}
|
|
206
|
-
|
|
207
|
-
|
|
176
|
+
if (ref.target === null) {
|
|
177
|
+
return null;
|
|
208
178
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
for (const { layer, config } of layers) {
|
|
213
|
-
const providers = config.workflow?.dispatchCeiling?.providers ?? {};
|
|
214
|
-
for (const [provider, providerValue] of Object.entries(providers)) {
|
|
215
|
-
const providerPath = `workflow.dispatchCeiling.providers.${provider}`;
|
|
216
|
-
if (typeof providerValue === 'string') {
|
|
217
|
-
refs.push({
|
|
218
|
-
layer,
|
|
219
|
-
provider,
|
|
220
|
-
value: providerValue,
|
|
221
|
-
path: providerPath,
|
|
222
|
-
});
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
const tierMap = providerValue;
|
|
226
|
-
for (const [tier, cell] of Object.entries(tierMap)) {
|
|
227
|
-
if (cell === undefined) {
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
addDispatchMatrixCellRefs(refs, layer, provider, `${providerPath}.${tier}`, cell);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
179
|
+
const value = ref.target.model ?? ref.target.effort;
|
|
180
|
+
if (!value) {
|
|
181
|
+
return null;
|
|
233
182
|
}
|
|
234
|
-
return
|
|
183
|
+
return {
|
|
184
|
+
provider: ref.target.harness ?? ref.provider,
|
|
185
|
+
value,
|
|
186
|
+
target: ref.target,
|
|
187
|
+
};
|
|
235
188
|
}
|
|
236
189
|
function formatDispatchMatrixIssueList(issues) {
|
|
237
190
|
return issues
|
|
@@ -239,12 +192,12 @@ function formatDispatchMatrixIssueList(issues) {
|
|
|
239
192
|
const suffix = 'message' in issue && typeof issue.message === 'string'
|
|
240
193
|
? `; ${issue.message}`
|
|
241
194
|
: '';
|
|
242
|
-
return `${issue.path}=${issue
|
|
195
|
+
return `${issue.path}=${dispatchMatrixRefValue(issue)} (${dispatchMatrixSourceLabel(issue.source)} config)${suffix}`;
|
|
243
196
|
})
|
|
244
197
|
.join(', ');
|
|
245
198
|
}
|
|
246
199
|
async function createDispatchMatrixDoctorCheck(scopeRoot, layers, dependencies) {
|
|
247
|
-
const refs = collectDispatchMatrixCellRefs(layers);
|
|
200
|
+
const refs = collectDispatchMatrixCellRefs(layers).filter((ref) => dispatchMatrixAvailabilityRef(ref) !== null);
|
|
248
201
|
if (refs.length === 0) {
|
|
249
202
|
return {
|
|
250
203
|
name: 'project:dispatch_matrix',
|
|
@@ -253,25 +206,19 @@ async function createDispatchMatrixDoctorCheck(scopeRoot, layers, dependencies)
|
|
|
253
206
|
message: 'No configured dispatch matrix cells found in user, shared, or local config layers.',
|
|
254
207
|
};
|
|
255
208
|
}
|
|
209
|
+
const pass = dependencies.createDispatchValidationPassContext({
|
|
210
|
+
cwd: scopeRoot,
|
|
211
|
+
env: dependencies.processEnv,
|
|
212
|
+
validateMatrixCell: dependencies.validateMatrixCell,
|
|
213
|
+
});
|
|
214
|
+
const results = await dependencies.validateDispatchMatrixRefs(refs, pass);
|
|
256
215
|
const issues = [];
|
|
257
|
-
for (const
|
|
258
|
-
|
|
259
|
-
try {
|
|
260
|
-
result = normalizeMatrixCellAvailability(await dependencies.validateMatrixCell(ref.provider, ref.value, {
|
|
261
|
-
cwd: scopeRoot,
|
|
262
|
-
env: dependencies.processEnv,
|
|
263
|
-
detailed: true,
|
|
264
|
-
...(ref.target ? { target: ref.target } : {}),
|
|
265
|
-
}));
|
|
266
|
-
}
|
|
267
|
-
catch {
|
|
268
|
-
result = { availability: 'unvalidated' };
|
|
269
|
-
}
|
|
270
|
-
if (result.availability !== 'valid') {
|
|
216
|
+
for (const result of results) {
|
|
217
|
+
if (result.status !== 'valid') {
|
|
271
218
|
issues.push({
|
|
272
|
-
...ref,
|
|
273
|
-
availability: result.
|
|
274
|
-
...(result.
|
|
219
|
+
...result.ref,
|
|
220
|
+
availability: result.status,
|
|
221
|
+
...(result.diagnostic ? { message: result.diagnostic } : {}),
|
|
275
222
|
});
|
|
276
223
|
}
|
|
277
224
|
}
|
|
@@ -332,6 +279,152 @@ async function isManagedCodexRoleConfig(scopeRoot, roleName, configFile, depende
|
|
|
332
279
|
return false;
|
|
333
280
|
}
|
|
334
281
|
}
|
|
282
|
+
function getCodexAgents(config) {
|
|
283
|
+
const agents = config.agents;
|
|
284
|
+
return agents && typeof agents === 'object' && !Array.isArray(agents)
|
|
285
|
+
? agents
|
|
286
|
+
: {};
|
|
287
|
+
}
|
|
288
|
+
function getCodexMaxDepthEntry(config) {
|
|
289
|
+
const agents = getCodexAgents(config);
|
|
290
|
+
return {
|
|
291
|
+
present: Object.prototype.hasOwnProperty.call(agents, 'max_depth'),
|
|
292
|
+
value: agents.max_depth,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Applies Codex config precedence without letting an invalid project value fall
|
|
297
|
+
* through to the lower-precedence user value.
|
|
298
|
+
*/
|
|
299
|
+
async function resolveDoctorCodexMaxDepth(scope, parsedConfig, userHome, dependencies) {
|
|
300
|
+
const scopedEntry = getCodexMaxDepthEntry(parsedConfig);
|
|
301
|
+
if (scope !== 'project' || scopedEntry.present) {
|
|
302
|
+
return scopedEntry;
|
|
303
|
+
}
|
|
304
|
+
const userConfigPath = join(userHome, '.codex', 'config.toml');
|
|
305
|
+
if (!(await dependencies.pathExists(userConfigPath))) {
|
|
306
|
+
return scopedEntry;
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
return getCodexMaxDepthEntry(TOML.parse(await dependencies.readFile(userConfigPath)));
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
return { present: true, value: null };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function createCodexMaxDepthCheck(scope, entry) {
|
|
316
|
+
const topology = 'root (0) → phase coordinator (1) → task worker (2)';
|
|
317
|
+
const numericDepth = typeof entry.value === 'number' && Number.isFinite(entry.value)
|
|
318
|
+
? entry.value
|
|
319
|
+
: null;
|
|
320
|
+
const sufficient = numericDepth !== null && numericDepth >= 2;
|
|
321
|
+
let state;
|
|
322
|
+
if (!entry.present) {
|
|
323
|
+
state = 'agents.max_depth is missing';
|
|
324
|
+
}
|
|
325
|
+
else if (numericDepth === null) {
|
|
326
|
+
state = 'agents.max_depth is not a valid number';
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
state = `agents.max_depth is ${numericDepth}`;
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
name: `${scope}:codex_max_depth`,
|
|
333
|
+
description: 'Codex managed-role nested dispatch depth',
|
|
334
|
+
status: sufficient ? 'pass' : 'warn',
|
|
335
|
+
message: sufficient
|
|
336
|
+
? `${state}; sufficient for ${topology}.`
|
|
337
|
+
: `${state}; OAT managed implementation requires ${topology}.`,
|
|
338
|
+
fix: sufficient
|
|
339
|
+
? undefined
|
|
340
|
+
: `Run \`oat sync --scope ${scope}\`, or materialize a single role with \`oat providers codex materialize <agent-name> --model <model> --effort <effort> --scope ${scope}\`.`,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
async function runCodexChecksForScope(scope, scopeRoot, userHome, dependencies) {
|
|
344
|
+
const checks = [];
|
|
345
|
+
const codexConfigPath = join(scopeRoot, '.codex', 'config.toml');
|
|
346
|
+
if (!(await dependencies.pathExists(codexConfigPath))) {
|
|
347
|
+
return checks;
|
|
348
|
+
}
|
|
349
|
+
let parsedConfig;
|
|
350
|
+
try {
|
|
351
|
+
parsedConfig = TOML.parse(await dependencies.readFile(codexConfigPath));
|
|
352
|
+
checks.push({
|
|
353
|
+
name: `${scope}:codex_config_toml`,
|
|
354
|
+
description: 'Codex config TOML parseability',
|
|
355
|
+
status: 'pass',
|
|
356
|
+
message: '.codex/config.toml parsed successfully.',
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
checks.push({
|
|
361
|
+
name: `${scope}:codex_config_toml`,
|
|
362
|
+
description: 'Codex config TOML parseability',
|
|
363
|
+
status: 'fail',
|
|
364
|
+
message: error instanceof Error
|
|
365
|
+
? `Failed to parse .codex/config.toml: ${error.message}`
|
|
366
|
+
: 'Failed to parse .codex/config.toml.',
|
|
367
|
+
fix: 'Repair .codex/config.toml syntax and rerun doctor.',
|
|
368
|
+
});
|
|
369
|
+
return checks;
|
|
370
|
+
}
|
|
371
|
+
const features = parsedConfig.features &&
|
|
372
|
+
typeof parsedConfig.features === 'object' &&
|
|
373
|
+
!Array.isArray(parsedConfig.features)
|
|
374
|
+
? parsedConfig.features
|
|
375
|
+
: null;
|
|
376
|
+
const agents = getCodexAgents(parsedConfig);
|
|
377
|
+
const managedRoles = [];
|
|
378
|
+
const roleConfigFiles = new Map();
|
|
379
|
+
for (const [roleName, roleConfig] of Object.entries(agents)) {
|
|
380
|
+
const configFile = getCodexAgentConfigFile(roleConfig);
|
|
381
|
+
if (!configFile) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
roleConfigFiles.set(roleName, configFile);
|
|
385
|
+
if (await isManagedCodexRoleConfig(scopeRoot, roleName, configFile, dependencies)) {
|
|
386
|
+
managedRoles.push(roleName);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (managedRoles.length === 0) {
|
|
390
|
+
return checks;
|
|
391
|
+
}
|
|
392
|
+
const multiAgentEnabled = features?.multi_agent === true || features?.multi_agent === 'true';
|
|
393
|
+
checks.push({
|
|
394
|
+
name: `${scope}:codex_multi_agent`,
|
|
395
|
+
description: 'Codex multi-agent feature flag',
|
|
396
|
+
status: multiAgentEnabled ? 'pass' : 'warn',
|
|
397
|
+
message: multiAgentEnabled
|
|
398
|
+
? 'features.multi_agent is enabled for codex managed roles.'
|
|
399
|
+
: 'Codex managed roles detected but features.multi_agent is not true.',
|
|
400
|
+
fix: multiAgentEnabled
|
|
401
|
+
? undefined
|
|
402
|
+
: 'Set [features] multi_agent = true in .codex/config.toml.',
|
|
403
|
+
});
|
|
404
|
+
checks.push(createCodexMaxDepthCheck(scope, await resolveDoctorCodexMaxDepth(scope, parsedConfig, userHome, dependencies)));
|
|
405
|
+
const missingRoleFiles = [];
|
|
406
|
+
for (const roleName of managedRoles) {
|
|
407
|
+
const configFile = roleConfigFiles.get(roleName);
|
|
408
|
+
if (!configFile)
|
|
409
|
+
continue;
|
|
410
|
+
const absoluteRolePath = join(scopeRoot, '.codex', configFile);
|
|
411
|
+
if (!(await dependencies.pathExists(absoluteRolePath))) {
|
|
412
|
+
missingRoleFiles.push(configFile);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
checks.push({
|
|
416
|
+
name: `${scope}:codex_role_file_refs`,
|
|
417
|
+
description: 'Codex role config_file references',
|
|
418
|
+
status: missingRoleFiles.length === 0 ? 'pass' : 'warn',
|
|
419
|
+
message: missingRoleFiles.length === 0
|
|
420
|
+
? 'All codex role config_file references exist.'
|
|
421
|
+
: `Missing codex role files: ${missingRoleFiles.join(', ')}`,
|
|
422
|
+
fix: missingRoleFiles.length === 0
|
|
423
|
+
? undefined
|
|
424
|
+
: `Regenerate codex roles with \`oat sync --scope ${scope}\`, or materialize a single role with \`oat providers codex materialize <agent-name> --model <model> --effort <effort> --scope ${scope}\`.`,
|
|
425
|
+
});
|
|
426
|
+
return checks;
|
|
427
|
+
}
|
|
335
428
|
async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies) {
|
|
336
429
|
const checks = [];
|
|
337
430
|
const skillsPath = join(scopeRoot, '.agents', 'skills');
|
|
@@ -407,6 +500,7 @@ async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies)
|
|
|
407
500
|
? undefined
|
|
408
501
|
: 'Install or enable a provider directory (e.g. .claude, .cursor, .codex).',
|
|
409
502
|
});
|
|
503
|
+
checks.push(...(await runCodexChecksForScope(scope, scopeRoot, dirname(userConfigDir), dependencies)));
|
|
410
504
|
if (scope === 'project') {
|
|
411
505
|
try {
|
|
412
506
|
const assetsRoot = await dependencies.resolveAssetsRoot();
|
|
@@ -456,90 +550,6 @@ async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies)
|
|
|
456
550
|
fix: 'Run `pnpm build` and rerun `oat doctor`.',
|
|
457
551
|
});
|
|
458
552
|
}
|
|
459
|
-
const codexConfigPath = join(scopeRoot, '.codex', 'config.toml');
|
|
460
|
-
const codexConfigExists = await dependencies.pathExists(codexConfigPath);
|
|
461
|
-
if (codexConfigExists) {
|
|
462
|
-
let parsedConfig = null;
|
|
463
|
-
try {
|
|
464
|
-
parsedConfig = TOML.parse(await dependencies.readFile(codexConfigPath));
|
|
465
|
-
checks.push({
|
|
466
|
-
name: `${scope}:codex_config_toml`,
|
|
467
|
-
description: 'Codex config TOML parseability',
|
|
468
|
-
status: 'pass',
|
|
469
|
-
message: '.codex/config.toml parsed successfully.',
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
catch (error) {
|
|
473
|
-
checks.push({
|
|
474
|
-
name: `${scope}:codex_config_toml`,
|
|
475
|
-
description: 'Codex config TOML parseability',
|
|
476
|
-
status: 'fail',
|
|
477
|
-
message: error instanceof Error
|
|
478
|
-
? `Failed to parse .codex/config.toml: ${error.message}`
|
|
479
|
-
: 'Failed to parse .codex/config.toml.',
|
|
480
|
-
fix: 'Repair .codex/config.toml syntax and rerun doctor.',
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
if (parsedConfig) {
|
|
484
|
-
const features = parsedConfig.features &&
|
|
485
|
-
typeof parsedConfig.features === 'object' &&
|
|
486
|
-
!Array.isArray(parsedConfig.features)
|
|
487
|
-
? parsedConfig.features
|
|
488
|
-
: null;
|
|
489
|
-
const agents = parsedConfig.agents &&
|
|
490
|
-
typeof parsedConfig.agents === 'object' &&
|
|
491
|
-
!Array.isArray(parsedConfig.agents)
|
|
492
|
-
? parsedConfig.agents
|
|
493
|
-
: null;
|
|
494
|
-
const managedRoles = [];
|
|
495
|
-
const roleConfigFiles = new Map();
|
|
496
|
-
for (const [roleName, roleConfig] of Object.entries(agents ?? {})) {
|
|
497
|
-
const configFile = getCodexAgentConfigFile(roleConfig);
|
|
498
|
-
if (!configFile) {
|
|
499
|
-
continue;
|
|
500
|
-
}
|
|
501
|
-
roleConfigFiles.set(roleName, configFile);
|
|
502
|
-
if (await isManagedCodexRoleConfig(scopeRoot, roleName, configFile, dependencies)) {
|
|
503
|
-
managedRoles.push(roleName);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
if (managedRoles.length > 0) {
|
|
507
|
-
const multiAgentEnabled = features?.multi_agent === true || features?.multi_agent === 'true';
|
|
508
|
-
checks.push({
|
|
509
|
-
name: `${scope}:codex_multi_agent`,
|
|
510
|
-
description: 'Codex multi-agent feature flag',
|
|
511
|
-
status: multiAgentEnabled ? 'pass' : 'warn',
|
|
512
|
-
message: multiAgentEnabled
|
|
513
|
-
? 'features.multi_agent is enabled for codex managed roles.'
|
|
514
|
-
: 'Codex managed roles detected but features.multi_agent is not true.',
|
|
515
|
-
fix: multiAgentEnabled
|
|
516
|
-
? undefined
|
|
517
|
-
: 'Set [features] multi_agent = true in .codex/config.toml.',
|
|
518
|
-
});
|
|
519
|
-
const missingRoleFiles = [];
|
|
520
|
-
for (const roleName of managedRoles) {
|
|
521
|
-
const configFile = roleConfigFiles.get(roleName);
|
|
522
|
-
if (!configFile)
|
|
523
|
-
continue;
|
|
524
|
-
const absoluteRolePath = join(scopeRoot, '.codex', configFile);
|
|
525
|
-
if (!(await dependencies.pathExists(absoluteRolePath))) {
|
|
526
|
-
missingRoleFiles.push(configFile);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
checks.push({
|
|
530
|
-
name: `${scope}:codex_role_file_refs`,
|
|
531
|
-
description: 'Codex role config_file references',
|
|
532
|
-
status: missingRoleFiles.length === 0 ? 'pass' : 'warn',
|
|
533
|
-
message: missingRoleFiles.length === 0
|
|
534
|
-
? 'All codex role config_file references exist.'
|
|
535
|
-
: `Missing codex role files: ${missingRoleFiles.join(', ')}`,
|
|
536
|
-
fix: missingRoleFiles.length === 0
|
|
537
|
-
? undefined
|
|
538
|
-
: 'Regenerate codex roles with `oat sync --scope project`, or materialize a single role with `oat providers codex materialize ...`.',
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
553
|
const repoReferenceRoot = join(scopeRoot, '.oat', 'repo');
|
|
544
554
|
const [userConfig, config, localConfig] = await Promise.all([
|
|
545
555
|
dependencies.readUserConfig(userConfigDir),
|
|
@@ -547,9 +557,9 @@ async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies)
|
|
|
547
557
|
dependencies.readOatLocalConfig(scopeRoot),
|
|
548
558
|
]);
|
|
549
559
|
checks.push(await createDispatchMatrixDoctorCheck(scopeRoot, [
|
|
550
|
-
{
|
|
551
|
-
{
|
|
552
|
-
{
|
|
560
|
+
{ source: 'user-config', config: userConfig },
|
|
561
|
+
{ source: 'repo-config', config },
|
|
562
|
+
{ source: 'local-config', config: localConfig },
|
|
553
563
|
], dependencies));
|
|
554
564
|
const projectManagementEnabled = config.tools?.['project-management'] === true;
|
|
555
565
|
if (projectManagementEnabled) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/gate/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,EAQL,KAAK,UAAU,EAIf,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,UAAU,EAEhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/gate/index.ts"],"names":[],"mappings":"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"}
|