@open-agent-toolkit/cli 0.2.49 → 0.2.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/tool-packs.md +35 -24
  3. package/assets/docs/reference/cli-reference.md +20 -5
  4. package/assets/docs/reference/file-locations.md +8 -2
  5. package/assets/docs/reference/oat-directory-structure.md +14 -5
  6. package/assets/docs/reference/troubleshooting.md +22 -4
  7. package/assets/docs/workflows/projects/artifacts.md +9 -4
  8. package/assets/docs/workflows/projects/lifecycle.md +17 -5
  9. package/assets/docs/workflows/projects/picking-up-projects.md +26 -9
  10. package/assets/docs/workflows/projects/pr-flow.md +5 -3
  11. package/assets/docs/workflows/projects/reviewing-oat-prs.md +12 -8
  12. package/assets/public-package-versions.json +4 -4
  13. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +2 -2
  14. package/assets/skills/oat-project-complete/SKILL.md +193 -26
  15. package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +306 -0
  16. package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +125 -0
  17. package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +106 -0
  18. package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +110 -0
  19. package/assets/skills/oat-project-complete/scripts/resolve-synced-archive-entry.mjs +164 -0
  20. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +2 -2
  21. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +2 -2
  22. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +2 -2
  23. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +2 -2
  24. package/dist/commands/doctor/index.d.ts +7 -0
  25. package/dist/commands/doctor/index.d.ts.map +1 -1
  26. package/dist/commands/doctor/index.js +44 -23
  27. package/dist/commands/pjm/index.d.ts +0 -2
  28. package/dist/commands/pjm/index.d.ts.map +1 -1
  29. package/dist/commands/pjm/index.js +5 -4
  30. package/dist/commands/pjm/migrate.d.ts +2 -1
  31. package/dist/commands/pjm/migrate.d.ts.map +1 -1
  32. package/dist/commands/pjm/migrate.js +23 -17
  33. package/dist/commands/project/archive/archive-utils.d.ts +8 -1
  34. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  35. package/dist/commands/project/archive/archive-utils.js +147 -50
  36. package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
  37. package/dist/commands/project/archive/push-runner.js +15 -2
  38. package/dist/commands/project/links/compute.d.ts +1 -0
  39. package/dist/commands/project/links/compute.d.ts.map +1 -1
  40. package/dist/commands/project/links/compute.js +5 -4
  41. package/dist/commands/project/links/index.d.ts +9 -0
  42. package/dist/commands/project/links/index.d.ts.map +1 -1
  43. package/dist/commands/project/links/index.js +71 -2
  44. package/dist/commands/project/list.d.ts +10 -2
  45. package/dist/commands/project/list.d.ts.map +1 -1
  46. package/dist/commands/project/list.js +205 -29
  47. package/dist/commands/project/open/index.d.ts +2 -0
  48. package/dist/commands/project/open/index.d.ts.map +1 -1
  49. package/dist/commands/project/open/index.js +16 -1
  50. package/dist/commands/project/prune/index.d.ts +10 -4
  51. package/dist/commands/project/prune/index.d.ts.map +1 -1
  52. package/dist/commands/project/prune/index.js +85 -15
  53. package/dist/commands/project/pull/index.d.ts +4 -0
  54. package/dist/commands/project/pull/index.d.ts.map +1 -1
  55. package/dist/commands/project/pull/index.js +82 -3
  56. package/dist/commands/project/sync/git.d.ts +9 -0
  57. package/dist/commands/project/sync/git.d.ts.map +1 -1
  58. package/dist/commands/project/sync/git.js +18 -0
  59. package/dist/commands/project/sync/ref-sync.d.ts +16 -1
  60. package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
  61. package/dist/commands/project/sync/ref-sync.js +288 -17
  62. package/dist/commands/project/sync/resolve-target.d.ts +10 -0
  63. package/dist/commands/project/sync/resolve-target.d.ts.map +1 -1
  64. package/dist/commands/project/sync/resolve-target.js +56 -1
  65. package/dist/commands/shared/project-scope.d.ts +2 -0
  66. package/dist/commands/shared/project-scope.d.ts.map +1 -1
  67. package/dist/commands/shared/project-scope.js +5 -0
  68. package/dist/commands/state/generate.d.ts +2 -0
  69. package/dist/commands/state/generate.d.ts.map +1 -1
  70. package/dist/commands/state/generate.js +93 -28
  71. package/dist/commands/status/index.d.ts +3 -1
  72. package/dist/commands/status/index.d.ts.map +1 -1
  73. package/dist/commands/status/index.js +77 -10
  74. package/dist/commands/tools/shared/pack-inventory.d.ts +10 -0
  75. package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
  76. package/dist/commands/tools/shared/pack-inventory.js +71 -21
  77. package/package.json +2 -2
