@open-agent-toolkit/cli 0.1.73 → 0.1.76

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 (139) hide show
  1. package/assets/config/dispatch-matrix-recommendation.json +13 -17
  2. package/assets/docs/cli-utilities/configuration.md +53 -72
  3. package/assets/docs/cli-utilities/workflow-gates.md +38 -2
  4. package/assets/docs/provider-sync/commands.md +26 -0
  5. package/assets/docs/provider-sync/config.md +17 -6
  6. package/assets/docs/provider-sync/index.md +17 -5
  7. package/assets/docs/provider-sync/manifest-and-drift.md +39 -1
  8. package/assets/docs/provider-sync/providers.md +23 -19
  9. package/assets/docs/provider-sync/scope-and-surface.md +5 -1
  10. package/assets/docs/reference/file-locations.md +9 -3
  11. package/assets/docs/reference/oat-directory-structure.md +11 -3
  12. package/assets/docs/reference/troubleshooting.md +11 -0
  13. package/assets/docs/workflows/projects/artifacts.md +1 -1
  14. package/assets/docs/workflows/projects/dispatch-ceiling.md +29 -33
  15. package/assets/docs/workflows/projects/implementation-execution.md +7 -4
  16. package/assets/docs/workflows/projects/lifecycle.md +4 -3
  17. package/assets/public-package-versions.json +4 -4
  18. package/assets/skills/create-agnostic-skill/SKILL.md +1 -1
  19. package/assets/skills/create-agnostic-skill/references/docs/skills-guide.md +24 -15
  20. package/assets/skills/oat-agent-instructions-analyze/references/docs/provider-reference.md +9 -3
  21. package/assets/skills/oat-agent-instructions-apply/references/docs/provider-reference.md +9 -3
  22. package/assets/skills/oat-dispatch-subagents/SKILL.md +1 -1
  23. package/assets/skills/oat-dispatch-subagents/references/provider-cursor.md +27 -15
  24. package/assets/skills/oat-project-autonomous/SKILL.md +1 -1
  25. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
  26. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
  27. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +17 -15
  28. package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +15 -9
  29. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
  30. package/assets/skills/oat-project-implement/references/phase-execution.md +11 -5
  31. package/assets/skills/oat-project-import-plan/SKILL.md +30 -8
  32. package/assets/skills/oat-project-plan/SKILL.md +30 -8
  33. package/assets/skills/oat-project-plan-writing/SKILL.md +12 -9
  34. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
  35. package/assets/skills/oat-project-quick-start/SKILL.md +30 -8
  36. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
  37. package/assets/skills/oat-project-review-provide/SKILL.md +19 -11
  38. package/assets/skills/oat-project-review-provide-remote/SKILL.md +27 -4
  39. package/dist/commands/config/index.d.ts.map +1 -1
  40. package/dist/commands/config/index.js +55 -0
  41. package/dist/commands/doctor/index.d.ts +2 -1
  42. package/dist/commands/doctor/index.d.ts.map +1 -1
  43. package/dist/commands/doctor/index.js +37 -1
  44. package/dist/commands/gate/index.d.ts +1 -1
  45. package/dist/commands/gate/index.d.ts.map +1 -1
  46. package/dist/commands/gate/index.js +35 -3
  47. package/dist/commands/init/index.d.ts +7 -3
  48. package/dist/commands/init/index.d.ts.map +1 -1
  49. package/dist/commands/init/index.js +129 -38
  50. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  51. package/dist/commands/project/dispatch-ceiling/index.js +17 -9
  52. package/dist/commands/remove/skill/remove-skill.d.ts +2 -0
  53. package/dist/commands/remove/skill/remove-skill.d.ts.map +1 -1
  54. package/dist/commands/remove/skill/remove-skill.js +20 -2
  55. package/dist/commands/shared/adopt-stray.d.ts.map +1 -1
  56. package/dist/commands/shared/adopt-stray.js +3 -0
  57. package/dist/commands/shared/codex-strays.d.ts +9 -0
  58. package/dist/commands/shared/codex-strays.d.ts.map +1 -1
  59. package/dist/commands/shared/codex-strays.js +4 -0
  60. package/dist/commands/shared/cursor-skill-disposition.d.ts +17 -0
  61. package/dist/commands/shared/cursor-skill-disposition.d.ts.map +1 -0
  62. package/dist/commands/shared/cursor-skill-disposition.js +41 -0
  63. package/dist/commands/status/index.d.ts +17 -5
  64. package/dist/commands/status/index.d.ts.map +1 -1
  65. package/dist/commands/status/index.js +151 -47
  66. package/dist/commands/sync/apply.d.ts.map +1 -1
  67. package/dist/commands/sync/apply.js +50 -24
  68. package/dist/commands/sync/dry-run.d.ts.map +1 -1
  69. package/dist/commands/sync/dry-run.js +31 -18
  70. package/dist/commands/sync/index.d.ts.map +1 -1
  71. package/dist/commands/sync/index.js +34 -25
  72. package/dist/commands/sync/sync.types.d.ts +26 -11
  73. package/dist/commands/sync/sync.types.d.ts.map +1 -1
  74. package/dist/commands/sync/sync.utils.d.ts.map +1 -1
  75. package/dist/commands/sync/sync.utils.js +3 -2
  76. package/dist/config/oat-config.d.ts +0 -1
  77. package/dist/config/oat-config.d.ts.map +1 -1
  78. package/dist/config/oat-config.js +26 -23
  79. package/dist/config/sync-config.d.ts +6 -2
  80. package/dist/config/sync-config.d.ts.map +1 -1
  81. package/dist/config/sync-config.js +18 -6
  82. package/dist/config/user-sync-config.d.ts +11 -0
  83. package/dist/config/user-sync-config.d.ts.map +1 -0
  84. package/dist/config/user-sync-config.js +61 -0
  85. package/dist/drift/strays.d.ts +1 -1
  86. package/dist/drift/strays.d.ts.map +1 -1
  87. package/dist/drift/strays.js +2 -1
  88. package/dist/engine/compute-plan.d.ts.map +1 -1
  89. package/dist/engine/compute-plan.js +154 -4
  90. package/dist/engine/engine.types.d.ts +2 -2
  91. package/dist/engine/engine.types.d.ts.map +1 -1
  92. package/dist/engine/engine.types.js +1 -0
  93. package/dist/engine/execute-plan.d.ts.map +1 -1
  94. package/dist/engine/execute-plan.js +4 -0
  95. package/dist/engine/index.d.ts +1 -1
  96. package/dist/engine/index.d.ts.map +1 -1
  97. package/dist/engine/index.js +1 -1
  98. package/dist/engine/scanner.d.ts +3 -1
  99. package/dist/engine/scanner.d.ts.map +1 -1
  100. package/dist/engine/scanner.js +4 -2
  101. package/dist/manifest/manifest.types.d.ts +12 -12
  102. package/dist/providers/ceiling/registry.d.ts +2 -0
  103. package/dist/providers/ceiling/registry.d.ts.map +1 -1
  104. package/dist/providers/ceiling/registry.js +20 -4
  105. package/dist/providers/codex/codec/sync-extension.d.ts +11 -10
  106. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  107. package/dist/providers/codex/codec/sync-extension.js +42 -14
  108. package/dist/providers/cursor/codec/catalog.d.ts +18 -0
  109. package/dist/providers/cursor/codec/catalog.d.ts.map +1 -0
  110. package/dist/providers/cursor/codec/catalog.js +39 -0
  111. package/dist/providers/cursor/codec/materialize.d.ts +24 -0
  112. package/dist/providers/cursor/codec/materialize.d.ts.map +1 -0
  113. package/dist/providers/cursor/codec/materialize.js +166 -0
  114. package/dist/providers/cursor/codec/shared.d.ts +14 -0
  115. package/dist/providers/cursor/codec/shared.d.ts.map +1 -0
  116. package/dist/providers/cursor/codec/shared.js +76 -0
  117. package/dist/providers/cursor/codec/sync-extension.d.ts +37 -0
  118. package/dist/providers/cursor/codec/sync-extension.d.ts.map +1 -0
  119. package/dist/providers/cursor/codec/sync-extension.js +393 -0
  120. package/dist/providers/cursor/index.d.ts +3 -0
  121. package/dist/providers/cursor/index.d.ts.map +1 -1
  122. package/dist/providers/cursor/index.js +3 -0
  123. package/dist/providers/cursor/paths.d.ts.map +1 -1
  124. package/dist/providers/cursor/paths.js +6 -4
  125. package/dist/providers/identity/availability.d.ts +7 -0
  126. package/dist/providers/identity/availability.d.ts.map +1 -1
  127. package/dist/providers/identity/availability.js +23 -0
  128. package/dist/providers/shared/adapter.types.d.ts +6 -0
  129. package/dist/providers/shared/adapter.types.d.ts.map +1 -1
  130. package/dist/providers/shared/adapter.utils.d.ts +2 -1
  131. package/dist/providers/shared/adapter.utils.d.ts.map +1 -1
  132. package/dist/providers/shared/adapter.utils.js +29 -6
  133. package/dist/providers/shared/index.d.ts +2 -0
  134. package/dist/providers/shared/index.d.ts.map +1 -1
  135. package/dist/providers/shared/index.js +1 -0
  136. package/dist/providers/shared/materialization-extension.d.ts +44 -0
  137. package/dist/providers/shared/materialization-extension.d.ts.map +1 -0
  138. package/dist/providers/shared/materialization-extension.js +26 -0
  139. package/package.json +2 -2
