@open-agent-toolkit/cli 0.1.46 → 0.1.48

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 (82) hide show
  1. package/assets/agents/oat-phase-implementer.md +202 -191
  2. package/assets/agents/oat-reviewer.md +11 -1
  3. package/assets/config/dispatch-matrix-recommendation.json +120 -13
  4. package/assets/docs/cli-utilities/configuration.md +172 -113
  5. package/assets/docs/cli-utilities/workflow-gates.md +161 -27
  6. package/assets/docs/contributing/skills.md +14 -8
  7. package/assets/docs/provider-sync/providers.md +41 -7
  8. package/assets/docs/provider-sync/scope-and-surface.md +2 -2
  9. package/assets/docs/reference/cli-reference.md +3 -1
  10. package/assets/docs/reference/oat-directory-structure.md +27 -26
  11. package/assets/docs/workflows/projects/artifacts.md +31 -1
  12. package/assets/docs/workflows/projects/dispatch-ceiling.md +235 -187
  13. package/assets/docs/workflows/projects/implementation-execution.md +283 -260
  14. package/assets/docs/workflows/projects/lifecycle.md +26 -5
  15. package/assets/docs/workflows/projects/reviews.md +27 -2
  16. package/assets/migration/pjm-restructure.md +1 -1
  17. package/assets/public-package-versions.json +4 -4
  18. package/assets/skills/oat-project-implement/SKILL.md +247 -133
  19. package/assets/skills/oat-project-import-plan/SKILL.md +173 -16
  20. package/assets/skills/oat-project-next/SKILL.md +2 -2
  21. package/assets/skills/oat-project-plan/SKILL.md +122 -92
  22. package/assets/skills/oat-project-plan-writing/SKILL.md +246 -15
  23. package/assets/skills/oat-project-quick-start/SKILL.md +157 -107
  24. package/assets/skills/oat-project-review-provide/SKILL.md +94 -22
  25. package/dist/commands/config/index.d.ts.map +1 -1
  26. package/dist/commands/config/index.js +64 -39
  27. package/dist/commands/doctor/index.d.ts.map +1 -1
  28. package/dist/commands/doctor/index.js +21 -6
  29. package/dist/commands/gate/index.d.ts +7 -1
  30. package/dist/commands/gate/index.d.ts.map +1 -1
  31. package/dist/commands/gate/index.js +544 -67
  32. package/dist/commands/gate/review-verdict.d.ts +16 -0
  33. package/dist/commands/gate/review-verdict.d.ts.map +1 -1
  34. package/dist/commands/gate/review-verdict.js +72 -9
  35. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  36. package/dist/commands/project/dispatch-ceiling/index.js +452 -16
  37. package/dist/commands/providers/codex/materialize.d.ts.map +1 -1
  38. package/dist/commands/providers/codex/materialize.js +6 -1
  39. package/dist/commands/shared/frontmatter.d.ts +4 -0
  40. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  41. package/dist/commands/shared/frontmatter.js +23 -0
  42. package/dist/commands/status/index.d.ts +1 -0
  43. package/dist/commands/status/index.d.ts.map +1 -1
  44. package/dist/commands/status/index.js +10 -4
  45. package/dist/commands/sync/index.d.ts.map +1 -1
  46. package/dist/commands/sync/index.js +10 -3
  47. package/dist/commands/sync/sync.types.d.ts +1 -0
  48. package/dist/commands/sync/sync.types.d.ts.map +1 -1
  49. package/dist/config/oat-config.d.ts +19 -2
  50. package/dist/config/oat-config.d.ts.map +1 -1
  51. package/dist/config/oat-config.js +139 -24
  52. package/dist/config/resolve.d.ts +8 -0
  53. package/dist/config/resolve.d.ts.map +1 -1
  54. package/dist/config/resolve.js +66 -2
  55. package/dist/engine/index.d.ts +1 -1
  56. package/dist/engine/index.d.ts.map +1 -1
  57. package/dist/engine/index.js +1 -1
  58. package/dist/engine/scanner.d.ts +1 -0
  59. package/dist/engine/scanner.d.ts.map +1 -1
  60. package/dist/engine/scanner.js +17 -1
  61. package/dist/fs/paths.d.ts +4 -0
  62. package/dist/fs/paths.d.ts.map +1 -1
  63. package/dist/fs/paths.js +18 -1
  64. package/dist/providers/ceiling/registry.d.ts +1 -0
  65. package/dist/providers/ceiling/registry.d.ts.map +1 -1
  66. package/dist/providers/ceiling/registry.js +12 -5
  67. package/dist/providers/codex/codec/catalog.d.ts +14 -0
  68. package/dist/providers/codex/codec/catalog.d.ts.map +1 -0
  69. package/dist/providers/codex/codec/catalog.js +21 -0
  70. package/dist/providers/codex/codec/materialize.d.ts.map +1 -1
  71. package/dist/providers/codex/codec/materialize.js +6 -5
  72. package/dist/providers/codex/codec/shared.d.ts +19 -0
  73. package/dist/providers/codex/codec/shared.d.ts.map +1 -1
  74. package/dist/providers/codex/codec/shared.js +98 -5
  75. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  76. package/dist/providers/codex/codec/sync-extension.js +142 -32
  77. package/dist/providers/identity/stamp.d.ts.map +1 -1
  78. package/dist/providers/identity/stamp.js +4 -0
  79. package/dist/shared/types.d.ts +1 -0
  80. package/dist/shared/types.d.ts.map +1 -1
  81. package/dist/shared/types.js +4 -0
  82. package/package.json +2 -2