@@ -2,11 +2,12 @@ import { lstat, mkdir, readFile, readdir, realpath, rm, stat, writeFile, } from
2
2
  import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
3
3
  import { ensureScopedRootGitignore } from '../../init/gitignore.js';
4
4
  import { getFrontmatterBlock } from '../../shared/frontmatter.js';
5
- import { canonicalizePath, isSyncedCheckout, resolveScopeRoot, SYNCED_REMOTE, syncedRefName, syncedRecordPath, } from '../../shared/project-scope.js';
5
+ import { canonicalizePath, completedSyncedRefName, isSyncedCheckout, resolveScopeRoot, SYNCED_REMOTE, syncedRefName, syncedRecordPath, } from '../../shared/project-scope.js';
6
6
  import { readOatLocalConfig, writeOatLocalConfig } from '../../../config/oat-config.js';
7
7
  import { CliError } from '../../../errors/cli-error.js';
8
8
  import { copyDirectory } from '../../../fs/io.js';
9
9
  import YAML from 'yaml';
10
+ import { isAtomicPushUnsupported, pushAtomicRefTransition, } from './git.js';
10
11
  import { buildSyncedRecord, readSyncedRecord, writeSyncedRecord, } from './record.js';
11
12
  export function classifyRemoteRefLookup(result, remote, ref) {
12
13
  if (result.code === 0)
@@ -442,6 +443,32 @@ async function prepareAdoptionRecord(target, result, adoptionRecord, now, additi
442
443
  pendingRecordPaths: [recordPath, ...additionalPendingPaths],
443
444
  };
444
445
  }