@@ -8,6 +8,7 @@ import { CliError } from '../../../errors/index.js';
8
8
  import { ensureDir, fileExists } from '../../../fs/io.js';
9
9
  import { validateRealPathWithinScope } from '../../../fs/paths.js';
10
10
  import TOML from '@iarna/toml';
11
+ import { hasMaterializationChanges, summarizeMaterializationPlan, } from '../../shared/index.js';
11
12
  import YAML from 'yaml';
12
13
  import { SUPPORTED_CODEX_BASE_ROLES, SUPPORTED_CODEX_ROLE_TARGETS, } from './catalog.js';
13
14
  import { mergeCodexConfig, readCodexMaxDepth, } from './config-merge.js';
@@ -404,10 +405,18 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
404
405
  const existingConfigPath = configPath(scopeRoot);
405
406
  const existingConfigContent = await readOptionalFile(existingConfigPath);
406
407
  if (isPartialSync && desiredRoles.length === 0) {
408
+ const aggregateConfigHash = hashContent(existingConfigContent ?? '');
407
409
  return {
410
+ provider: 'codex',
408
411
  operations: [],
412
+ managedEntries: [],
413
+ aggregateHash: aggregateConfigHash,
414
+ metadata: {
415
+ managedRoles: [],
416
+ aggregateConfigHash,
417
+ },
409
418
  managedRoles: [],
410
- aggregateConfigHash: hashContent(existingConfigContent ?? ''),
419
+ aggregateConfigHash,
411
420
  };
412
421
  }