@@ -2,19 +2,19 @@ import { createHash } from 'node:crypto';
2
2
  import { readdir, readFile, rm, writeFile } from 'node:fs/promises';
3
3
  import { basename, dirname, join, relative, resolve } from 'node:path';
4
4
  import { parseCanonicalAgentFile } from '../../../agents/canonical/index.js';
5
- import { isCodexMaterializedRouteTarget, resolveActiveProject, validateDispatchRouteTarget, } from '../../../config/oat-config.js';
5
+ import { isCodexMaterializedRouteTarget, isWorkflowDispatchCandidateLadder, isWorkflowDispatchFallbackRoute, normalizeProjectPath, resolveActiveProject, validateDispatchRouteTarget, } from '../../../config/oat-config.js';
6
6
  import { resolveEffectiveConfig } from '../../../config/resolve.js';
7
+ import { CliError } from '../../../errors/index.js';
7
8
  import { ensureDir, fileExists } from '../../../fs/io.js';
9
+ import { validateRealPathWithinScope } from '../../../fs/paths.js';
8
10
  import TOML from '@iarna/toml';
9
11
  import YAML from 'yaml';
12
+ import { SUPPORTED_CODEX_BASE_ROLES, SUPPORTED_CODEX_ROLE_TARGETS, } from './catalog.js';
10
13
  import { mergeCodexConfig } from './config-merge.js';
11
14
  import { exportCanonicalAgentToCodexRole } from './export-to-codex.js';
12
15
  import { materializeCodexRole } from './materialize.js';
13
- import { isOatManagedCodexRoleFile } from './shared.js';
14
- const CODEX_MATERIALIZED_BASE_ROLES = new Set([
15
- 'oat-phase-implementer',
16
- 'oat-reviewer',
17
- ]);
16
+ import { isOatManagedCodexRoleFile, readOatManagedCodexRoleOwner, withOatManagedCodexRoleOwner, } from './shared.js';
17
+ const CODEX_MATERIALIZED_BASE_ROLES = new Set(SUPPORTED_CODEX_BASE_ROLES);
18
18
  function hashContent(content) {
19
19
  return createHash('sha256').update(content).digest('hex');
20
20
  }
@@ -74,14 +74,22 @@ async function desiredRolesFromCanonical(canonicalEntries, scopeRoot, materializ
74
74
  description: materialized.description,
75
75
  configFile: materialized.configFile,
76
76
  rolePath: join(scopeRoot, '.codex', materialized.configFile),
77
- content: materialized.content,
77
+ content: withOatManagedCodexRoleOwner(materialized.content, target.owner),
78
78
  });
79
79
  }
80
80
  }
81
81
  }
