@open-agent-toolkit/cli 0.1.46 → 0.1.50

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 (98) 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 +213 -113
  5. package/assets/docs/cli-utilities/workflow-gates.md +178 -27
  6. package/assets/docs/contributing/skills.md +14 -8
  7. package/assets/docs/provider-sync/providers.md +46 -8
  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 +297 -197
  13. package/assets/docs/workflows/projects/implementation-execution.md +306 -249
  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 +290 -149
  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 +110 -22
  25. package/assets/skills/oat-project-review-provide-remote/SKILL.md +28 -1
  26. package/dist/commands/config/index.d.ts +3 -0
  27. package/dist/commands/config/index.d.ts.map +1 -1
  28. package/dist/commands/config/index.js +92 -125
  29. package/dist/commands/doctor/index.d.ts +3 -0
  30. package/dist/commands/doctor/index.d.ts.map +1 -1
  31. package/dist/commands/doctor/index.js +57 -95
  32. package/dist/commands/gate/index.d.ts +7 -1
  33. package/dist/commands/gate/index.d.ts.map +1 -1
  34. package/dist/commands/gate/index.js +615 -67
  35. package/dist/commands/gate/review-verdict.d.ts +16 -0
  36. package/dist/commands/gate/review-verdict.d.ts.map +1 -1
  37. package/dist/commands/gate/review-verdict.js +72 -9
  38. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  39. package/dist/commands/project/dispatch-ceiling/index.js +567 -99
  40. package/dist/commands/providers/codex/materialize.d.ts.map +1 -1
  41. package/dist/commands/providers/codex/materialize.js +6 -1
  42. package/dist/commands/shared/frontmatter.d.ts +4 -0
  43. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  44. package/dist/commands/shared/frontmatter.js +23 -0
  45. package/dist/commands/status/index.d.ts +1 -0
  46. package/dist/commands/status/index.d.ts.map +1 -1
  47. package/dist/commands/status/index.js +10 -4
  48. package/dist/commands/sync/index.d.ts.map +1 -1
  49. package/dist/commands/sync/index.js +10 -3
  50. package/dist/commands/sync/sync.types.d.ts +1 -0
  51. package/dist/commands/sync/sync.types.d.ts.map +1 -1
  52. package/dist/config/dispatch-matrix.d.ts +59 -0
  53. package/dist/config/dispatch-matrix.d.ts.map +1 -0
  54. package/dist/config/dispatch-matrix.js +264 -0
  55. package/dist/config/oat-config.d.ts +9 -18
  56. package/dist/config/oat-config.d.ts.map +1 -1
  57. package/dist/config/oat-config.js +99 -120
  58. package/dist/config/resolve.d.ts +8 -0
  59. package/dist/config/resolve.d.ts.map +1 -1
  60. package/dist/config/resolve.js +66 -1
  61. package/dist/engine/index.d.ts +1 -1
  62. package/dist/engine/index.d.ts.map +1 -1
  63. package/dist/engine/index.js +1 -1
  64. package/dist/engine/scanner.d.ts +1 -0
  65. package/dist/engine/scanner.d.ts.map +1 -1
  66. package/dist/engine/scanner.js +17 -1
  67. package/dist/fs/paths.d.ts +4 -0
  68. package/dist/fs/paths.d.ts.map +1 -1
  69. package/dist/fs/paths.js +18 -1
  70. package/dist/providers/ceiling/registry.d.ts +1 -0
  71. package/dist/providers/ceiling/registry.d.ts.map +1 -1
  72. package/dist/providers/ceiling/registry.js +12 -5
  73. package/dist/providers/codex/codec/catalog.d.ts +14 -0
  74. package/dist/providers/codex/codec/catalog.d.ts.map +1 -0
  75. package/dist/providers/codex/codec/catalog.js +21 -0
  76. package/dist/providers/codex/codec/materialize.d.ts.map +1 -1
  77. package/dist/providers/codex/codec/materialize.js +6 -5
  78. package/dist/providers/codex/codec/shared.d.ts +19 -0
  79. package/dist/providers/codex/codec/shared.d.ts.map +1 -1
  80. package/dist/providers/codex/codec/shared.js +98 -5
  81. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  82. package/dist/providers/codex/codec/sync-extension.js +142 -32
  83. package/dist/providers/identity/availability.d.ts +12 -0
  84. package/dist/providers/identity/availability.d.ts.map +1 -1
  85. package/dist/providers/identity/availability.js +72 -29
  86. package/dist/providers/identity/dispatch-report.d.ts +124 -0
  87. package/dist/providers/identity/dispatch-report.d.ts.map +1 -0
  88. package/dist/providers/identity/dispatch-report.js +285 -0
  89. package/dist/providers/identity/dispatch-validation.d.ts +28 -0
  90. package/dist/providers/identity/dispatch-validation.d.ts.map +1 -0
  91. package/dist/providers/identity/dispatch-validation.js +149 -0
  92. package/dist/providers/identity/stamp.d.ts +2 -0
  93. package/dist/providers/identity/stamp.d.ts.map +1 -1
  94. package/dist/providers/identity/stamp.js +7 -1
  95. package/dist/shared/types.d.ts +1 -0
  96. package/dist/shared/types.d.ts.map +1 -1
  97. package/dist/shared/types.js +4 -0
  98. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAQzC,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAehB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkFpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AA65DD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAwKT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAezC,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAahB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,mCAAmC,EACnC,0BAA0B,EAC3B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkFpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,mCAAmC,EAAE,OAAO,mCAAmC,CAAC;IAChF,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAo0DD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAsKT"}