446
+ async function probeChildRemoteRefs(target, git) {
447
+ const completedRef = completedSyncedRefName(target.slug);
448
+ const lookup = await git.run(['ls-remote', '--exit-code', target.remote, target.ref, completedRef], { cwd: target.repoRoot, allowFailure: true });
449
+ if (classifyRemoteRefLookup(lookup, target.remote, `${target.ref} and ${completedRef}`) === 'absent') {
450
+ return { activeSha: null, completedSha: null };
451
+ }
452
+ const expectedRefs = new Set([target.ref, completedRef]);
453
+ const advertised = new Map();
454
+ for (const row of lookup.stdout.split('\n').filter(Boolean)) {
455
+ const fields = row.trim().split(/\s+/);
456
+ if (fields.length !== 2 ||
457
+ !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(fields[0] ?? '') ||
458
+ !expectedRefs.has(fields[1] ?? '') ||
459
+ advertised.has(fields[1] ?? '')) {
460
+ throw new CliError(`Unable to verify terminal refs for child ${target.slug}: origin returned a malformed ref advertisement.`, 2);
461
+ }
462
+ advertised.set(fields[1], fields[0]);
463
+ }
464
+ if (advertised.size === 0) {
465
+ throw new CliError(`Unable to verify terminal refs for child ${target.slug}: origin returned a malformed empty advertisement.`, 2);
466
+ }
467
+ return {
468
+ activeSha: advertised.get(target.ref) ?? null,
469
+ completedSha: advertised.get(completedRef) ?? null,
470
+ };
471
+ }
445
472
  export async function pullChildren(parentTarget, git) {
446
473
  await assertCanonicalSyncTargetIdentity(parentTarget);
447
474
  const statePath = resolve(parentTarget.projectPath, 'state.md');
@@ -477,11 +504,9 @@ export async function pullChildren(parentTarget, git) {
477
504
  };
478
505
  try {
479
506
  await assertCanonicalSyncTargetIdentity(childTarget);
480
- const adoptionRecord = await classifyAdoptionRecord(childTarget, git);
481
- const remote = await git.run(['ls-remote', '--exit-code', childTarget.remote, childTarget.ref], { cwd: parentTarget.repoRoot, allowFailure: true });
482
- if (remote.code === 2 &&
483
- remote.stdout.trim() === '' &&
484
- remote.stderr.trim() === '') {
507
+ const completedRef = completedSyncedRefName(slug);
508
+ const { activeSha, completedSha } = await probeChildRemoteRefs(childTarget, git);
509
+ if (activeSha === null && completedSha === null) {
485
510
  results.push({
486
511
  slug,
487
512
  status: 'missing',
@@ -490,15 +515,27 @@ export async function pullChildren(parentTarget, git) {
490
515
  });
491
516
  continue;
492
517
  }
493
- if (remote.code !== 0) {
518
+ if (activeSha !== null &&
519
+ completedSha !== null &&
520
+ activeSha !== completedSha) {
521
+ results.push({
522
+ slug,
523
+ status: 'error',
524
+ message: `Invalid terminal refs for child ${slug}: active ${childTarget.ref} is ${activeSha}, completed ${completedRef} is ${completedSha}. Repair the ref mismatch before retrying; the child was not pulled.`,
525
+ exitCode: 1,
526
+ });
527
+ continue;
528
+ }
529
+ if (completedSha !== null) {
494
530
  results.push({
495
531
  slug,
496
532
  status: 'error',
497
- message: `git ls-remote ${childTarget.remote} ${childTarget.ref} failed (exit ${remote.code}): ${remote.stderr || remote.stdout || 'unknown Git error'}`,
498
- exitCode: 2,
533
+ message: `Synced child ${slug} is already archived at ${completedRef}@${completedSha} and will not be pulled.${activeSha === completedSha ? ` The matching active ref ${childTarget.ref} is only an inert alias.` : ''}`,
534
+ exitCode: 1,
499
535
  });
500
536
  continue;
501
537
  }
538
+ const adoptionRecord = await classifyAdoptionRecord(childTarget, git);
502
539
  const result = await pullSynced(childTarget, git, {
503
540
  adopt: adoptionRecord !== 'durable',
504
541
  adoptionRecord,
@@ -613,6 +650,216 @@ async function removePreflightedSyncedCheckout(target, git, force) {
613
650
  await git.run(['worktree', 'prune'], { cwd: target.repoRoot });
614
651
  return { status: 'removed' };
615
652
  }
653
+ async function probeTerminalRefs(target, expectedSha, git) {
654
+ const { probeSyncedTerminalRefs } = await import('./resolve-target.js');
655
+ return probeSyncedTerminalRefs(target, expectedSha, git);
656
+ }
657
+ function assertRetirementProbe(target, expectedSha, probe) {
658
+ if (probe.state === 'wrong-sha') {
659
+ if (probe.completedSha !== null && probe.completedSha !== expectedSha) {
660
+ throw new CliError(`Refusing to retire ${target.slug}: completed ref ${probe.completedRef} points to a different SHA (${probe.completedSha}) than the bound source SHA ${expectedSha}.`, 2);
661
+ }
662
+ throw new CliError(`Refusing to retire ${target.slug}: active source ref ${probe.activeRef} points to ${probe.activeSha ?? 'an unknown SHA'}, not the bound source SHA ${expectedSha}.`, 2);
663
+ }
664
+ if (probe.state === 'absent') {
665
+ throw new CliError(`Refusing to retire ${target.slug}: source ref ${probe.activeRef} is missing and completed ref ${probe.completedRef} does not preserve ${expectedSha}.`, 2);
666
+ }
667
+ }
668
+ function isTerminalRetirementProbe(probe) {
669
+ return probe.state === 'completed-only' || probe.state === 'both';
670
+ }
671
+ async function fallbackRemoteRefTransition(target, sourceSha, completedRef, completedExpectedSha, git) {
672
+ const create = await git.run([
673
+ 'push',
674
+ `--force-with-lease=${completedRef}:${completedExpectedSha ?? ''}`,
675
+ target.remote,
676
+ `${sourceSha}:${completedRef}`,
677
+ ], { cwd: target.repoRoot, allowFailure: true });
678
+ if (create.code !== 0) {
679
+ const afterFailure = await probeTerminalRefs(target, sourceSha, git);
680
+ assertRetirementProbe(target, sourceSha, afterFailure);
681
+ if (isTerminalRetirementProbe(afterFailure))
682
+ return;
683
+ throw new CliError(`Remote ref retirement for ${target.slug} was rejected while creating ${completedRef}: ${create.stderr || create.stdout || 'unknown Git error'}`, 2);
684
+ }
685
+ const afterCreate = await probeTerminalRefs(target, sourceSha, git);
686
+ assertRetirementProbe(target, sourceSha, afterCreate);
687
+ if (!isTerminalRetirementProbe(afterCreate)) {
688
+ throw new CliError(`Remote ref retirement for ${target.slug} did not preserve both refs after completed-ref creation.`, 2);
689
+ }
690
+ }
691
+ async function transitionRemoteRefs(target, sourceSha, completedRef, completedExpectedSha, git) {
692
+ const result = await pushAtomicRefTransition(git, target.repoRoot, {
693
+ remote: target.remote,
694
+ sourceSha,
695
+ activeRef: target.ref,
696
+ completedRef,
697
+ completedExpectedSha,
698
+ });
699
+ if (result.code === 0)
700
+ return;
701
+ if (isAtomicPushUnsupported(result)) {
702
+ await fallbackRemoteRefTransition(target, sourceSha, completedRef, completedExpectedSha, git);
703
+ return;
704
+ }
705
+ const afterFailure = await probeTerminalRefs(target, sourceSha, git);
706
+ assertRetirementProbe(target, sourceSha, afterFailure);
707
+ if (!isTerminalRetirementProbe(afterFailure)) {
708
+ throw new CliError(`Remote ref retirement for ${target.slug} was rejected; active ref ${target.ref} was retained: ${result.stderr || result.stdout || 'unknown Git error'}`, 2);
709
+ }
710
+ }
711
+ async function localRefSha(target, ref, git) {
712
+ const result = await git.run(['show-ref', '--hash', ref], {
713
+ cwd: target.repoRoot,
714
+ allowFailure: true,
715
+ });
716
+ assertExpectedGitResult(`git show-ref --hash ${ref}`, result, [0, 1]);
717
+ return result.code === 0 ? result.stdout : null;
718
+ }
719
+ async function reconcileLocalTerminalRefs(target, sourceSha, completedRef, git) {
720
+ const [activeSha, completedSha] = await Promise.all([
721
+ localRefSha(target, target.ref, git),
722
+ localRefSha(target, completedRef, git),
723
+ ]);
724
+ if (activeSha !== null && activeSha !== sourceSha) {
725
+ throw new CliError(`Refusing local ref cleanup for ${target.slug}: ${target.ref} points to ${activeSha}, not ${sourceSha}.`, 2);
726
+ }
727
+ if (completedSha !== null && completedSha !== sourceSha) {
728
+ throw new CliError(`Refusing local ref cleanup for ${target.slug}: ${completedRef} points to ${completedSha}, not ${sourceSha}.`, 2);
729
+ }
730
+ if (completedSha === null) {
731
+ const object = await git.run(['cat-file', '-e', `${sourceSha}^{commit}`], {
732
+ cwd: target.repoRoot,
733
+ allowFailure: true,
734
+ });
735
+ if (object.code !== 0) {
736
+ await git.run([
737
+ 'fetch',
738
+ '--no-tags',
739
+ '--no-write-fetch-head',
740
+ target.remote,
741
+ completedRef,
742
+ ], { cwd: target.repoRoot });
743
+ const afterFetch = await probeTerminalRefs(target, sourceSha, git);
744
+ assertRetirementProbe(target, sourceSha, afterFetch);
745
+ if (!isTerminalRetirementProbe(afterFetch)) {
746
+ throw new CliError(`Refusing local ref reconciliation for ${target.slug}: remote terminal refs changed while fetching ${completedRef}.`, 2);
747
+ }
748
+ await git.run(['cat-file', '-e', `${sourceSha}^{commit}`], {
749
+ cwd: target.repoRoot,
750
+ });
751
+ }
752
+ await git.run(['update-ref', completedRef, sourceSha], {
753
+ cwd: target.repoRoot,
754
+ });
755
+ }
756
+ if (activeSha !== null) {
757
+ await git.run(['update-ref', '-d', target.ref, sourceSha], {
758
+ cwd: target.repoRoot,
759
+ });
760
+ }
761
+ const [finalActiveSha, finalCompletedSha] = await Promise.all([
762
+ localRefSha(target, target.ref, git),
763
+ localRefSha(target, completedRef, git),
764
+ ]);
765
+ if (finalActiveSha !== null || finalCompletedSha !== sourceSha) {
766
+ throw new CliError(`Unable to verify local terminal refs for ${target.slug} after remote retirement.`, 2);
767
+ }
768
+ }
769
+ export async function retireSyncedRef(target, sourceSha, git) {
770
+ await assertCanonicalSyncTargetIdentity(target);
771
+ const initial = await probeTerminalRefs(target, sourceSha, git);
772
+ assertRetirementProbe(target, sourceSha, initial);
773
+ const completedRef = initial.completedRef;
774
+ if (initial.state === 'active-only') {
775
+ await transitionRemoteRefs(target, sourceSha, completedRef, initial.completedSha, git);
776
+ }
777
+ const terminal = await probeTerminalRefs(target, sourceSha, git);
778
+ assertRetirementProbe(target, sourceSha, terminal);
779
+ if (!isTerminalRetirementProbe(terminal)) {
780
+ throw new CliError(`Unable to verify a completed terminal state for ${target.slug}; completed ref ${completedRef} was not safely established.`, 2);
781
+ }
782
+ await reconcileLocalTerminalRefs(target, sourceSha, completedRef, git);
783
+ const activeAliasRetained = terminal.state === 'both';
784
+ return {
785
+ status: isTerminalRetirementProbe(initial) ? 'already-retired' : 'retired',
786
+ state: activeAliasRetained ? 'matching-aliases' : 'completed-only',
787
+ activeAliasRetained,
788
+ activeRef: target.ref,
789
+ completedRef,
790
+ verifiedSha: sourceSha,
791
+ };
792
+ }
793
+ function exactRemoteRefSha(result, remote, ref) {
794
+ if (classifyRemoteRefLookup(result, remote, ref) === 'absent')
795
+ return null;
796
+ const rows = result.stdout.split('\n').filter((row) => row.length > 0);
797
+ if (rows.length !== 1) {
798
+ throw new CliError(`Unable to verify ${remote}/${ref}: git ls-remote returned an unexpected number of exact-ref rows.`, 2);
799
+ }
800
+ const fields = rows[0]?.trim().split(/\s+/) ?? [];
801
+ if (fields.length !== 2 ||
802
+ fields[1] !== ref ||
803
+ !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(fields[0] ?? '')) {
804
+ throw new CliError(`Unable to verify ${remote}/${ref}: git ls-remote returned a malformed or unexpected row.`, 2);
805
+ }
806
+ const [sha] = fields;
807
+ return sha;
808
+ }
809
+ export async function deleteCompletedSyncedRefForPrune(target, git) {
810
+ await assertCanonicalSyncTargetIdentity(target);
811
+ const completedRef = completedSyncedRefName(target.slug);
812
+ const [remote, localSha] = await Promise.all([
813
+ git.run(['ls-remote', '--exit-code', target.remote, completedRef], {
814
+ cwd: target.repoRoot,
815
+ allowFailure: true,
816
+ }),
817
+ localRefSha(target, completedRef, git),
818
+ ]);
819
+ const remoteSha = exactRemoteRefSha(remote, target.remote, completedRef);
820
+ if (remoteSha !== null) {
821
+ const deletion = await git.run([
822
+ 'push',
823
+ `--force-with-lease=${completedRef}:${remoteSha}`,
824
+ target.remote,
825
+ `:${completedRef}`,
826
+ ], { cwd: target.repoRoot, allowFailure: true });
827
+ if (deletion.code !== 0) {
828
+ const afterRejection = exactRemoteRefSha(await git.run(['ls-remote', '--exit-code', target.remote, completedRef], { cwd: target.repoRoot, allowFailure: true }), target.remote, completedRef);
829
+ if (afterRejection !== null) {
830
+ if (afterRejection !== remoteSha) {
831
+ throw new CliError(`Explicit prune stopped because remote completed ref ${completedRef} advanced from observed ${remoteSha} to ${afterRejection}; the advanced ref was retained. Review the new terminal identity and retry the destructive prune.`, 2);
832
+ }
833
+ throw new CliError(`Explicit prune could not delete retained terminal history at ${completedRef}; the local completed ref was retained. Restore access to ${target.remote} and retry the destructive prune: ${deletion.stderr || deletion.stdout || 'unknown Git error'}`, 2);
834
+ }
835
+ }
836
+ else {
837
+ const verifiedSha = exactRemoteRefSha(await git.run(['ls-remote', '--exit-code', target.remote, completedRef], { cwd: target.repoRoot, allowFailure: true }), target.remote, completedRef);
838
+ if (verifiedSha !== null) {
839
+ if (verifiedSha !== remoteSha) {
840
+ throw new CliError(`Explicit prune stopped because remote completed ref ${completedRef} advanced from observed ${remoteSha} to ${verifiedSha}; the advanced ref was retained. Review the new terminal identity and retry the destructive prune.`, 2);
841
+ }
842
+ throw new CliError(`Explicit prune could not verify deletion of retained terminal history at ${completedRef}; the local completed ref was retained.`, 2);
843
+ }
844
+ }
845
+ }
846
+ if (localSha !== null) {
847
+ const localDeletion = await git.run(['update-ref', '-d', completedRef, localSha], { cwd: target.repoRoot, allowFailure: true });
848
+ if (localDeletion.code !== 0) {
849
+ const currentLocalSha = await localRefSha(target, completedRef, git);
850
+ if (currentLocalSha !== null) {
851
+ if (currentLocalSha !== localSha) {
852
+ throw new CliError(`Explicit prune stopped because local completed ref ${completedRef} advanced from observed ${localSha} to ${currentLocalSha}; the advanced ref was retained. Review the new terminal identity and retry the destructive prune.`, 2);
853
+ }
854
+ throw new CliError(`Explicit prune could not delete local completed ref ${completedRef} at observed SHA ${localSha}; the ref was retained. Retry after resolving the local Git error: ${localDeletion.stderr || localDeletion.stdout || 'unknown Git error'}`, 2);
855
+ }
856
+ }
857
+ }
858
+ return {
859
+ completedRef,
860
+ deleted: remoteSha !== null || localSha !== null,
861
+ };
862
+ }
616
863
  export async function pruneSynced(target, git, options) {
617
864
  await assertCanonicalSyncTargetIdentity(target);
618
865
  const scopeRelativeSyncedRoot = relative(resolve(target.repoRoot), resolve(target.syncedRoot));
@@ -678,15 +925,39 @@ export async function pruneSynced(target, git, options) {
678
925
  throw new CliError(`Refusing to prune ${target.slug}: checkout ${checkout.projectPath} is ${preflight.status}. Push it first or pass --force. Pinned PR links will stop resolving after prune.`, 1);
679
926
  }
680
927
  }
681
- try {
682
- await git.run(['push', target.remote, `:${target.ref}`], {
683
- cwd: target.repoRoot,
684
- });
928
+ if (options.expectedActiveAliasSha) {
929
+ const expectedSha = options.expectedActiveAliasSha;
930
+ const deletion = await git.run([
931
+ 'push',
932
+ `--force-with-lease=${target.ref}:${expectedSha}`,
933
+ target.remote,
934
+ `:${target.ref}`,
935
+ ], { cwd: target.repoRoot, allowFailure: true });
936
+ const afterDeletion = await probeTerminalRefs(target, expectedSha, git);
937
+ if (afterDeletion.activeSha === null &&
938
+ afterDeletion.completedSha === expectedSha) {
939
+ // A rejected transport may still have applied the deletion. The
940
+ // authoritative postcondition is safe, so cleanup may continue.
941
+ }
942
+ else if (afterDeletion.activeSha !== null &&
943
+ afterDeletion.activeSha !== expectedSha) {
944
+ throw new CliError(`Explicit prune stopped because active alias ${target.ref} advanced from observed ${expectedSha} to ${afterDeletion.activeSha}; the advanced active ref, all checkouts, the discovery record, and completed history were retained. Reconcile the terminal ref mismatch before retrying.`, 1);
945
+ }
946
+ else {
947
+ throw new CliError(`Unable to safely delete matching active alias ${target.ref} at ${expectedSha}; no local checkouts or discovery records were removed and completed history was retained. Restore access to ${target.remote}, verify both terminal refs, and retry explicit prune: ${deletion.stderr || deletion.stdout || 'terminal postcondition was not established'}`, 2);
948
+ }
685
949
  }
686
- catch (error) {
687
- const detail = error instanceof Error ? error.message : String(error);
688
- const forceFlag = options.force ? ' --force' : '';
689
- throw new CliError(`Unable to delete remote ref ${target.ref}; no local checkouts were removed. Restore access to ${target.remote}, then retry oat project prune ${shellQuote(target.slug)}${forceFlag}. ${detail}`, 2);
950
+ else {
951
+ try {
952
+ await git.run(['push', target.remote, `:${target.ref}`], {
953
+ cwd: target.repoRoot,
954
+ });
955
+ }
956
+ catch (error) {
957
+ const detail = error instanceof Error ? error.message : String(error);
958
+ const forceFlag = options.force ? ' --force' : '';
959
+ throw new CliError(`Unable to delete remote ref ${target.ref}; no local checkouts were removed. Restore access to ${target.remote}, then retry oat project prune ${shellQuote(target.slug)}${forceFlag}. ${detail}`, 2);
960
+ }
690
961
  }
691
962
  try {
692
963
  for (const checkout of checkouts) {
@@ -23,5 +23,15 @@ export interface ResolvedSyncTarget extends SyncTarget {
23
23
  adopt: boolean;
24
24
  adoptionRecord: AdoptionRecordState;
25
25
  }
26
+ export type SyncedTerminalRefState = 'absent' | 'active-only' | 'completed-only' | 'both' | 'wrong-sha';
27
+ export interface SyncedTerminalRefProbe {
28
+ state: SyncedTerminalRefState;
29
+ activeRef: string;
30
+ completedRef: string;
31
+ expectedSha: string;
32
+ activeSha: string | null;
33
+ completedSha: string | null;
34
+ }
35
+ export declare function probeSyncedTerminalRefs(target: SyncTarget, expectedSha: string, git: GitRunner): Promise<SyncedTerminalRefProbe>;
26
36
  export declare function resolveSyncedTarget(context: ResolveSyncedTargetContext, pathOrSlug?: string, overrides?: Partial<ResolveSyncedTargetDependencies>, options?: ResolveSyncedTargetOptions): Promise<ResolvedSyncTarget>;
27
37
  //# sourceMappingURL=resolve-target.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-target.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/sync/resolve-target.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAQ,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAOjE,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAG7E,OAAO,EAIL,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,+BAA+B;IAC9C,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,mBAAmB,CAAC;CACrC;AAsFD,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,EACnC,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,GAAE,OAAO,CAAC,+BAA+B,CAAM,EACxD,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,kBAAkB,CAAC,CA+H7B"}
1
+ {"version":3,"file":"resolve-target.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/sync/resolve-target.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAQ,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAQjE,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAG7E,OAAO,EAIL,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,+BAA+B;IAC9C,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,mBAAmB,CAAC;CACrC;AAED,MAAM,MAAM,sBAAsB,GAC9B,QAAQ,GACR,aAAa,GACb,gBAAgB,GAChB,MAAM,GACN,WAAW,CAAC;AAEhB,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,sBAAsB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AA+DD,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,sBAAsB,CAAC,CA+BjC;AAsFD,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,EACnC,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,GAAE,OAAO,CAAC,+BAA+B,CAAM,EACxD,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,kBAAkB,CAAC,CA+H7B"}
@@ -2,10 +2,65 @@ import { realpath, stat } from 'node:fs/promises';
2
2
  import { isAbsolute, relative, resolve, sep } from 'node:path';
3
3
  import { defaultGitRunner } from '../../project/sync/git.js';
4
4
  import { resolveProjectsRoot } from '../../shared/oat-paths.js';
5
- import { assertValidProjectSlug, resolveProjectScope, resolveScopeRoot, syncedRecordPath, } from '../../shared/project-scope.js';
5
+ import { assertValidProjectSlug, completedSyncedRefName, resolveProjectScope, resolveScopeRoot, syncedRecordPath, } from '../../shared/project-scope.js';
6
6
  import { readOatLocalConfig } from '../../../config/oat-config.js';
7
7
  import { CliError } from '../../../errors/cli-error.js';
8
8
  import { assertCanonicalSyncTargetIdentity, buildSyncTarget, classifyRemoteRefLookup, classifyAdoptionRecord, } from './ref-sync.js';
9
+ async function remoteTerminalRefShas(target, activeRef, completedRef, git) {
10
+ const result = await git.run(['ls-remote', '--exit-code', target.remote, activeRef, completedRef], { cwd: target.repoRoot, allowFailure: true });
11
+ if (classifyRemoteRefLookup(result, target.remote, `${activeRef} and ${completedRef}`) === 'absent') {
12
+ return { activeSha: null, completedSha: null };
13
+ }
14
+ const requestedRefs = new Set([activeRef, completedRef]);
15
+ const advertised = new Map();
16
+ const rows = result.stdout.split('\n').filter((row) => row.length > 0);
17
+ if (rows.length === 0) {
18
+ throw new CliError(`Unable to verify terminal refs for ${target.slug}: git ls-remote returned a malformed empty advertisement.`, 2);
19
+ }
20
+ for (const row of rows) {
21
+ const fields = row.trim().split(/\s+/);
22
+ if (fields.length !== 2 ||
23
+ !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(fields[0] ?? '')) {
24
+ throw new CliError(`Unable to verify terminal refs for ${target.slug}: git ls-remote returned a malformed row.`, 2);
25
+ }
26
+ const [sha, ref] = fields;
27
+ if (!requestedRefs.has(ref)) {
28
+ throw new CliError(`Unable to verify terminal refs for ${target.slug}: git ls-remote returned unexpected ref ${ref}.`, 2);
29
+ }
30
+ if (advertised.has(ref)) {
31
+ throw new CliError(`Unable to verify terminal refs for ${target.slug}: git ls-remote returned duplicate rows for ${ref}.`, 2);
32
+ }
33
+ advertised.set(ref, sha);
34
+ }
35
+ return {
36
+ activeSha: advertised.get(activeRef) ?? null,
37
+ completedSha: advertised.get(completedRef) ?? null,
38
+ };
39
+ }
40
+ export async function probeSyncedTerminalRefs(target, expectedSha, git) {
41
+ const activeRef = target.ref;
42
+ const completedRef = completedSyncedRefName(target.slug);
43
+ const { activeSha, completedSha } = await remoteTerminalRefShas(target, activeRef, completedRef, git);
44
+ const wrongSha = (activeSha !== null && activeSha !== expectedSha) ||
45
+ (completedSha !== null && completedSha !== expectedSha);
46
+ const state = wrongSha
47
+ ? 'wrong-sha'
48
+ : activeSha !== null && completedSha !== null
49
+ ? 'both'
50
+ : activeSha !== null
51
+ ? 'active-only'
52
+ : completedSha !== null
53
+ ? 'completed-only'
54
+ : 'absent';
55
+ return {
56
+ state,
57
+ activeRef,
58
+ completedRef,
59
+ expectedSha,
60
+ activeSha,
61
+ completedSha,
62
+ };
63
+ }
9
64
  async function pathExists(path) {
10
65
  try {
11
66
  await stat(path);
@@ -1,6 +1,7 @@
1
1
  export type ProjectScope = 'shared' | 'local' | 'synced';
2
2
  export declare const PROJECT_SCOPES: readonly ["shared", "local", "synced"];
3
3
  export declare const SYNCED_REF_NAMESPACE = "refs/oat/projects";
4
+ export declare const COMPLETED_SYNCED_REF_NAMESPACE = "refs/oat/completed";
4
5
  export declare const SYNCED_REMOTE = "origin";
5
6
  export declare function canonicalizePath(path: string): string;
6
7
  interface EffectiveConfigResult {
@@ -16,6 +17,7 @@ export declare function resolveScopeRoot(repoRoot: string, projectsRoot: string,
16
17
  export declare function resolveProjectScope(projectPath: string, projectsRoot: string, repoRoot: string): ProjectScope | null;
17
18
  export declare function assertValidProjectSlug(slug: string, exitCode?: 1 | 2): void;
18
19
  export declare function syncedRefName(slug: string): string;
20
+ export declare function completedSyncedRefName(slug: string): string;
19
21
  export declare function syncedRecordPath(scopeRoot: string, slug: string): string;
20
22
  export declare function isSyncedCheckout(projectPath: string): Promise<boolean>;
21
23
  export declare function resolveDefaultScope(repoRoot: string, env: NodeJS.ProcessEnv, dependencies?: Partial<ProjectScopeDependencies>): Promise<ProjectScope>;
@@ -1 +1 @@
1
- {"version":3,"file":"project-scope.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/project-scope.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEzD,eAAO,MAAM,cAAc,wCAIiB,CAAC;AAC7C,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AACxD,eAAO,MAAM,aAAa,WAAW,CAAC;AAItC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBrD;AAED,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9C;AAED,UAAU,wBAAwB;IAChC,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACrC;AAMD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,CAKR;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,YAAY,GAClB,MAAM,CAOR;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,YAAY,GAAG,IAAI,CAmBrB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,CAAC,GAAG,CAAK,GAClB,IAAI,CAON;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa5E;AAeD,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,YAAY,GAAE,OAAO,CAAC,wBAAwB,CAAM,GACnD,OAAO,CAAC,YAAY,CAAC,CAqBvB"}
1
+ {"version":3,"file":"project-scope.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/project-scope.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEzD,eAAO,MAAM,cAAc,wCAIiB,CAAC;AAC7C,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AACxD,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,aAAa,WAAW,CAAC;AAItC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBrD;AAED,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9C;AAED,UAAU,wBAAwB;IAChC,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACrC;AAMD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,CAKR;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,YAAY,GAClB,MAAM,CAOR;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,YAAY,GAAG,IAAI,CAmBrB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,CAAC,GAAG,CAAK,GAClB,IAAI,CAON;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa5E;AAeD,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,YAAY,GAAE,OAAO,CAAC,wBAAwB,CAAM,GACnD,OAAO,CAAC,YAAY,CAAC,CAqBvB"}
@@ -10,6 +10,7 @@ export const PROJECT_SCOPES = [
10
10
  'synced',
11
11
  ];
12
12
  export const SYNCED_REF_NAMESPACE = 'refs/oat/projects';
13
+ export const COMPLETED_SYNCED_REF_NAMESPACE = 'refs/oat/completed';
13
14
  export const SYNCED_REMOTE = 'origin';
14
15
  const PROJECT_SLUG_PATTERN = /^[a-zA-Z0-9_-]+$/;
15
16
  export function canonicalizePath(path) {
@@ -69,6 +70,10 @@ export function syncedRefName(slug) {
69
70
  assertValidProjectSlug(slug);
70
71
  return `${SYNCED_REF_NAMESPACE}/${slug}`;
71
72
  }
73
+ export function completedSyncedRefName(slug) {
74
+ assertValidProjectSlug(slug);
75
+ return `${COMPLETED_SYNCED_REF_NAMESPACE}/${slug}`;
76
+ }
72
77
  export function syncedRecordPath(scopeRoot, slug) {
73
78
  assertValidProjectSlug(slug);
74
79
  return join(scopeRoot, `${slug}.json`);
@@ -1,3 +1,4 @@
1
+ import { type GitRunner } from '../project/sync/git.js';
1
2
  export interface GitOperations {
2
3
  isGitRepo(root: string): boolean;
3
4
  diffFileCount(root: string, sha: string): number;
@@ -7,6 +8,7 @@ export interface GenerateStateOptions {
7
8
  env?: NodeJS.ProcessEnv;
8
9
  today?: string;
9
10
  git?: GitOperations;
11
+ gitRunner?: GitRunner;
10
12
  }
11
13
  export interface GenerateStateResult {
12
14
  dashboardPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAiHD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvE;AAydD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CA2C9B"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAU9E,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAiHD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvE;AAsgBD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAkF9B"}