@open-agent-toolkit/cli 0.1.39 → 0.1.41
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/docs/cli-utilities/backlog-lifecycle.md +61 -0
- package/assets/docs/cli-utilities/config-and-local-state.md +47 -0
- package/assets/docs/cli-utilities/configuration.md +2 -0
- package/assets/docs/cli-utilities/index.md +1 -0
- package/assets/docs/provider-sync/instruction-sync.md +1 -0
- package/assets/docs/workflows/projects/dispatch-ceiling.md +14 -3
- package/assets/docs/workflows/projects/implementation-execution.md +16 -2
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-pjm-review-backlog/SKILL.md +29 -7
- package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +25 -5
- package/assets/skills/oat-project-implement/SKILL.md +41 -10
- package/assets/templates/pjm-agents.md +80 -1
- package/assets/templates/pjm-handoffs-readme.md +21 -0
- package/assets/templates/reference-agents.md +13 -0
- package/assets/templates/repo-agents.md +4 -0
- package/assets/templates/repo-readme.md +35 -0
- package/dist/commands/backlog/archive.d.ts +33 -0
- package/dist/commands/backlog/archive.d.ts.map +1 -0
- package/dist/commands/backlog/archive.js +229 -0
- package/dist/commands/backlog/index.d.ts +2 -0
- package/dist/commands/backlog/index.d.ts.map +1 -1
- package/dist/commands/backlog/index.js +56 -2
- package/dist/commands/backlog/regenerate-index.d.ts +5 -1
- package/dist/commands/backlog/regenerate-index.d.ts.map +1 -1
- package/dist/commands/backlog/regenerate-index.js +7 -1
- package/dist/commands/backlog/shared/item-status.d.ts +13 -0
- package/dist/commands/backlog/shared/item-status.d.ts.map +1 -0
- package/dist/commands/backlog/shared/item-status.js +34 -0
- package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
- package/dist/commands/instructions/instructions.utils.js +24 -0
- package/dist/commands/pjm/doctor.d.ts.map +1 -1
- package/dist/commands/pjm/doctor.js +161 -0
- package/dist/commands/pjm/index.d.ts.map +1 -1
- package/dist/commands/pjm/index.js +2 -1
- package/dist/commands/pjm/init.d.ts +2 -1
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +7 -0
- package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
- package/dist/commands/project/dispatch-ceiling/index.js +80 -4
- package/package.json +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access, readdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { BACKLOG_ITEM_STATUSES, extractBacklogStatus, isTerminalBacklogStatus, isValidBacklogStatus, } from '../backlog/shared/item-status.js';
|
|
3
4
|
import { getFrontmatterBlock } from '../shared/frontmatter.js';
|
|
4
5
|
import { CANONICAL_REPO_REFERENCE_PATHS } from './init.js';
|
|
5
6
|
const ALLOWED_TOP_LEVEL_DIRECTORIES = new Set([
|
|
@@ -108,6 +109,45 @@ function containsTemplateFrontmatter(content) {
|
|
|
108
109
|
(/\boat_template\s*:/i.test(frontmatter) ||
|
|
109
110
|
/\boat_template_name\s*:/i.test(frontmatter)));
|
|
110
111
|
}
|
|
112
|
+
const BACKLOG_ITEMS_DIRECTORY = 'pjm/backlog/items';
|
|
113
|
+
const BACKLOG_ARCHIVED_DIRECTORY = 'pjm/backlog/archived';
|
|
114
|
+
const BACKLOG_COMPLETED_FILE = 'pjm/backlog/completed.md';
|
|
115
|
+
// Best-effort backlog id scan: `BL-YYMMDD-slug` (legacy lowercase `bl-`
|
|
116
|
+
// matched case-insensitively). The six-digit requirement keeps the literal
|
|
117
|
+
// `BL-YYMMDD-slug` format placeholder in `completed.md` from matching.
|
|
118
|
+
const BACKLOG_ID_PATTERN = /\bbl-\d{6}-[a-z0-9][a-z0-9-]*/gi;
|
|
119
|
+
/**
|
|
120
|
+
* Single frontmatter pass over both backlog directories. Every drift check
|
|
121
|
+
* derives from this scan so the filesystem is walked once. `index.md` is the
|
|
122
|
+
* managed index, not an item, so it is excluded.
|
|
123
|
+
*/
|
|
124
|
+
async function collectBacklogItems(repoRoot) {
|
|
125
|
+
const records = [];
|
|
126
|
+
const directories = [
|
|
127
|
+
{ directory: 'items', relativeDir: BACKLOG_ITEMS_DIRECTORY },
|
|
128
|
+
{ directory: 'archived', relativeDir: BACKLOG_ARCHIVED_DIRECTORY },
|
|
129
|
+
];
|
|
130
|
+
for (const { directory, relativeDir } of directories) {
|
|
131
|
+
const fileNames = await listMarkdownFiles(join(repoRoot, relativeDir));
|
|
132
|
+
for (const fileName of fileNames) {
|
|
133
|
+
if (fileName === 'index.md') {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const content = await readIfExists(join(repoRoot, relativeDir, fileName));
|
|
137
|
+
records.push({
|
|
138
|
+
directory,
|
|
139
|
+
relativePath: `${relativeDir}/${fileName}`,
|
|
140
|
+
id: fileName.replace(/\.md$/, ''),
|
|
141
|
+
status: content ? extractBacklogStatus(content) : null,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return records;
|
|
146
|
+
}
|
|
147
|
+
/** Best-effort scan of completed-log content for backlog item ids. */
|
|
148
|
+
function extractCompletedIds(content) {
|
|
149
|
+
return [...content.matchAll(BACKLOG_ID_PATTERN)].map((match) => match[0]);
|
|
150
|
+
}
|
|
111
151
|
function checkStatus(missing) {
|
|
112
152
|
return missing.length === 0 ? 'pass' : 'fail';
|
|
113
153
|
}
|
|
@@ -246,5 +286,126 @@ export async function runPjmDoctorChecks(repoRoot, options = {}) {
|
|
|
246
286
|
? undefined
|
|
247
287
|
: 'Move active operational docs to pjm/ and leave reference/ for durable append-mostly artifacts.',
|
|
248
288
|
});
|
|
289
|
+
const backlogItems = await collectBacklogItems(repoRoot);
|
|
290
|
+
const terminalInItems = backlogItems
|
|
291
|
+
.filter((item) => item.directory === 'items' &&
|
|
292
|
+
item.status !== null &&
|
|
293
|
+
isTerminalBacklogStatus(item.status))
|
|
294
|
+
.map((item) => item.relativePath);
|
|
295
|
+
checks.push({
|
|
296
|
+
name: 'pjm:backlog_terminal_in_items',
|
|
297
|
+
description: 'Terminal-status backlog items awaiting archive',
|
|
298
|
+
status: checkStatus(terminalInItems),
|
|
299
|
+
message: terminalInItems.length === 0
|
|
300
|
+
? 'No terminal-status backlog items remain under items/.'
|
|
301
|
+
: `Terminal-status backlog items still under items/: ${terminalInItems.join(', ')}`,
|
|
302
|
+
fix: terminalInItems.length === 0
|
|
303
|
+
? undefined
|
|
304
|
+
: 'Run `oat backlog archive <id>` to move each closed/wont_do item into archived/.',
|
|
305
|
+
});
|
|
306
|
+
// A missing/empty `status:` is as much a drift as an out-of-enum value: the
|
|
307
|
+
// `oat backlog archive` command rejects both, so `pjm doctor` must surface
|
|
308
|
+
// both rather than leaving status-less items silently invisible.
|
|
309
|
+
const missingStatusItems = backlogItems
|
|
310
|
+
.filter((item) => item.status === null || item.status.trim() === '')
|
|
311
|
+
.map((item) => item.relativePath);
|
|
312
|
+
const outOfEnumStatusItems = backlogItems
|
|
313
|
+
.filter((item) => item.status !== null &&
|
|
314
|
+
item.status.trim() !== '' &&
|
|
315
|
+
!isValidBacklogStatus(item.status))
|
|
316
|
+
.map((item) => item.relativePath);
|
|
317
|
+
const invalidStatus = [...outOfEnumStatusItems, ...missingStatusItems];
|
|
318
|
+
const invalidStatusDetails = [];
|
|
319
|
+
if (outOfEnumStatusItems.length > 0) {
|
|
320
|
+
invalidStatusDetails.push(`out-of-enum status: ${outOfEnumStatusItems.join(', ')}`);
|
|
321
|
+
}
|
|
322
|
+
if (missingStatusItems.length > 0) {
|
|
323
|
+
invalidStatusDetails.push(`missing status: ${missingStatusItems.join(', ')}`);
|
|
324
|
+
}
|
|
325
|
+
checks.push({
|
|
326
|
+
name: 'pjm:backlog_invalid_status',
|
|
327
|
+
description: 'Backlog items with a missing or out-of-enum status',
|
|
328
|
+
status: checkStatus(invalidStatus),
|
|
329
|
+
message: invalidStatus.length === 0
|
|
330
|
+
? 'All backlog item statuses are within the valid enum.'
|
|
331
|
+
: `Backlog items with an invalid status (${invalidStatusDetails.join('; ')}). Valid statuses: ${BACKLOG_ITEM_STATUSES.join(', ')}.`,
|
|
332
|
+
fix: invalidStatus.length === 0
|
|
333
|
+
? undefined
|
|
334
|
+
: `Set a valid status (${BACKLOG_ITEM_STATUSES.join(', ')}) on each listed item.`,
|
|
335
|
+
});
|
|
336
|
+
const archivedOpen = backlogItems
|
|
337
|
+
.filter((item) => item.directory === 'archived' &&
|
|
338
|
+
item.status !== null &&
|
|
339
|
+
isValidBacklogStatus(item.status) &&
|
|
340
|
+
!isTerminalBacklogStatus(item.status))
|
|
341
|
+
.map((item) => item.relativePath);
|
|
342
|
+
checks.push({
|
|
343
|
+
name: 'pjm:backlog_archived_open',
|
|
344
|
+
description: 'Archived backlog items with a non-terminal status',
|
|
345
|
+
status: warnStatus(archivedOpen),
|
|
346
|
+
message: archivedOpen.length === 0
|
|
347
|
+
? 'No archived backlog items carry an open status.'
|
|
348
|
+
: `Archived backlog items still marked open/in_progress: ${archivedOpen.join(', ')}`,
|
|
349
|
+
fix: archivedOpen.length === 0
|
|
350
|
+
? undefined
|
|
351
|
+
: 'Set a terminal status (closed/wont_do) on each archived item, or move it back under items/.',
|
|
352
|
+
});
|
|
353
|
+
// A single id must never live in both `items/` and `archived/`: the live copy
|
|
354
|
+
// shadows the archived record and `oat backlog archive` refuses to reconcile
|
|
355
|
+
// it automatically (auto-archiving would clobber the archived file). Derive
|
|
356
|
+
// from the same single scan so the filesystem is not re-walked.
|
|
357
|
+
const backlogPathsByIdAndDir = new Map();
|
|
358
|
+
for (const item of backlogItems) {
|
|
359
|
+
const key = item.id.toLowerCase();
|
|
360
|
+
const entry = backlogPathsByIdAndDir.get(key) ?? {};
|
|
361
|
+
entry[item.directory] = item.relativePath;
|
|
362
|
+
backlogPathsByIdAndDir.set(key, entry);
|
|
363
|
+
}
|
|
364
|
+
const duplicateIdPairs = [];
|
|
365
|
+
for (const entry of backlogPathsByIdAndDir.values()) {
|
|
366
|
+
if (entry.items && entry.archived) {
|
|
367
|
+
duplicateIdPairs.push(`${entry.items} + ${entry.archived}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
checks.push({
|
|
371
|
+
name: 'pjm:backlog_duplicate_id',
|
|
372
|
+
description: 'Backlog ids present in both items/ and archived/',
|
|
373
|
+
status: checkStatus(duplicateIdPairs),
|
|
374
|
+
message: duplicateIdPairs.length === 0
|
|
375
|
+
? 'No backlog id is present in both items/ and archived/.'
|
|
376
|
+
: `Backlog ids present in both items/ and archived/: ${duplicateIdPairs.join('; ')}`,
|
|
377
|
+
fix: duplicateIdPairs.length === 0
|
|
378
|
+
? undefined
|
|
379
|
+
: 'Reconcile each duplicate manually: decide whether the active (items/) or archived copy is authoritative, remove the other, then re-run `oat backlog archive <id>` if the item still needs archiving.',
|
|
380
|
+
});
|
|
381
|
+
const itemPathsById = new Map();
|
|
382
|
+
for (const item of backlogItems) {
|
|
383
|
+
if (item.directory === 'items') {
|
|
384
|
+
itemPathsById.set(item.id.toLowerCase(), item.relativePath);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const completedContent = await readIfExists(join(repoRoot, BACKLOG_COMPLETED_FILE));
|
|
388
|
+
const completedUnarchived = [];
|
|
389
|
+
const seenUnarchived = new Set();
|
|
390
|
+
if (completedContent) {
|
|
391
|
+
for (const completedId of extractCompletedIds(completedContent)) {
|
|
392
|
+
const relativePath = itemPathsById.get(completedId.toLowerCase());
|
|
393
|
+
if (relativePath && !seenUnarchived.has(relativePath)) {
|
|
394
|
+
seenUnarchived.add(relativePath);
|
|
395
|
+
completedUnarchived.push(relativePath);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
checks.push({
|
|
400
|
+
name: 'pjm:backlog_completed_unarchived',
|
|
401
|
+
description: 'Completed-log entries whose item file is still under items/',
|
|
402
|
+
status: warnStatus(completedUnarchived),
|
|
403
|
+
message: completedUnarchived.length === 0
|
|
404
|
+
? 'No completed backlog entries reference an item still under items/.'
|
|
405
|
+
: `Completed log references items still under items/: ${completedUnarchived.join(', ')}`,
|
|
406
|
+
fix: completedUnarchived.length === 0
|
|
407
|
+
? undefined
|
|
408
|
+
: 'Run `oat backlog archive <id>` to archive each completed item still under items/.',
|
|
409
|
+
});
|
|
249
410
|
return checks;
|
|
250
411
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/pjm/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/pjm/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAA0B,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAiBnE,UAAU,sBAAsB;IAC9B,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,cAAc,EAAE,OAAO,cAAc,CAAC;IACtC,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AA6FD,wBAAgB,gBAAgB,CAC9B,SAAS,GAAE,OAAO,CAAC,sBAAsB,CAAM,GAC9C,OAAO,CAuJT"}
|
|
@@ -7,7 +7,7 @@ import { resolveProjectRoot } from '../../fs/paths.js';
|
|
|
7
7
|
import { formatDoctorResults } from '../../ui/output.js';
|
|
8
8
|
import { Command } from 'commander';
|
|
9
9
|
import { runPjmDoctorChecks } from './doctor.js';
|
|
10
|
-
import { initializeRepoReference } from './init.js';
|
|
10
|
+
import { initializeRepoReference, INSTRUCTIONS_SYNC_HINT } from './init.js';
|
|
11
11
|
import { migratePjmRepo, readPjmMigrationPrompt } from './migrate.js';
|
|
12
12
|
const DEFAULT_DEPENDENCIES = {
|
|
13
13
|
buildCommandContext,
|
|
@@ -111,6 +111,7 @@ export function createPjmCommand(overrides = {}) {
|
|
|
111
111
|
if (result.skipped.length > 0) {
|
|
112
112
|
context.logger.info(`Skipped existing: ${result.skipped.join(', ')}`);
|
|
113
113
|
}
|
|
114
|
+
context.logger.info(INSTRUCTIONS_SYNC_HINT);
|
|
114
115
|
}
|
|
115
116
|
process.exitCode = 0;
|
|
116
117
|
}
|
|
@@ -8,6 +8,7 @@ export interface RepoReferenceInitResult {
|
|
|
8
8
|
created: string[];
|
|
9
9
|
skipped: string[];
|
|
10
10
|
}
|
|
11
|
-
export declare const
|
|
11
|
+
export declare const INSTRUCTIONS_SYNC_HINT: string;
|
|
12
|
+
export declare const CANONICAL_REPO_REFERENCE_PATHS: readonly [...("AGENTS.md" | "pjm/AGENTS.md" | "pjm/current-state.md" | "pjm/roadmap.md" | "reference/AGENTS.md" | "README.md" | "pjm/handoffs/README.md")[], "pjm/backlog/index.md", "pjm/backlog/completed.md", "pjm/backlog/items/.gitkeep", "pjm/backlog/archived/.gitkeep", "reference/decisions/index.md"];
|
|
12
13
|
export declare function initializeRepoReference(options: InitializeRepoReferenceOptions): Promise<RepoReferenceInitResult>;
|
|
13
14
|
//# sourceMappingURL=init.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/pjm/init.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/pjm/init.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAoBD,eAAO,MAAM,sBAAsB,QAEiD,CAAC;AAWrF,eAAO,MAAM,8BAA8B,iTAIjC,CAAC;AAyEX,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,uBAAuB,CAAC,CAgElC"}
|
|
@@ -9,7 +9,14 @@ const TEMPLATE_TARGETS = [
|
|
|
9
9
|
{ template: 'current-state.md', target: 'pjm/current-state.md' },
|
|
10
10
|
{ template: 'roadmap.md', target: 'pjm/roadmap.md' },
|
|
11
11
|
{ template: 'reference-agents.md', target: 'reference/AGENTS.md' },
|
|
12
|
+
{ template: 'repo-readme.md', target: 'README.md' },
|
|
13
|
+
{ template: 'pjm-handoffs-readme.md', target: 'pjm/handoffs/README.md' },
|
|
12
14
|
];
|
|
15
|
+
// Next-step hint printed after init/backfill. `oat pjm init` never writes
|
|
16
|
+
// CLAUDE.md shims itself — strategy ownership stays with `oat instructions
|
|
17
|
+
// sync`, so we point the operator at it (with the `--dry-run` preview).
|
|
18
|
+
export const INSTRUCTIONS_SYNC_HINT = 'Next step: run `oat instructions sync` to create CLAUDE.md shims for the ' +
|
|
19
|
+
'repo-reference AGENTS.md files (preview with `oat instructions sync --dry-run`).';
|
|
13
20
|
const BACKLOG_PATHS = [
|
|
14
21
|
'pjm/backlog/index.md',
|
|
15
22
|
'pjm/backlog/completed.md',
|
|
@@ -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;AAG9B,OAAO,EAEL,KAAK,uBAAuB,EAG7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AASzB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
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;AAG9B,OAAO,EAEL,KAAK,uBAAuB,EAG7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AASzB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6BpC,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;AAknBD,wBAAgB,mCAAmC,CACjD,SAAS,GAAE,OAAO,CAAC,2BAA2B,CAAM,GACnD,OAAO,CAqDT"}
|
|
@@ -173,12 +173,67 @@ function readResolvedConfigCeiling(provider, resolvedConfig) {
|
|
|
173
173
|
function normalizeRole(value) {
|
|
174
174
|
return value === 'reviewer' ? 'reviewer' : 'implementer';
|
|
175
175
|
}
|
|
176
|
+
function providerValueOrder(provider) {
|
|
177
|
+
if (provider === 'codex') {
|
|
178
|
+
return CODEX_VALUES;
|
|
179
|
+
}
|
|
180
|
+
if (provider === 'claude') {
|
|
181
|
+
return CLAUDE_VALUES;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
function normalizePreferredValue(provider, value) {
|
|
186
|
+
if (!value) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const normalized = value.trim();
|
|
190
|
+
if (!normalized) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
const order = providerValueOrder(provider);
|
|
194
|
+
if (!order) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
if (!isValidProviderValue(provider, normalized)) {
|
|
198
|
+
const validValues = order.join(', ');
|
|
199
|
+
throw new Error(`Invalid preferred dispatch value "${normalized}" for ${provider}. Valid values: ${validValues}.`);
|
|
200
|
+
}
|
|
201
|
+
return normalized;
|
|
202
|
+
}
|
|
203
|
+
function selectDispatchValue(provider, role, ceilingValue, preferredValue) {
|
|
204
|
+
if (role === 'reviewer' || preferredValue === null) {
|
|
205
|
+
return {
|
|
206
|
+
role,
|
|
207
|
+
preferredValue,
|
|
208
|
+
selectedValue: ceilingValue,
|
|
209
|
+
capped: false,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const order = providerValueOrder(provider);
|
|
213
|
+
const preferredIndex = order?.indexOf(preferredValue) ?? -1;
|
|
214
|
+
const ceilingIndex = order?.indexOf(ceilingValue) ?? -1;
|
|
215
|
+
if (!order || preferredIndex < 0 || ceilingIndex < 0) {
|
|
216
|
+
return {
|
|
217
|
+
role,
|
|
218
|
+
preferredValue,
|
|
219
|
+
selectedValue: ceilingValue,
|
|
220
|
+
capped: false,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const selectedIndex = Math.min(preferredIndex, ceilingIndex);
|
|
224
|
+
return {
|
|
225
|
+
role,
|
|
226
|
+
preferredValue,
|
|
227
|
+
selectedValue: order[selectedIndex],
|
|
228
|
+
capped: preferredIndex > ceilingIndex,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
176
231
|
/**
|
|
177
232
|
* Join a resolved ceiling value with the active provider's adapter to compute
|
|
178
233
|
* the enforcement mode, mechanism, dispatch args, and verify-on-upgrade flag.
|
|
179
234
|
* Mode is computed here at call time — it is never read from persisted state.
|
|
180
235
|
*/
|
|
181
|
-
function buildProviderResolution(provider, value, role, orchestratorTier) {
|
|
236
|
+
function buildProviderResolution(provider, value, role, orchestratorTier, preferredValue) {
|
|
182
237
|
const adapter = getCeilingAdapter(provider);
|
|
183
238
|
if (value === null) {
|
|
184
239
|
return {
|
|
@@ -187,9 +242,17 @@ function buildProviderResolution(provider, value, role, orchestratorTier) {
|
|
|
187
242
|
mechanism: adapter.mechanism,
|
|
188
243
|
dispatchArgs: null,
|
|
189
244
|
verifyOnDispatch: false,
|
|
245
|
+
selection: {
|
|
246
|
+
role,
|
|
247
|
+
preferredValue,
|
|
248
|
+
selectedValue: null,
|
|
249
|
+
capped: false,
|
|
250
|
+
},
|
|
190
251
|
};
|
|
191
252
|
}
|
|
192
|
-
const
|
|
253
|
+
const selection = selectDispatchValue(provider, role, value, preferredValue);
|
|
254
|
+
const dispatchValue = selection.selectedValue ?? value;
|
|
255
|
+
const dispatchArgs = adapter.compileToDispatchArgs(dispatchValue, role, {
|
|
193
256
|
orchestratorTier,
|
|
194
257
|
});
|
|
195
258
|
let mode;
|
|
@@ -207,7 +270,10 @@ function buildProviderResolution(provider, value, role, orchestratorTier) {
|
|
|
207
270
|
mode,
|
|
208
271
|
mechanism: adapter.mechanism,
|
|
209
272
|
dispatchArgs,
|
|
210
|
-
verifyOnDispatch: adapter.verifyOnDispatch(
|
|
273
|
+
verifyOnDispatch: adapter.verifyOnDispatch(dispatchValue, {
|
|
274
|
+
orchestratorTier,
|
|
275
|
+
}),
|
|
276
|
+
selection,
|
|
211
277
|
};
|
|
212
278
|
}
|
|
213
279
|
function readCodexDefaultFromToml(content) {
|
|
@@ -257,8 +323,9 @@ async function resolveDispatchCeiling(context, dependencies, options) {
|
|
|
257
323
|
const providerDefaultEffort = provider === 'codex'
|
|
258
324
|
? await resolveCodexProviderDefaultEffort(repoRoot, context, dependencies)
|
|
259
325
|
: 'not-applicable';
|
|
326
|
+
const preferredValue = normalizePreferredValue(provider, options.preferred);
|
|
260
327
|
const resolvedValue = await resolveCeilingValue(provider, resolvedConfig, projectPath, dependencies);
|
|
261
|
-
const providerResolution = buildProviderResolution(provider, resolvedValue?.value ?? null, role, orchestratorTier);
|
|
328
|
+
const providerResolution = buildProviderResolution(provider, resolvedValue?.value ?? null, role, orchestratorTier, preferredValue);
|
|
262
329
|
const providers = {
|
|
263
330
|
[provider]: providerResolution,
|
|
264
331
|
};
|
|
@@ -332,9 +399,17 @@ function writeHumanResolution(context, resolution) {
|
|
|
332
399
|
if (resolution.provider === 'codex') {
|
|
333
400
|
context.logger.info(`Codex provider default effort: ${resolution.providerDefaultEffort}`);
|
|
334
401
|
context.logger.info(`Note: OAT will use pinned subagent variants up to ${resolution.value ?? 'the resolved ceiling'}. Base/unpinned roles resolve through the provider default.`);
|
|
402
|
+
if (providerResolution?.selection.selectedValue &&
|
|
403
|
+
providerResolution.selection.preferredValue) {
|
|
404
|
+
context.logger.info(`Selected Codex effort: ${providerResolution.selection.selectedValue}`);
|
|
405
|
+
}
|
|
335
406
|
}
|
|
336
407
|
else {
|
|
337
408
|
context.logger.info('Effort axis: not-applicable');
|
|
409
|
+
if (providerResolution?.selection.selectedValue &&
|
|
410
|
+
providerResolution.selection.preferredValue) {
|
|
411
|
+
context.logger.info(`Selected dispatch value: ${providerResolution.selection.selectedValue}`);
|
|
412
|
+
}
|
|
338
413
|
}
|
|
339
414
|
}
|
|
340
415
|
async function runDispatchCeilingResolve(context, dependencies, options) {
|
|
@@ -370,6 +445,7 @@ export function createProjectDispatchCeilingCommand(overrides = {}) {
|
|
|
370
445
|
.requiredOption('--provider <provider>', 'Provider name: codex or claude are enforced; any other provider resolves as advisory (unsupported)')
|
|
371
446
|
.option('--role <role>', 'Dispatch role for variant compilation: implementer (default) or reviewer')
|
|
372
447
|
.option('--orchestrator-tier <tier>', 'Orchestrator tier, used to flag verify-on-upgrade for above-orchestrator requests')
|
|
448
|
+
.option('--preferred <value>', 'Preferred implementer/fix dispatch value before capping by the resolved ceiling')
|
|
373
449
|
.option('--project-path <path>', 'Read project-state ceiling from an explicit project path')
|
|
374
450
|
.option('--preflight', 'Treat unresolved non-interactive resolution as an implementation block')
|
|
375
451
|
.option('--non-interactive', 'Force non-interactive block behavior when the ceiling is unresolved')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-agent-toolkit/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
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.
|
|
37
|
+
"@open-agent-toolkit/control-plane": "0.1.41"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.0",
|