82
+ const roleContents = new Map();
83
+ for (const role of roles) {
84
+ const existing = roleContents.get(role.roleName);
85
+ if (existing !== undefined && existing !== role.content) {
86
+ throw new CliError(`Distinct Codex targets produced the same role name ${role.roleName}. Refusing ambiguous role writes.`);
87
+ }
88
+ roleContents.set(role.roleName, role.content);
89
+ }
82
90
  return roles.sort((left, right) => left.roleName.localeCompare(right.roleName));
83
91
  }
84
- function collectCodexTargetFromEntry(entry, targets) {
92
+ function collectCodexTargetFromEntry(entry, targets, owner) {
85
93
  if (typeof entry === 'string') {
86
94
  return;
87
95
  }
@@ -92,26 +100,42 @@ function collectCodexTargetFromEntry(entry, targets) {
92
100
  if (!validation.valid || !entry.model || !entry.effort) {
93
101
  return;
94
102
  }
95
- targets.set(`${entry.model}\0${entry.effort}`, {
96
- model: entry.model,
97
- effort: entry.effort,
98
- });
103
+ const key = `${entry.model}\0${entry.effort}`;
104
+ if (!targets.has(key)) {
105
+ targets.set(key, {
106
+ model: entry.model,
107
+ effort: entry.effort,
108
+ owner,
109
+ });
110
+ }
99
111
  }
100
- function collectCodexTargetsFromCell(cell, targets) {
112
+ function collectCodexTargetsFromCell(cell, targets, owner) {
101
113
  if (typeof cell === 'string') {
102
114
  return;
103
115
  }
116
+ if (isWorkflowDispatchCandidateLadder(cell)) {
117
+ for (const candidate of cell.candidates) {
118
+ if (isWorkflowDispatchFallbackRoute(candidate)) {
119
+ for (const entry of candidate.route) {
120
+ collectCodexTargetFromEntry(entry, targets, owner);
121
+ }
122
+ continue;
123
+ }
124
+ collectCodexTargetFromEntry(candidate, targets, owner);
125
+ }
126
+ return;
127
+ }
104
128
  for (const entry of cell) {
105
- collectCodexTargetFromEntry(entry, targets);
129
+ collectCodexTargetFromEntry(entry, targets, owner);
106
130
  }
107
131
  }
108
- function collectCodexMaterializationTargetsFromProvider(providerValue, targets) {
132
+ function collectCodexMaterializationTargetsFromProvider(providerValue, targets, owner) {
109
133
  if (providerValue === undefined || typeof providerValue === 'string') {
110
134
  return;
111
135
  }
112
136
  for (const cell of Object.values(providerValue)) {
113
137
  if (cell !== undefined) {
114
- collectCodexTargetsFromCell(cell, targets);
138
+ collectCodexTargetsFromCell(cell, targets, owner);
115
139
  }
116
140
  }
117
141
  }
@@ -137,26 +161,54 @@ function routeTargetFromUnknown(value) {
137
161
  }
138
162
  return Object.keys(target).length > 0 ? target : null;
139
163
  }
140
- function collectCodexTargetsFromUnknownCell(value, targets) {
141
- if (typeof value === 'string' || !Array.isArray(value)) {
164
+ function collectCodexTargetsFromUnknownCell(value, targets, owner) {
165
+ const collectCandidate = (candidate) => {
166
+ if (!candidate ||
167
+ typeof candidate !== 'object' ||
168
+ Array.isArray(candidate)) {
169
+ return;
170
+ }
171
+ const route = candidate['route'];
172
+ if (Array.isArray(route)) {
173
+ for (const entry of route) {
174
+ const target = routeTargetFromUnknown(entry);
175
+ if (target) {
176
+ collectCodexTargetFromEntry(target, targets, owner);
177
+ }
178
+ }
179
+ return;
180
+ }
181
+ const target = routeTargetFromUnknown(candidate);
182
+ if (target) {
183
+ collectCodexTargetFromEntry(target, targets, owner);
184
+ }
185
+ };
186
+ if (Array.isArray(value)) {
187
+ for (const entry of value) {
188
+ collectCandidate(entry);
189
+ }
190
+ return;
191
+ }
192
+ if (!value || typeof value !== 'object') {
193
+ return;
194
+ }
195
+ const candidates = value['candidates'];
196
+ if (!Array.isArray(candidates)) {
142
197
  return;
143
198
  }
144
- for (const entry of value) {
199
+ for (const entry of candidates) {
145
200
  if (typeof entry === 'string') {
146
201
  continue;
147
202
  }
148
- const target = routeTargetFromUnknown(entry);
149
- if (target) {
150
- collectCodexTargetFromEntry(target, targets);
151
- }
203
+ collectCandidate(entry);
152
204
  }
153
205
  }
154
- function collectCodexTargetsFromUnknownProvider(value, targets) {
206
+ function collectCodexTargetsFromUnknownProvider(value, targets, owner) {
155
207
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
156
208
  return;
157
209
  }
158
210
  for (const cell of Object.values(value)) {
159
- collectCodexTargetsFromUnknownCell(cell, targets);
211
+ collectCodexTargetsFromUnknownCell(cell, targets, owner);
160
212
  }
161
213
  }
162
214
  function frontmatterBlock(content) {
@@ -168,13 +220,31 @@ function frontmatterBlock(content) {
168
220
  return end > 0 ? normalized.slice(4, end) : null;
169
221
  }
170
222
  async function collectProjectStateCodexTargets(scopeRoot, options, targets) {
171
- const projectPath = options.projectPath === undefined
223
+ const projectPathCandidate = options.projectPath === undefined
172
224
  ? (await resolveActiveProject(scopeRoot)).path
173
225
  : options.projectPath;
226
+ if (!projectPathCandidate) {
227
+ return;
228
+ }
229
+ const projectPath = normalizeProjectPath(scopeRoot, projectPathCandidate);
174
230
  if (!projectPath) {
231
+ if (options.projectPath !== undefined) {
232
+ throw new CliError(`Project-scoped Codex materialization path must be repo-relative or inside repo root: ${projectPathCandidate}`);
233
+ }
234
+ return;
235
+ }
236
+ let realProjectPath;
237
+ try {
238
+ const validated = await validateRealPathWithinScope(join(scopeRoot, projectPath), scopeRoot);
239
+ realProjectPath = validated.realPath;
240
+ }
241
+ catch {
242
+ if (options.projectPath !== undefined) {
243
+ throw new CliError(`Project-scoped Codex materialization path must be repo-relative or inside repo root: ${projectPathCandidate}`);
244
+ }
175
245
  return;
176
246
  }
177
- const statePath = join(resolve(scopeRoot, projectPath), 'state.md');
247
+ const statePath = join(realProjectPath, 'state.md');
178
248
  const stateContent = await readOptionalFile(statePath);
179
249
  if (!stateContent) {
180
250
  return;
@@ -195,17 +265,32 @@ async function collectProjectStateCodexTargets(scopeRoot, options, targets) {
195
265
  if (!matrix || typeof matrix !== 'object' || Array.isArray(matrix)) {
196
266
  return;
197
267
  }
198
- collectCodexTargetsFromUnknownProvider(matrix['codex'], targets);
268
+ collectCodexTargetsFromUnknownProvider(matrix['codex'], targets, 'project-config');
269
+ }
270
+ function isUserCodexScope(scopeRoot, options) {
271
+ if (!options.userConfigDir) {
272
+ return false;
273
+ }
274
+ return resolve(scopeRoot) === resolve(options.userConfigDir, '..');
199
275
  }
200
276
  async function readCodexMaterializationTargets(scopeRoot, options = {}) {
201
277
  const targets = new Map();
202
278
  const effectiveConfig = await resolveEffectiveConfig(scopeRoot, options.userConfigDir ?? join(scopeRoot, '.oat', '__no-user-config__'), options.env);
279
+ if (isUserCodexScope(scopeRoot, options)) {
280
+ collectCodexMaterializationTargetsFromProvider(effectiveConfig.user.workflow?.dispatchCeiling?.providers?.codex, targets, 'user-config');
281
+ return sortCodexMaterializationTargets(targets);
282
+ }
283
+ for (const target of SUPPORTED_CODEX_ROLE_TARGETS) {
284
+ targets.set(`${target.model}\0${target.effort}`, {
285
+ ...target,
286
+ owner: 'supported-catalogue',
287
+ });
288
+ }
203
289
  for (const providerValue of [
204
- effectiveConfig.user.workflow?.dispatchCeiling?.providers?.codex,
205
290
  effectiveConfig.shared.workflow?.dispatchCeiling?.providers?.codex,
206
291
  effectiveConfig.local.workflow?.dispatchCeiling?.providers?.codex,
207
292
  ]) {
208
- collectCodexMaterializationTargetsFromProvider(providerValue, targets);
293
+ collectCodexMaterializationTargetsFromProvider(providerValue, targets, 'project-config');
209
294
  }
210
295
  await collectProjectStateCodexTargets(scopeRoot, options, targets);
211
296
  return sortCodexMaterializationTargets(targets);
@@ -235,7 +320,7 @@ function parseConfigAgentTable(content) {
235
320
  return {};
236
321
  }
237
322
  }
238
- async function collectStaleManagedRoles(scopeRoot, existingConfigContent, desiredRoleNames) {
323
+ async function collectStaleManagedRoles(scopeRoot, existingConfigContent, desiredRoleNames, cleanupOwner, removeLegacyProjectRoles) {
239
324
  const agents = parseConfigAgentTable(existingConfigContent);
240
325
  const stale = new Set();
241
326
  for (const [roleName, roleConfig] of Object.entries(agents)) {
@@ -251,6 +336,13 @@ async function collectStaleManagedRoles(scopeRoot, existingConfigContent, desire
251
336
  if (!roleContent || !isOatManagedCodexRoleFile(roleContent, roleName)) {
252
337
  continue;
253
338
  }
339
+ const owner = readOatManagedCodexRoleOwner(roleContent);
340
+ const legacyProjectRole = removeLegacyProjectRoles &&
341
+ owner === null &&
342
+ SUPPORTED_CODEX_BASE_ROLES.some((baseRole) => roleName.startsWith(`${baseRole}-`));
343
+ if (owner !== cleanupOwner && !legacyProjectRole) {
344
+ continue;
345
+ }
254
346
  stale.add(roleName);
255
347
  }
256
348
  const agentsDir = join(scopeRoot, '.codex', 'agents');
@@ -274,6 +366,13 @@ async function collectStaleManagedRoles(scopeRoot, existingConfigContent, desire
274
366
  if (!roleContent || !isOatManagedCodexRoleFile(roleContent, roleName)) {
275
367
  continue;
276
368
  }
369
+ const owner = readOatManagedCodexRoleOwner(roleContent);
370
+ const legacyProjectRole = removeLegacyProjectRoles &&
371
+ owner === null &&
372
+ SUPPORTED_CODEX_BASE_ROLES.some((baseRole) => roleName.startsWith(`${baseRole}-`));
373
+ if (owner !== cleanupOwner && !legacyProjectRole) {
374
+ continue;
375
+ }
277
376
  stale.add(roleName);
278
377
  }
279
378
  return [...stale].sort((left, right) => left.localeCompare(right));
@@ -281,13 +380,24 @@ async function collectStaleManagedRoles(scopeRoot, existingConfigContent, desire
281
380
  export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntries, allowedCanonicalPaths, options = {}) {
282
381
  const isPartialSync = allowedCanonicalPaths !== undefined && allowedCanonicalPaths.length > 0;
283
382
  const materializationTargets = await readCodexMaterializationTargets(scopeRoot, options);
383
+ if (!isPartialSync &&
384
+ isUserCodexScope(scopeRoot, options) &&
385
+ materializationTargets.length > 0) {
386
+ const canonicalBaseRoles = new Set(canonicalEntries
387
+ .filter((entry) => entry.type === 'agent' && entry.isFile)
388
+ .map((entry) => entry.name.replace(/\.md$/i, '')));
389
+ const missingBaseRoles = SUPPORTED_CODEX_BASE_ROLES.filter((role) => !canonicalBaseRoles.has(role));
390
+ if (missingBaseRoles.length > 0) {
391
+ throw new CliError(`Bundled managed Codex role definitions are unavailable for user sync: ${missingBaseRoles.join(', ')}. Refusing stale user-role cleanup.`);
392
+ }
393
+ }
284
394
  const desiredRoles = await desiredRolesFromCanonical(canonicalEntries.filter((entry) => canonicalPathAllowed(scopeRoot, entry, allowedCanonicalPaths)), scopeRoot, materializationTargets);
285
395
  const desiredRoleNames = new Set(desiredRoles.map((role) => role.roleName));
286
396
  const existingConfigPath = configPath(scopeRoot);
287
397
  const existingConfigContent = await readOptionalFile(existingConfigPath);
288
398
  const staleRoles = isPartialSync
289
399
  ? []
290
- : await collectStaleManagedRoles(scopeRoot, existingConfigContent, desiredRoleNames);
400
+ : await collectStaleManagedRoles(scopeRoot, existingConfigContent, desiredRoleNames, isUserCodexScope(scopeRoot, options) ? 'user-config' : 'project-config', !isUserCodexScope(scopeRoot, options));
291
401
  if (isPartialSync && desiredRoles.length === 0) {
292
402
  return {
293
403
  operations: [],
@@ -1 +1 @@
1
- {"version":3,"file":"stamp.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/stamp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,KAAK,GAAG,QAAQ,CAAC;AACjE,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,KAAK,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAc,SAAQ,mBAAmB;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAyDD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAiBvE;AAqGD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,0BAA+B,GACvC,aAAa,EAAE,CAqBjB;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,0BAA+B,GACvC,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAepC"}
1
+ {"version":3,"file":"stamp.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/stamp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,KAAK,GAAG,QAAQ,CAAC;AACjE,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,KAAK,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAc,SAAQ,mBAAmB;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAyDD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAiBvE;AA6GD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,0BAA+B,GACvC,aAAa,EAAE,CAqBjB;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,0BAA+B,GACvC,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAepC"}
@@ -90,6 +90,10 @@ function parseModernStamp(line, lineNumber, fields, options) {
90
90
  warn(options, lineNumber, 'missing or invalid role');
91
91
  return undefined;
92
92
  }
93
+ if (role !== inferRole(action)) {
94
+ warn(options, lineNumber, `incompatible action/role pair ${action}/${role}`);
95
+ return undefined;
96
+ }
93
97
  const scope = singleToken(fields.get('scope'), '');
94
98
  if (!scope) {
95
99
  warn(options, lineNumber, 'missing scope');
@@ -7,4 +7,5 @@ export declare const ScopeSchema: z.ZodEnum<["project", "user", "all"]>;
7
7
  export type Scope = z.infer<typeof ScopeSchema>;
8
8
  export type ConcreteScope = Exclude<Scope, 'all'>;
9
9
  export declare const SCOPE_CONTENT_TYPES: Record<Scope, ContentType[]>;
10
+ export declare const USER_SCOPE_MANAGED_AGENT_FILES: readonly ["oat-phase-implementer.md", "oat-reviewer.md"];
10
11
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB,uCAAqC,CAAC;AACpE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,kBAAkB,wCAAsC,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,WAAW,uCAAqC,CAAC;AAC9D,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAChD,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAQlD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,CAI5D,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB,uCAAqC,CAAC;AACpE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,kBAAkB,wCAAsC,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,WAAW,uCAAqC,CAAC;AAC9D,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAChD,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAQlD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,CAI5D,CAAC;AAEF,eAAO,MAAM,8BAA8B,0DAGjC,CAAC"}
@@ -12,3 +12,7 @@ export const SCOPE_CONTENT_TYPES = {
12
12
  user: USER_SCOPE_CONTENT_TYPES,
13
13
  all: ALL_SCOPE_CONTENT_TYPES,
14
14
  };
15
+ export const USER_SCOPE_MANAGED_AGENT_FILES = [
16
+ 'oat-phase-implementer.md',
17
+ 'oat-reviewer.md',
18
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/cli",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "private": false,
5
5
  "description": "Open Agent Toolkit CLI",
6
6
  "homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
@@ -34,7 +34,7 @@
34
34
  "ora": "^9.0.0",
35
35
  "yaml": "2.8.2",
36
36
  "zod": "^3.25.76",
37
- "@open-agent-toolkit/control-plane": "0.1.46"
37
+ "@open-agent-toolkit/control-plane": "0.1.48"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^22.10.0",