413
422
  const inheritedMaxDepth = await readInheritedCodexMaxDepth(scopeRoot, options);
@@ -419,10 +428,12 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
419
428
  const existingRoleContent = await readOptionalFile(role.rolePath);
420
429
  if (existingRoleContent === null) {
421
430
  operations.push({
431
+ provider: 'codex',
422
432
  action: 'create',
423
433
  target: 'role',
424
434
  path: toRelativePath(scopeRoot, role.rolePath),
425
435
  reason: 'managed role file missing',
436
+ entryName: role.roleName,
426
437
  roleName: role.roleName,
427
438
  content: role.content,
428
439
  });
@@ -430,20 +441,24 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
430
441
  }
431
442
  if (existingRoleContent.trimEnd() !== role.content.trimEnd()) {
432
443
  operations.push({
444
+ provider: 'codex',
433
445
  action: 'update',
434
446
  target: 'role',
435
447
  path: toRelativePath(scopeRoot, role.rolePath),
436
448
  reason: 'managed role file differs from canonical export',
449
+ entryName: role.roleName,
437
450
  roleName: role.roleName,
438
451
  content: role.content,
439
452
  });
440
453
  continue;
441
454
  }
442
455
  operations.push({
456
+ provider: 'codex',
443
457
  action: 'skip',
444
458
  target: 'role',
445
459
  path: toRelativePath(scopeRoot, role.rolePath),
446
460
  reason: 'managed role file already in sync',
461
+ entryName: role.roleName,
447
462
  roleName: role.roleName,
448
463
  });
449
464
  }
@@ -453,10 +468,12 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
453
468
  if (staleRoleContent &&
454
469
  isOatManagedCodexRoleFile(staleRoleContent, staleRole)) {
455
470
  operations.push({
471
+ provider: 'codex',
456
472
  action: 'remove',
457
473
  target: 'role',
458
474
  path: toRelativePath(scopeRoot, staleRolePath),
459
475
  reason: 'stale managed role removed',
476
+ entryName: staleRole,
460
477
  roleName: staleRole,
461
478
  });
462
479
  }
@@ -468,6 +485,7 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
468
485
  inheritedMaxDepth,
469
486
  });
470
487
  operations.push({
488
+ provider: 'codex',
471
489
  action: existingConfigContent === null
472
490
  ? 'create'
473
491
  : configMerge.changed
@@ -482,10 +500,22 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
482
500
  : 'codex config already in sync',
483
501
  content: configMerge.mergedContent,
484
502
  });
503
+ const managedRoles = [
504
+ ...desiredRoles.map((role) => role.roleName),
505
+ ...staleRoles,
506
+ ];
507
+ const aggregateConfigHash = hashContent(configMerge.mergedContent);
485
508
  return {
509
+ provider: 'codex',
486
510
  operations,
487
- managedRoles: [...desiredRoles.map((role) => role.roleName), ...staleRoles],
488
- aggregateConfigHash: hashContent(configMerge.mergedContent),
511
+ managedEntries: managedRoles,
512
+ aggregateHash: aggregateConfigHash,
513
+ metadata: {
514
+ managedRoles,
515
+ aggregateConfigHash,
516
+ },
517
+ managedRoles,
518
+ aggregateConfigHash,
489
519
  };
490
520
  }