@@ -5,12 +5,14 @@ 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 { normalizeDispatchMatrix, validateDispatchRouteTarget, walkDispatchMatrix, } from '../../config/dispatch-matrix.js';
8
9
  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';
10
+ import { VALID_DISPATCH_POLICY_MODES, VALID_MANAGED_DISPATCH_POLICIES, readOatConfig, readOatLocalConfig, readUserConfig, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
10
11
  import { resolveEffectiveConfig, } from '../../config/resolve.js';
11
12
  import { resolveAssetsRoot } from '../../fs/assets.js';
12
13
  import { resolveProjectRoot } from '../../fs/paths.js';
13
14
  import { normalizeMatrixCellAvailability, validateMatrixCell, } from '../../providers/identity/availability.js';
15
+ import { createDispatchValidationPassContext, validateDispatchMatrixRefs, } from '../../providers/identity/dispatch-validation.js';
14
16
  import { Command } from 'commander';
15
17
  import { createConfigDumpCommand } from './dump.js';
16
18
  const DISPATCH_CEILING_PROVIDER_KEY_PREFIX = 'workflow.dispatchCeiling.providers.';
@@ -497,7 +499,7 @@ const CONFIG_CATALOG = [
497
499
  group: 'Workflow Preferences (3-layer: local > shared > user)',
498
500
  file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
499
501
  scope: 'workflow',
500
- type: 'low | medium | high | xhigh',
502
+ type: 'low | medium | high | xhigh | max',
501
503
  defaultValue: 'unset',
502
504
  mutability: 'read/write',
503
505
  owningCommand: 'oat config set workflow.dispatchCeiling.providers.codex <value>',
@@ -563,6 +565,8 @@ const DEFAULT_DEPENDENCIES = {
563
565
  readFile: (path) => readFileDefault(path, 'utf8'),
564
566
  confirmAction,
565
567
  validateMatrixCell,
568
+ createDispatchValidationPassContext,
569
+ validateDispatchMatrixRefs,
566
570
  processEnv: process.env,
567
571
  };
568
572
  function isConfigKey(value) {
@@ -623,6 +627,7 @@ const WORKFLOW_ENUM_VALUES = {
623
627
  'medium',
624
628
  'high',
625
629
  'xhigh',
630
+ 'max',
626
631
  ],
627
632
  'workflow.dispatchCeiling.providers.claude': [
628
633
  'haiku',
@@ -764,99 +769,62 @@ function parseDispatchMatrixRecommendation(raw) {
764
769
  if (!isRecord(parsed.providers)) {
765
770
  throw new Error('Dispatch matrix recommendation asset is missing providers.');
766
771
  }
772
+ const normalized = normalizeDispatchMatrix(parsed.providers, {
773
+ pathPrefix: 'workflow.dispatchCeiling.providers',
774
+ compatibilityMode: 'layered-config',
775
+ });
767
776
  return {
768
777
  version,
769
- providers: parsed.providers,
778
+ providers: normalized.providers,
779
+ issues: normalized.issues,
770
780
  };
771
781
  }
772
- function isRouteTarget(entry) {
773
- return isRecord(entry);
774
- }
775
- function formatRouteTargetValue(entry) {
776
- return [entry.model, entry.effort].filter(Boolean).join('/');
782
+ function collectDispatchMatrixCellRefs(providers) {
783
+ return walkDispatchMatrix(providers, {
784
+ source: 'repo-config',
785
+ pathPrefix: 'workflow.dispatchCeiling.providers',
786
+ });
777
787
  }
778
- function addDispatchMatrixCellRefs(refs, provider, path, cell) {
779
- if (typeof cell === 'string') {
780
- refs.push({ provider, value: cell, path });
781
- return;
782
- }
783
- for (const [index, entry] of cell.entries()) {
784
- const entryPath = `${path}[${index}]`;
785
- if (typeof entry === 'string') {
786
- refs.push({ provider, value: entry, path: entryPath });
787
- continue;
788
+ function collectDispatchMatrixTargetValidationErrors(refs, issues) {
789
+ const errors = issues.map((issue) => {
790
+ const relativePath = issue.path.startsWith(DISPATCH_CEILING_PROVIDER_KEY_PREFIX)
791
+ ? issue.path.slice(DISPATCH_CEILING_PROVIDER_KEY_PREFIX.length)
792
+ : '';
793
+ const provider = relativePath.split(/[.[]/, 1)[0] ?? '';
794
+ const closedValues = closedDispatchProviderValues(provider);
795
+ if (typeof issue.value === 'string' && closedValues) {
796
+ return `Invalid value for ${issue.path}: expected one of ${closedValues.join(' | ')}, got '${issue.value}'`;
788
797
  }
789
- if (!isRouteTarget(entry)) {
798
+ return `${issue.path}: malformed dispatch matrix entry (${issue.kind}).`;
799
+ });
800
+ for (const ref of refs) {
801
+ if (ref.target === null) {
790
802
  continue;
791
803
  }
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
- }
804
- if (entry.model) {
805
- refs.push({
806
- provider: targetProvider,
807
- value: entry.model,
808
- path: `${entryPath}.model`,
809
- });
810
- }
811
- if (entry.effort) {
812
- refs.push({
813
- provider: targetProvider,
814
- value: entry.effort,
815
- path: `${entryPath}.effort`,
816
- });
804
+ const validation = validateDispatchRouteTarget(ref.provider, ref.target);
805
+ if (!validation.valid) {
806
+ errors.push(`${ref.path}: ${validation.reason}`);
817
807
  }
818
808
  }
809
+ return errors;
819
810
  }
820
- function collectDispatchMatrixCellRefs(providers) {
821
- const refs = [];
822
- for (const [provider, providerValue] of Object.entries(providers)) {
823
- const providerPath = `workflow.dispatchCeiling.providers.${provider}`;
824
- if (typeof providerValue === 'string') {
825
- refs.push({ provider, value: providerValue, path: providerPath });
826
- continue;
827
- }
828
- for (const [tier, cell] of Object.entries(providerValue)) {
829
- if (cell === undefined) {
830
- continue;
831
- }
832
- addDispatchMatrixCellRefs(refs, provider, `${providerPath}.${tier}`, cell);
833
- }
811
+ function toAvailabilityRef(ref) {
812
+ if (ref.value !== null) {
813
+ return { provider: ref.provider, value: ref.value, path: ref.path };
834
814
  }
835
- return refs;
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
- }
815
+ if (ref.target === null) {
816
+ return null;
858
817
  }
859
- return errors;
818
+ const value = ref.target.model ?? ref.target.effort;
819
+ if (!value) {
820
+ return null;
821
+ }
822
+ return {
823
+ provider: ref.target.harness ?? ref.provider,
824
+ value,
825
+ path: ref.path,
826
+ target: ref.target,
827
+ };
860
828
  }
861
829
  function applyWorkflowValue(workflow, key, value) {
862
830
  const subKey = key.slice('workflow.'.length);
@@ -967,6 +935,9 @@ function formatResolvedValue(value) {
967
935
  if (Array.isArray(value)) {
968
936
  return value.join(',');
969
937
  }
938
+ if (typeof value === 'object') {
939
+ return JSON.stringify(value);
940
+ }
970
941
  return String(value);
971
942
  }
972
943
  async function getConfigValue(repoRoot, userConfigDir, key, dependencies) {
@@ -1184,73 +1155,75 @@ async function setConfigValue(repoRoot, userConfigDir, key, rawValue, surface, d
1184
1155
  source: 'shared',
1185
1156
  };
1186
1157
  }
1187
- function hasExistingDispatchMatrix(config) {
1188
- const providers = config.workflow?.dispatchCeiling?.providers;
1189
- return providers !== undefined && Object.keys(providers).length > 0;
1190
- }
1191
1158
  async function loadDispatchMatrixRecommendation(dependencies) {
1192
1159
  const assetsRoot = await dependencies.resolveAssetsRoot();
1193
1160
  const assetPath = join(assetsRoot, DISPATCH_MATRIX_RECOMMENDATION_ASSET);
1194
1161
  return parseDispatchMatrixRecommendation(await dependencies.readFile(assetPath));
1195
1162
  }
1196
1163
  async function validateRecommendationCells(repoRoot, recommendation, dependencies, warn) {
1197
- const targetErrors = collectDispatchMatrixTargetValidationErrors(recommendation.providers);
1164
+ const refs = collectDispatchMatrixCellRefs(recommendation.providers);
1165
+ const targetErrors = collectDispatchMatrixTargetValidationErrors(refs, recommendation.issues);
1198
1166
  if (targetErrors.length > 0) {
1199
1167
  throw new Error(targetErrors.join('\n'));
1200
1168
  }
1201
- for (const ref of collectDispatchMatrixCellRefs(recommendation.providers)) {
1169
+ const availabilityRefs = refs.filter((matrixRef) => {
1170
+ const ref = toAvailabilityRef(matrixRef);
1171
+ if (ref === null) {
1172
+ return false;
1173
+ }
1202
1174
  const closedValues = closedDispatchProviderValues(ref.provider);
1203
1175
  if (!ref.target && closedValues && !closedValues.includes(ref.value)) {
1204
1176
  throw new Error(`Invalid value for ${ref.path}: expected one of ${closedValues.join(' | ')}, got '${ref.value}'`);
1205
1177
  }
1206
- let availability;
1207
- try {
1208
- availability = await dependencies.validateMatrixCell(ref.provider, ref.value, {
1209
- cwd: repoRoot,
1210
- env: dependencies.processEnv,
1211
- detailed: true,
1212
- ...(ref.target ? { target: ref.target } : {}),
1213
- });
1214
- }
1215
- catch {
1216
- availability = 'unvalidated';
1217
- }
1218
- const warning = matrixCellAvailabilityWarning(ref.path, ref.value, availability);
1178
+ return true;
1179
+ });
1180
+ const pass = dependencies.createDispatchValidationPassContext({
1181
+ cwd: repoRoot,
1182
+ env: dependencies.processEnv,
1183
+ validateMatrixCell: dependencies.validateMatrixCell,
1184
+ });
1185
+ const results = await dependencies.validateDispatchMatrixRefs(availabilityRefs, pass);
1186
+ for (const result of results) {
1187
+ const ref = toAvailabilityRef(result.ref);
1188
+ const warning = matrixCellAvailabilityWarning(ref.path, ref.value, {
1189
+ availability: result.status,
1190
+ ...(result.diagnostic ? { message: result.diagnostic } : {}),
1191
+ });
1219
1192
  if (warning) {
1220
1193
  warn(warning);
1221
1194
  }
1222
1195
  }
1223
1196
  }
1224
1197
  function applyDispatchMatrixRecommendation(workflow, recommendation) {
1198
+ const existingProviders = workflow?.dispatchCeiling?.providers ?? {};
1199
+ const providers = {
1200
+ ...recommendation.providers,
1201
+ };
1202
+ for (const [provider, existingValue] of Object.entries(existingProviders)) {
1203
+ const recommendedValue = recommendation.providers[provider];
1204
+ if (recommendedValue &&
1205
+ typeof recommendedValue !== 'string' &&
1206
+ typeof existingValue !== 'string') {
1207
+ providers[provider] = { ...recommendedValue, ...existingValue };
1208
+ }
1209
+ else {
1210
+ providers[provider] = existingValue;
1211
+ }
1212
+ }
1225
1213
  return {
1226
1214
  ...(workflow ?? {}),
1227
1215
  dispatchCeiling: {
1228
1216
  ...workflow?.dispatchCeiling,
1229
1217
  recommendationVersion: recommendation.version,
1230
- providers: recommendation.providers,
1218
+ providers,
1231
1219
  },
1232
1220
  };
1233
1221
  }
1234
- async function confirmDispatchMatrixOverwrite(context, dependencies, source, yes) {
1235
- if (yes) {
1236
- return;
1237
- }
1238
- if (!context.interactive) {
1239
- throw new Error('Dispatch matrix already exists; rerun interactively or pass --yes to replace it.');
1240
- }
1241
- const shouldReplace = await dependencies.confirmAction(`Replace existing dispatch matrix in ${source} config?`, { interactive: context.interactive });
1242
- if (!shouldReplace) {
1243
- throw new Error('Dispatch matrix adoption cancelled; existing matrix unchanged.');
1244
- }
1245
- }
1246
1222
  async function adoptDispatchMatrixRecommendation(repoRoot, userConfigDir, options, context, dependencies) {
1247
1223
  const source = options.surface === 'auto' ? 'local' : options.surface;
1248
1224
  const recommendation = await loadDispatchMatrixRecommendation(dependencies);
1249
1225
  if (source === 'user') {
1250
1226
  const userConfig = await dependencies.readUserConfig(userConfigDir);
1251
- if (hasExistingDispatchMatrix(userConfig)) {
1252
- await confirmDispatchMatrixOverwrite(context, dependencies, source, options.yes);
1253
- }
1254
1227
  await validateRecommendationCells(repoRoot, recommendation, dependencies, context.logger.warn);
1255
1228
  await dependencies.writeUserConfig(userConfigDir, {
1256
1229
  ...userConfig,
@@ -1264,9 +1237,6 @@ async function adoptDispatchMatrixRecommendation(repoRoot, userConfigDir, option
1264
1237
  }
1265
1238
  if (source === 'local') {
1266
1239
  const localConfig = await dependencies.readOatLocalConfig(repoRoot);
1267
- if (hasExistingDispatchMatrix(localConfig)) {
1268
- await confirmDispatchMatrixOverwrite(context, dependencies, source, options.yes);
1269
- }
1270
1240
  await validateRecommendationCells(repoRoot, recommendation, dependencies, context.logger.warn);
1271
1241
  await dependencies.writeOatLocalConfig(repoRoot, {
1272
1242
  ...localConfig,
@@ -1279,9 +1249,6 @@ async function adoptDispatchMatrixRecommendation(repoRoot, userConfigDir, option
1279
1249
  };
1280
1250
  }
1281
1251
  const sharedConfig = await dependencies.readOatConfig(repoRoot);
1282
- if (hasExistingDispatchMatrix(sharedConfig)) {
1283
- await confirmDispatchMatrixOverwrite(context, dependencies, source, options.yes);
1284
- }
1285
1252
  await validateRecommendationCells(repoRoot, recommendation, dependencies, context.logger.warn);
1286
1253
  await dependencies.writeOatConfig(repoRoot, {
1287
1254
  ...sharedConfig,
@@ -1560,7 +1527,7 @@ export function createConfigCommand(overrides = {}) {
1560
1527
  .option('--shared', 'Write to the shared repo config (.oat/config.json)')
1561
1528
  .option('--local', 'Write to the repo-local config (.oat/config.local.json)')
1562
1529
  .option('--user', 'Write to the user-level config (~/.oat/config.json)')
1563
- .option('--yes', 'Replace an existing adopted matrix without prompting')
1530
+ .option('--yes', 'Compatibility flag; adoption always preserves explicit existing cells')
1564
1531
  .action(async (template, options, command) => {
1565
1532
  const context = dependencies.buildCommandContext(readGlobalOptions(command));
1566
1533
  try {
@@ -1579,7 +1546,7 @@ export function createConfigCommand(overrides = {}) {
1579
1546
  surface = 'local';
1580
1547
  else if (options.user)
1581
1548
  surface = 'user';
1582
- await runAdopt(template, { surface, yes: options.yes === true }, context, dependencies);
1549
+ await runAdopt(template, { surface }, context, dependencies);
1583
1550
  }
1584
1551
  catch (error) {
1585
1552
  const message = error instanceof Error ? error.message : String(error);
@@ -3,6 +3,7 @@ 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
5
  import { type MatrixCellAvailabilityResponse, type ValidateMatrixCellOptions } from '../../providers/identity/availability.js';
6
+ import { createDispatchValidationPassContext, validateDispatchMatrixRefs } from '../../providers/identity/dispatch-validation.js';
6
7
  import type { ConcreteScope } from '../../shared/types.js';
7
8
  import { type DoctorCheck } from '../../ui/output.js';
8
9
  import { Command } from 'commander';
@@ -23,6 +24,8 @@ interface DoctorDependencies {
23
24
  readOatLocalConfig: (repoRoot: string) => Promise<OatLocalConfig>;
24
25
  readUserConfig: (userConfigDir: string) => Promise<UserConfig>;
25
26
  validateMatrixCell: (provider: string, value: string, options: ValidateMatrixCellOptions) => Promise<MatrixCellAvailabilityResponse>;
27
+ createDispatchValidationPassContext: typeof createDispatchValidationPassContext;
28
+ validateDispatchMatrixRefs: typeof validateDispatchMatrixRefs;
26
29
  processEnv: NodeJS.ProcessEnv;
27
30
  runPjmDoctorChecks: (repoRoot: string, options?: PjmDoctorOptions) => Promise<DoctorCheck[]>;
28
31
  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":"AAWA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAY9B,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAO9D,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,mCAAmC,EACnC,0BAA0B,EAC3B,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,UAAU,kBAAkB;IAC1B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,gBAAgB,EAAE,CAChB,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1D,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,cAAc,EAAE,CACd,SAAS,EAAE,MAAM,KACd,OAAO,CACV,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CACnE,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,mCAAmC,EAAE,OAAO,mCAAmC,CAAC;IAChF,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,gBAAgB,KACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5B,kBAAkB,EAAE,CAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,KAC3C,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAClC;AAED,UAAU,oBAAoB;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,UAAU,kBAAkB;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,0BAA0B,EAAE,MAAM,CAAC;IACnC,cAAc,EAAE,oBAAoB,EAAE,CAAC;CACxC;AA2pBD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,kBAAkB,CAAM,GAC1C,OAAO,CAcT"}
@@ -7,7 +7,8 @@ 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, isCodexMaterializedRouteTarget, } from '../../config/oat-config.js';
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 { normalizeMatrixCellAvailability, validateMatrixCell, } from '../../providers/identity/availability.js';
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,82 +145,46 @@ function createDependencies() {
141
145
  checkSkillVersions: (scopeRoot, assetsRoot, pathExists = pathExistsDefault) => checkSkillVersionsDefault(scopeRoot, assetsRoot, pathExists),
142
146
  };
143
147
  }
144
- function isRouteTarget(entry) {
145
- return typeof entry === 'object' && entry !== null && !Array.isArray(entry);
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 formatRouteTargetValue(entry) {
148
- return [entry.model, entry.effort].filter(Boolean).join('/');
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 addDispatchMatrixCellRefs(refs, layer, provider, path, cell) {
151
- if (typeof cell === 'string') {
152
- refs.push({ layer, provider, value: cell, path });
153
- 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 };
154
175
  }
155
- for (const [index, entry] of cell.entries()) {
156
- const entryPath = `${path}[${index}]`;
157
- if (typeof entry === 'string') {
158
- refs.push({ layer, provider, value: entry, path: entryPath });
159
- continue;
160
- }
161
- if (!isRouteTarget(entry)) {
162
- continue;
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
- }
177
- if (entry.model) {
178
- refs.push({
179
- layer,
180
- provider: targetProvider,
181
- value: entry.model,
182
- path: `${entryPath}.model`,
183
- });
184
- }
185
- if (entry.effort) {
186
- refs.push({
187
- layer,
188
- provider: targetProvider,
189
- value: entry.effort,
190
- path: `${entryPath}.effort`,
191
- });
192
- }
176
+ if (ref.target === null) {
177
+ return null;
193
178
  }
194
- }
195
- function collectDispatchMatrixCellRefs(layers) {
196
- const refs = [];
197
- for (const { layer, config } of layers) {
198
- const providers = config.workflow?.dispatchCeiling?.providers ?? {};
199
- for (const [provider, providerValue] of Object.entries(providers)) {
200
- const providerPath = `workflow.dispatchCeiling.providers.${provider}`;
201
- if (typeof providerValue === 'string') {
202
- refs.push({
203
- layer,
204
- provider,
205
- value: providerValue,
206
- path: providerPath,
207
- });
208
- continue;
209
- }
210
- const tierMap = providerValue;
211
- for (const [tier, cell] of Object.entries(tierMap)) {
212
- if (cell === undefined) {
213
- continue;
214
- }
215
- addDispatchMatrixCellRefs(refs, layer, provider, `${providerPath}.${tier}`, cell);
216
- }
217
- }
179
+ const value = ref.target.model ?? ref.target.effort;
180
+ if (!value) {
181
+ return null;
218
182
  }
219
- return refs;
183
+ return {
184
+ provider: ref.target.harness ?? ref.provider,
185
+ value,
186
+ target: ref.target,
187
+ };
220
188
  }
221
189
  function formatDispatchMatrixIssueList(issues) {
222
190
  return issues
@@ -224,12 +192,12 @@ function formatDispatchMatrixIssueList(issues) {
224
192
  const suffix = 'message' in issue && typeof issue.message === 'string'
225
193
  ? `; ${issue.message}`
226
194
  : '';
227
- return `${issue.path}=${issue.value} (${issue.layer} config)${suffix}`;
195
+ return `${issue.path}=${dispatchMatrixRefValue(issue)} (${dispatchMatrixSourceLabel(issue.source)} config)${suffix}`;
228
196
  })
229
197
  .join(', ');
230
198
  }
231
199
  async function createDispatchMatrixDoctorCheck(scopeRoot, layers, dependencies) {
232
- const refs = collectDispatchMatrixCellRefs(layers);
200
+ const refs = collectDispatchMatrixCellRefs(layers).filter((ref) => dispatchMatrixAvailabilityRef(ref) !== null);
233
201
  if (refs.length === 0) {
234
202
  return {
235
203
  name: 'project:dispatch_matrix',
@@ -238,25 +206,19 @@ async function createDispatchMatrixDoctorCheck(scopeRoot, layers, dependencies)
238
206
  message: 'No configured dispatch matrix cells found in user, shared, or local config layers.',
239
207
  };
240
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);
241
215
  const issues = [];
242
- for (const ref of refs) {
243
- let result;
244
- try {
245
- result = normalizeMatrixCellAvailability(await dependencies.validateMatrixCell(ref.provider, ref.value, {
246
- cwd: scopeRoot,
247
- env: dependencies.processEnv,
248
- detailed: true,
249
- ...(ref.target ? { target: ref.target } : {}),
250
- }));
251
- }
252
- catch {
253
- result = { availability: 'unvalidated' };
254
- }
255
- if (result.availability !== 'valid') {
216
+ for (const result of results) {
217
+ if (result.status !== 'valid') {
256
218
  issues.push({
257
- ...ref,
258
- availability: result.availability,
259
- ...(result.message ? { message: result.message } : {}),
219
+ ...result.ref,
220
+ availability: result.status,
221
+ ...(result.diagnostic ? { message: result.diagnostic } : {}),
260
222
  });
261
223
  }
262
224
  }
@@ -532,9 +494,9 @@ async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies)
532
494
  dependencies.readOatLocalConfig(scopeRoot),
533
495
  ]);
534
496
  checks.push(await createDispatchMatrixDoctorCheck(scopeRoot, [
535
- { layer: 'user', config: userConfig },
536
- { layer: 'shared', config },
537
- { layer: 'local', config: localConfig },
497
+ { source: 'user-config', config: userConfig },
498
+ { source: 'repo-config', config },
499
+ { source: 'local-config', config: localConfig },
538
500
  ], dependencies));
539
501
  const projectManagementEnabled = config.tools?.['project-management'] === true;
540
502
  if (projectManagementEnabled) {
@@ -4,6 +4,7 @@ import { type ResolvedConfig } from '../../config/resolve.js';
4
4
  import { type ModelFamily } from '../../providers/identity/family.js';
5
5
  import { type IdentityConfidence, type IdentityProvenance } from '../../providers/identity/provenance.js';
6
6
  import { Command } from 'commander';
7
+ import { parseReviewGateVerdict } from './review-verdict.js';
7
8
  interface GateCommandDependencies {
8
9
  buildCommandContext: (options: GlobalOptions) => CommandContext;
9
10
  resolveProjectRoot: (cwd: string) => Promise<string>;
@@ -15,6 +16,7 @@ interface GateCommandDependencies {
15
16
  writeUserConfig: (userConfigDir: string, config: UserConfig) => Promise<void>;
16
17
  resolveEffectiveConfig: (repoRoot: string, userConfigDir: string, env: NodeJS.ProcessEnv) => Promise<ResolvedConfig>;
17
18
  runProcess: (command: string, args: string[], options: ProcessRunOptions) => Promise<ProcessRunResult>;
19
+ parseReviewGateVerdict: typeof parseReviewGateVerdict;
18
20
  processEnv: NodeJS.ProcessEnv;
19
21
  }
20
22
  interface ProcessRunOptions {
@@ -44,8 +46,10 @@ interface GateProducerIdentity {
44
46
  confidence: IdentityConfidence;
45
47
  family: ModelFamily;
46
48
  avoidFamilies: ModelFamily[];
49
+ contributingScopes?: string[];
50
+ contributingStampCount?: number;
47
51
  diversityClaimable: boolean;
48
- source: 'flag' | 'stamp' | 'unknown';
52
+ source: 'flag' | 'stamp' | 'aggregated-stamps' | 'unknown';
49
53
  }
50
54
  interface GateDiversityMetadata {
51
55
  avoid: CrossProviderAvoid;
@@ -57,6 +61,8 @@ interface GateDiversityMetadata {
57
61
  family: ModelFamily;
58
62
  source: GateProducerIdentity['source'];
59
63
  avoidFamilies: ModelFamily[];
64
+ contributingScopes?: string[];
65
+ contributingStampCount?: number;
60
66
  };
61
67
  reviewer: {
62
68
  target: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/gate/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/gate/index.ts"],"names":[],"mappings":"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"}