491
521
  export async function applyCodexProjectExtensionPlan(scopeRoot, plan) {
@@ -517,19 +547,10 @@ export async function applyCodexProjectExtensionPlan(scopeRoot, plan) {
517
547
  return result;
518
548
  }
519
549
  export function hasCodexExtensionChanges(plan) {
520
- return plan.operations.some((operation) => operation.action !== 'skip');
550
+ return hasMaterializationChanges(plan);
521
551
  }
522
552
  export function summarizeCodexExtension(plan) {
523
- let plannedOperations = 0;
524
- let skipped = 0;
525
- for (const operation of plan.operations) {
526
- if (operation.action === 'skip') {
527
- skipped += 1;
528
- continue;
529
- }
530
- plannedOperations += 1;
531
- }
532
- return { plannedOperations, skipped };
553
+ return summarizeMaterializationPlan(plan);
533
554
  }
534
555
  export function toCodexExtensionOperations(plan) {
535
556
  return plan.operations.map((operation) => ({
@@ -540,3 +561,10 @@ export function toCodexExtensionOperations(plan) {
540
561
  roleName: operation.roleName,
541
562
  }));
542
563
  }
564
+ export const codexMaterializationExtension = {
565
+ provider: 'codex',
566
+ computePlan(context) {
567
+ return computeCodexProjectExtensionPlan(context.scopeRoot, context.canonicalEntries, context.allowedCanonicalPaths, context.options);
568
+ },
569
+ applyPlan: applyCodexProjectExtensionPlan,
570
+ };
@@ -0,0 +1,18 @@
1
+ export type CursorPinSyntaxFamily = 'gpt-reasoning' | 'claude-effort' | 'composer-fast' | 'grok-effort-fast';
2
+ export interface CursorPinGateEvidence {
3
+ gate: 'g01';
4
+ probeName: string;
5
+ disposition: 'approved';
6
+ }
7
+ export interface CursorModelPinMapping {
8
+ ladderModelId: string;
9
+ frontmatterModel: string;
10
+ syntaxFamily: CursorPinSyntaxFamily;
11
+ gateEvidence: CursorPinGateEvidence;
12
+ catalogue: boolean;
13
+ }
14
+ export declare const CURSOR_MODEL_PIN_MAPPINGS: readonly [CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping, CursorModelPinMapping];
15
+ export declare const SUPPORTED_CURSOR_ROLE_TARGETS: CursorModelPinMapping[];
16
+ export declare const SUPPORTED_CURSOR_BASE_ROLES: readonly ["oat-phase-implementer", "oat-reviewer"];
17
+ export declare function findCursorModelPinMapping(ladderModelId: string): CursorModelPinMapping | undefined;
18
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../../src/providers/cursor/codec/catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAC7B,eAAe,GACf,eAAe,GACf,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,KAAK,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,qBAAqB,CAAC;IACpC,YAAY,EAAE,qBAAqB,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;CACpB;AA0BD,eAAO,MAAM,yBAAyB,oWA2Ee,CAAC;AAEtD,eAAO,MAAM,6BAA6B,yBAEzC,CAAC;AAEF,eAAO,MAAM,2BAA2B,oDAG9B,CAAC;AAEX,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,GACpB,qBAAqB,GAAG,SAAS,CAInC"}
@@ -0,0 +1,39 @@
1
+ function approvedMapping(ladderModelId, frontmatterModel, syntaxFamily, options = {}) {
2
+ return {
3
+ ladderModelId,
4
+ frontmatterModel,
5
+ syntaxFamily,
6
+ gateEvidence: {
7
+ gate: 'g01',
8
+ probeName: options.probeName ??
9
+ `oat-pin-probe-${ladderModelId.replaceAll('.', '-')}`,
10
+ disposition: 'approved',
11
+ },
12
+ catalogue: options.catalogue ?? true,
13
+ };
14
+ }
15
+ export const CURSOR_MODEL_PIN_MAPPINGS = [
16
+ approvedMapping('composer-2.5', 'composer-2.5[fast=true]', 'composer-fast'),
17
+ approvedMapping('composer-2.5-fast', 'composer-2.5[fast=true]', 'composer-fast', { catalogue: false }),
18
+ approvedMapping('claude-sonnet-5-high', 'claude-sonnet-5[effort=high]', 'claude-effort'),
19
+ approvedMapping('gpt-5.6-luna-high', 'gpt-5.6-luna[reasoning=high]', 'gpt-reasoning'),
20
+ approvedMapping('gpt-5.6-luna-xhigh', 'gpt-5.6-luna[reasoning=xhigh]', 'gpt-reasoning'),
21
+ approvedMapping('cursor-grok-4.5-high', 'grok-4.5[effort=high,fast=false]', 'grok-effort-fast'),
22
+ approvedMapping('cursor-grok-4.5-high-fast', 'grok-4.5[effort=high,fast=true]', 'grok-effort-fast', { catalogue: false }),
23
+ approvedMapping('gpt-5.6-terra-high', 'gpt-5.6-terra[reasoning=high]', 'gpt-reasoning'),
24
+ approvedMapping('gpt-5.6-sol-medium', 'gpt-5.6-sol[reasoning=medium]', 'gpt-reasoning'),
25
+ approvedMapping('gpt-5.6-sol-high', 'gpt-5.6-sol[reasoning=high]', 'gpt-reasoning'),
26
+ approvedMapping('claude-fable-5-thinking-high', 'claude-fable-5[effort=high]', 'claude-effort'),
27
+ approvedMapping('claude-fable-5-thinking-xhigh', 'claude-fable-5[effort=xhigh]', 'claude-effort'),
28
+ approvedMapping('claude-fable-5-xhigh', 'claude-fable-5[effort=xhigh]', 'claude-effort', { catalogue: false }),
29
+ approvedMapping('gpt-5.6-sol-xhigh', 'gpt-5.6-sol[reasoning=xhigh]', 'gpt-reasoning'),
30
+ approvedMapping('gpt-5.6-sol-max', 'gpt-5.6-sol[reasoning=max]', 'gpt-reasoning'),
31
+ ];
32
+ export const SUPPORTED_CURSOR_ROLE_TARGETS = CURSOR_MODEL_PIN_MAPPINGS.filter((mapping) => mapping.catalogue);
33
+ export const SUPPORTED_CURSOR_BASE_ROLES = [
34
+ 'oat-phase-implementer',
35
+ 'oat-reviewer',
36
+ ];
37
+ export function findCursorModelPinMapping(ladderModelId) {
38
+ return CURSOR_MODEL_PIN_MAPPINGS.find((mapping) => mapping.ladderModelId === ladderModelId);
39
+ }
@@ -0,0 +1,24 @@
1
+ import type { CanonicalAgentDocument } from '../../../agents/canonical/index.js';
2
+ import type { CursorModelPinMapping } from './catalog.js';
3
+ import { type CursorRoleOwner } from './shared.js';
4
+ export interface CursorMaterializedAgent {
5
+ roleName: string;
6
+ fileName: string;
7
+ description: string;
8
+ content: string;
9
+ owner: CursorRoleOwner;
10
+ mapping: CursorModelPinMapping;
11
+ }
12
+ export interface CursorMaterializeAgentOptions {
13
+ agent: CanonicalAgentDocument;
14
+ mapping: CursorModelPinMapping;
15
+ owner: CursorRoleOwner;
16
+ }
17
+ export declare function materializeCursorAgent({ agent, mapping, owner, }: CursorMaterializeAgentOptions): CursorMaterializedAgent;
18
+ export declare function materializeCursorAgents(options: {
19
+ agents: CanonicalAgentDocument[];
20
+ targets: readonly CursorModelPinMapping[];
21
+ owner: CursorRoleOwner;
22
+ }): CursorMaterializedAgent[];
23
+ export declare function assertNoUnmanagedCursorAgentCollisions(scopeRoot: string, desiredRoleNames: Iterable<string>): Promise<void>;
24
+ //# sourceMappingURL=materialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize.d.ts","sourceRoot":"","sources":["../../../../src/providers/cursor/codec/materialize.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAKhE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAQlB,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,qBAAqB,CAAC;CAChC;AAED,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE,sBAAsB,CAAC;IAC9B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,KAAK,EAAE,eAAe,CAAC;CACxB;AA4BD,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,OAAO,EACP,KAAK,GACN,EAAE,6BAA6B,GAAG,uBAAuB,CA8BzD;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC1C,KAAK,EAAE,eAAe,CAAC;CACxB,GAAG,uBAAuB,EAAE,CAwB5B;AAqCD,wBAAsB,sCAAsC,CAC1D,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CA0Ef"}
@@ -0,0 +1,166 @@
1
+ import { lstat, readFile, readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { CliError } from '../../../errors/index.js';
4
+ import { validateRealPathWithinScope } from '../../../fs/paths.js';
5
+ import YAML from 'yaml';
6
+ import { buildCursorMaterializedRoleName, isOatManagedCursorRoleFile, normalizeCursorRoleName, renderCursorManagedComments, } from './shared.js';
7
+ const CURSOR_AGENT_DISCOVERY_DIRECTORIES = [
8
+ '.cursor/agents',
9
+ '.claude/agents',
10
+ '.codex/agents',
11
+ ];
12
+ function assertApprovedMapping(mapping) {
13
+ if (mapping.gateEvidence.gate !== 'g01' ||
14
+ mapping.gateEvidence.disposition !== 'approved' ||
15
+ !mapping.gateEvidence.probeName.trim()) {
16
+ throw new CliError(`Cannot materialize Cursor model ${mapping.ladderModelId}: mapping-specific gate g01 approval is required.`);
17
+ }
18
+ if (!/\[[^\]]+\]$/.test(mapping.frontmatterModel)) {
19
+ throw new CliError(`Cannot materialize Cursor model ${mapping.ladderModelId}: frontmatter model must include a non-empty bracket segment.`);
20
+ }
21
+ }
22
+ function optionalCursorBoolean(agent, field) {
23
+ const value = field === 'readonly' ? agent.readonly : agent.frontmatter[field];
24
+ return typeof value === 'boolean' ? value : undefined;
25
+ }
26
+ export function materializeCursorAgent({ agent, mapping, owner, }) {
27
+ assertApprovedMapping(mapping);
28
+ const roleName = buildCursorMaterializedRoleName({
29
+ agentName: agent.name,
30
+ ladderModelId: mapping.ladderModelId,
31
+ });
32
+ if (!roleName) {
33
+ throw new CliError('Cannot materialize Cursor role: missing role name.');
34
+ }
35
+ const readonly = optionalCursorBoolean(agent, 'readonly');
36
+ const isBackground = optionalCursorBoolean(agent, 'is_background');
37
+ const frontmatter = {
38
+ name: roleName,
39
+ description: agent.description,
40
+ model: mapping.frontmatterModel,
41
+ ...(readonly !== undefined ? { readonly } : {}),
42
+ ...(isBackground !== undefined ? { is_background: isBackground } : {}),
43
+ };
44
+ const yaml = YAML.stringify(frontmatter).trimEnd();
45
+ const comments = renderCursorManagedComments(roleName, owner).join('\n');
46
+ return {
47
+ roleName,
48
+ fileName: `${roleName}.md`,
49
+ description: agent.description,
50
+ content: `---\n${comments}\n${yaml}\n---\n${agent.body}`,
51
+ owner,
52
+ mapping,
53
+ };
54
+ }
55
+ export function materializeCursorAgents(options) {
56
+ const materialized = options.agents.flatMap((agent) => options.targets.map((mapping) => materializeCursorAgent({
57
+ agent,
58
+ mapping,
59
+ owner: options.owner,
60
+ })));
61
+ const byRoleName = new Map();
62
+ for (const role of materialized) {
63
+ const existing = byRoleName.get(role.roleName);
64
+ if (existing) {
65
+ throw new CliError(`Distinct Cursor targets produced the same Cursor role name ${role.roleName} (${existing.mapping.ladderModelId}, ${role.mapping.ladderModelId}). Refusing ambiguous role writes.`);
66
+ }
67
+ byRoleName.set(role.roleName, role);
68
+ }
69
+ return materialized.sort((left, right) => left.roleName.localeCompare(right.roleName));
70
+ }
71
+ async function readPathStats(path) {
72
+ try {
73
+ return await lstat(path);
74
+ }
75
+ catch (error) {
76
+ if (error.code === 'ENOENT') {
77
+ return null;
78
+ }
79
+ throw error;
80
+ }
81
+ }
82
+ function readDeclaredCursorAgentName(content) {
83
+ const normalized = content.startsWith('\uFEFF') ? content.slice(1) : content;
84
+ const match = /^---\n([\s\S]*?)\n---(?:\n|$)/.exec(normalized);
85
+ if (!match?.[1]) {
86
+ return null;
87
+ }
88
+ try {
89
+ const frontmatter = YAML.parse(match[1]);
90
+ if (!frontmatter ||
91
+ typeof frontmatter !== 'object' ||
92
+ Array.isArray(frontmatter)) {
93
+ return null;
94
+ }
95
+ const name = frontmatter['name'];
96
+ return typeof name === 'string' && name.trim()
97
+ ? normalizeCursorRoleName(name)
98
+ : null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ export async function assertNoUnmanagedCursorAgentCollisions(scopeRoot, desiredRoleNames) {
105
+ const desiredNames = new Set(desiredRoleNames);
106
+ for (const directory of CURSOR_AGENT_DISCOVERY_DIRECTORIES) {
107
+ const directoryPath = join(scopeRoot, directory);
108
+ const directoryStats = await readPathStats(directoryPath);
109
+ if (!directoryStats) {
110
+ continue;
111
+ }
112
+ if (directoryStats.isSymbolicLink()) {
113
+ throw new CliError(`Cursor agent discovery directory is a symbolic link: ${directory}. Refusing unsafe materialization.`);
114
+ }
115
+ if (!directoryStats.isDirectory()) {
116
+ throw new CliError(`Cursor agent discovery path is not a directory: ${directory}.`);
117
+ }
118
+ for (const fileName of await readdir(directoryPath)) {
119
+ if (!fileName.endsWith('.md')) {
120
+ continue;
121
+ }
122
+ const relativePath = `${directory}/${fileName}`;
123
+ const absolutePath = join(directoryPath, fileName);
124
+ const filenameRoleName = normalizeCursorRoleName(fileName);
125
+ const fileStats = await readPathStats(absolutePath);
126
+ if (!fileStats) {
127
+ continue;
128
+ }
129
+ if (fileStats.isSymbolicLink()) {
130
+ try {
131
+ await validateRealPathWithinScope(absolutePath, scopeRoot);
132
+ }
133
+ catch {
134
+ throw new CliError(`Cursor agent definition is a symbolic link at ${relativePath} whose target escapes the sync scope.`);
135
+ }
136
+ }
137
+ if (!fileStats.isFile() && !fileStats.isSymbolicLink()) {
138
+ continue;
139
+ }
140
+ let content;
141
+ try {
142
+ content = await readFile(absolutePath, 'utf8');
143
+ }
144
+ catch {
145
+ throw new CliError(`Cursor role name collision at ${relativePath}: existing Markdown definition cannot be verified as OAT-managed.`);
146
+ }
147
+ const declaredRoleName = readDeclaredCursorAgentName(content);
148
+ const filenameCollision = desiredNames.has(filenameRoleName);
149
+ const declaredCollision = declaredRoleName !== null && desiredNames.has(declaredRoleName);
150
+ if (!filenameCollision && !declaredCollision) {
151
+ continue;
152
+ }
153
+ if (fileStats.isSymbolicLink()) {
154
+ throw new CliError(`Cursor agent definition is a symbolic link at ${relativePath} with a colliding role name. Refusing unsafe materialization.`);
155
+ }
156
+ const collidingRoleName = declaredCollision
157
+ ? declaredRoleName
158
+ : filenameRoleName;
159
+ if (!isOatManagedCursorRoleFile(content, collidingRoleName)) {
160
+ throw new CliError(declaredCollision && !filenameCollision
161
+ ? `Unmanaged Cursor definition at ${relativePath} declares colliding name ${collidingRoleName}.`
162
+ : `Cursor role name collision at ${relativePath}: existing Markdown definition is unmanaged.`);
163
+ }
164
+ }
165
+ }
166
+ }
@@ -0,0 +1,14 @@
1
+ export declare const OAT_MANAGED_CURSOR_COMMENT = "# oat-managed: true";
2
+ export declare const OAT_CURSOR_ROLE_COMMENT_PREFIX = "# oat-role: ";
3
+ export declare const OAT_CURSOR_OWNER_COMMENT_PREFIX = "# oat-owner: ";
4
+ export type CursorRoleOwner = 'supported-catalogue' | 'user-config' | 'project-config';
5
+ export declare function normalizeCursorRoleName(input: string): string;
6
+ export declare function buildCursorMaterializedRoleName(options: {
7
+ agentName: string;
8
+ ladderModelId: string;
9
+ }): string;
10
+ export declare function renderCursorManagedComments(roleName: string, owner: CursorRoleOwner): string[];
11
+ export declare function isOatManagedCursorRoleFile(content: string, roleName?: string): boolean;
12
+ export declare function readOatManagedCursorRoleName(content: string): string | null;
13
+ export declare function readOatManagedCursorRoleOwner(content: string): CursorRoleOwner | null;
14
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/providers/cursor/codec/shared.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,wBAAwB,CAAC;AAChE,eAAO,MAAM,8BAA8B,iBAAiB,CAAC;AAC7D,eAAO,MAAM,+BAA+B,kBAAkB,CAAC;AAE/D,MAAM,MAAM,eAAe,GACvB,qBAAqB,GACrB,aAAa,GACb,gBAAgB,CAAC;AAYrB,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,CAIT;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,eAAe,GACrB,MAAM,EAAE,CAcV;AAgDD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAMT;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE3E;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,GACd,eAAe,GAAG,IAAI,CAExB"}
@@ -0,0 +1,76 @@
1
+ export const OAT_MANAGED_CURSOR_COMMENT = '# oat-managed: true';
2
+ export const OAT_CURSOR_ROLE_COMMENT_PREFIX = '# oat-role: ';
3
+ export const OAT_CURSOR_OWNER_COMMENT_PREFIX = '# oat-owner: ';
4
+ const CURSOR_ROLE_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
5
+ const CURSOR_ROLE_COMMENT_PATTERN = /^# oat-role: ([a-z0-9]+(?:-[a-z0-9]+)*)$/;
6
+ const CURSOR_OWNER_COMMENT_PATTERN = /^# oat-owner: (supported-catalogue|user-config|project-config)$/;
7
+ export function normalizeCursorRoleName(input) {
8
+ return input
9
+ .trim()
10
+ .toLowerCase()
11
+ .replace(/\.md$/i, '')
12
+ .replace(/[^a-z0-9]+/g, '-')
13
+ .replace(/-{2,}/g, '-')
14
+ .replace(/^-+|-+$/g, '');
15
+ }
16
+ export function buildCursorMaterializedRoleName(options) {
17
+ return normalizeCursorRoleName(`${normalizeCursorRoleName(options.agentName)}-${normalizeCursorRoleName(options.ladderModelId)}`);
18
+ }
19
+ export function renderCursorManagedComments(roleName, owner) {
20
+ const normalizedRoleName = normalizeCursorRoleName(roleName);
21
+ if (normalizedRoleName !== roleName ||
22
+ !CURSOR_ROLE_NAME_PATTERN.test(roleName)) {
23
+ throw new Error(`Invalid managed Cursor role name: ${roleName}`);
24
+ }
25
+ return [
26
+ OAT_MANAGED_CURSOR_COMMENT,
27
+ `${OAT_CURSOR_ROLE_COMMENT_PREFIX}${roleName}`,
28
+ `${OAT_CURSOR_OWNER_COMMENT_PREFIX}${owner}`,
29
+ ];
30
+ }
31
+ function frontmatterLines(content) {
32
+ const normalized = content.startsWith('\uFEFF') ? content.slice(1) : content;
33
+ if (!normalized.startsWith('---\n')) {
34
+ return null;
35
+ }
36
+ const end = normalized.indexOf('\n---', 4);
37
+ if (end < 0) {
38
+ return null;
39
+ }
40
+ return normalized.slice(4, end).split('\n');
41
+ }
42
+ function parseCursorManagedRoleMarkers(content) {
43
+ const lines = frontmatterLines(content);
44
+ if (!lines) {
45
+ return null;
46
+ }
47
+ const managedLines = lines.filter((line) => line.startsWith('# oat-managed:'));
48
+ const roleLines = lines.filter((line) => line.startsWith('# oat-role:'));
49
+ const ownerLines = lines.filter((line) => line.startsWith('# oat-owner:'));
50
+ if (managedLines.length !== 1 ||
51
+ roleLines.length !== 1 ||
52
+ ownerLines.length !== 1 ||
53
+ managedLines[0] !== OAT_MANAGED_CURSOR_COMMENT) {
54
+ return null;
55
+ }
56
+ const roleMatch = CURSOR_ROLE_COMMENT_PATTERN.exec(roleLines[0]);
57
+ const ownerMatch = CURSOR_OWNER_COMMENT_PATTERN.exec(ownerLines[0]);
58
+ if (!roleMatch || !ownerMatch) {
59
+ return null;
60
+ }
61
+ return {
62
+ roleName: roleMatch[1],
63
+ owner: ownerMatch[1],
64
+ };
65
+ }
66
+ export function isOatManagedCursorRoleFile(content, roleName) {
67
+ const markers = parseCursorManagedRoleMarkers(content);
68
+ return (markers !== null &&
69
+ (roleName === undefined || markers.roleName === roleName));
70
+ }
71
+ export function readOatManagedCursorRoleName(content) {
72
+ return parseCursorManagedRoleMarkers(content)?.roleName ?? null;
73
+ }
74
+ export function readOatManagedCursorRoleOwner(content) {
75
+ return parseCursorManagedRoleMarkers(content)?.owner ?? null;
76
+ }
@@ -0,0 +1,37 @@
1
+ import type { CanonicalEntry } from '../../../engine/index.js';
2
+ import { type MaterializationApplyResult, type MaterializationContext, type MaterializationExtension, type MaterializationOperation, type MaterializationPlan, type MaterializationWriteOperation } from '../../shared/index.js';
3
+ import { type CursorModelPinMapping } from './catalog.js';
4
+ import { type CursorRoleOwner } from './shared.js';
5
+ export type CursorExtensionTarget = 'role';
6
+ export interface CursorExtensionOperation extends MaterializationOperation<'cursor', CursorExtensionTarget> {
7
+ roleName?: string;
8
+ }
9
+ export interface CursorExtensionWriteOperation extends MaterializationWriteOperation<'cursor', CursorExtensionTarget> {
10
+ roleName?: string;
11
+ }
12
+ export interface CursorExtensionPlanMetadata {
13
+ cleanupOwners: CursorRoleOwner[];
14
+ isPartialSync: boolean;
15
+ }
16
+ export interface CursorExtensionPlan extends MaterializationPlan<'cursor', CursorExtensionTarget, CursorExtensionPlanMetadata> {
17
+ operations: CursorExtensionWriteOperation[];
18
+ }
19
+ export type CursorExtensionApplyResult = MaterializationApplyResult;
20
+ export interface CursorMaterializationTargetOptions {
21
+ userConfigDir?: string;
22
+ projectPath?: string | null;
23
+ env?: NodeJS.ProcessEnv;
24
+ enabled?: boolean;
25
+ modelMappings?: readonly CursorModelPinMapping[];
26
+ supportedTargets?: readonly CursorModelPinMapping[];
27
+ }
28
+ export declare function computeCursorProjectExtensionPlan(scopeRoot: string, canonicalEntries: CanonicalEntry[], allowedCanonicalPaths?: string[], options?: CursorMaterializationTargetOptions): Promise<CursorExtensionPlan>;
29
+ export declare function applyCursorProjectExtensionPlan(scopeRoot: string, plan: CursorExtensionPlan): Promise<CursorExtensionApplyResult>;
30
+ export declare function hasCursorExtensionChanges(plan: CursorExtensionPlan): boolean;
31
+ export declare function summarizeCursorExtension(plan: CursorExtensionPlan): {
32
+ plannedOperations: number;
33
+ skipped: number;
34
+ };
35
+ export declare function toCursorExtensionOperations(plan: CursorExtensionPlan): CursorExtensionOperation[];
36
+ export declare const cursorMaterializationExtension: MaterializationExtension<CursorExtensionPlan, MaterializationContext<CursorMaterializationTargetOptions>>;
37
+ //# sourceMappingURL=sync-extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/cursor/codec/sync-extension.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIpD,OAAO,EAIL,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAC;AAMnB,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB,CACxE,QAAQ,EACR,qBAAqB,CACtB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA8B,SAAQ,6BAA6B,CAClF,QAAQ,EACR,qBAAqB,CACtB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB,CAC9D,QAAQ,EACR,qBAAqB,EACrB,2BAA2B,CAC5B;IACC,UAAU,EAAE,6BAA6B,EAAE,CAAC;CAC7C;AAED,MAAM,MAAM,0BAA0B,GAAG,0BAA0B,CAAC;AAQpE,MAAM,WAAW,kCAAkC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD,gBAAgB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACrD;AAoWD,wBAAsB,iCAAiC,CACrD,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,cAAc,EAAE,EAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAChC,OAAO,GAAE,kCAAuC,GAC/C,OAAO,CAAC,mBAAmB,CAAC,CAmH9B;AA6DD,wBAAsB,+BAA+B,CACnD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,0BAA0B,CAAC,CA0BrC;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAE5E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,GAAG;IACnE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAEA;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,mBAAmB,GACxB,wBAAwB,EAAE,CAM5B;AAED,eAAO,MAAM,8BAA8B,EAAE,wBAAwB,CACnE,mBAAmB,EACnB,sBAAsB,CAAC,kCAAkC,CAAC,CAY3D,CAAC"}