@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
@@ -7,7 +7,7 @@ import { initializeDecisionRecords } from '../decision/init.js';
7
7
  import { migrateDecisionRecords, } from '../decision/migrate.js';
8
8
  import { stripTemplateFrontmatter } from '../shared/strip-template-frontmatter.js';
9
9
  import YAML from 'yaml';
10
- import { initializeRepoReference } from './init.js';
10
+ import { CANONICAL_REPO_REFERENCE_PATHS, initializeRepoReference, } from './init.js';
11
11
  const ACTIVE_MOVES = [
12
12
  { source: 'reference/current-state.md', target: 'pjm/current-state.md' },
13
13
  { source: 'reference/roadmap.md', target: 'pjm/roadmap.md' },
@@ -17,6 +17,10 @@ const JUDGMENT_TARGETS = [
17
17
  'reference/backlog.md',
18
18
  'reference/backlog-completed.md',
19
19
  ];
20
+ const LEGACY_MIGRATION_PATHS = [
21
+ ...ACTIVE_MOVES.map((move) => move.source),
22
+ 'reference/decision-record.md',
23
+ ];
20
24
  async function pathExists(path) {
21
25
  try {
22
26
  await access(path);
@@ -228,19 +232,17 @@ async function collectJudgmentProposals(repoRoot) {
228
232
  }
229
233
  return actions;
230
234
  }
231
- async function isAlreadyMigrated(repoRoot) {
232
- const hasPjm = await pathExists(join(repoRoot, 'pjm'));
233
- const hasDecisions = await pathExists(join(repoRoot, 'reference', 'decisions'));
234
- if (!hasPjm || !hasDecisions) {
235
- return false;
236
- }
237
- for (const legacyPath of [
238
- 'reference/current-state.md',
239
- 'reference/roadmap.md',
240
- 'reference/backlog',
241
- 'reference/decision-record.md',
242
- ]) {
235
+ async function hasRecognizedLegacySources(repoRoot) {
236
+ for (const legacyPath of LEGACY_MIGRATION_PATHS) {
243
237
  if (await pathExists(join(repoRoot, legacyPath))) {
238
+ return true;
239
+ }
240
+ }
241
+ return false;
242
+ }
243
+ async function isAlreadyMigrated(repoRoot) {
244
+ for (const relativePath of CANONICAL_REPO_REFERENCE_PATHS) {
245
+ if (!(await pathExists(join(repoRoot, relativePath)))) {
244
246
  return false;
245
247
  }
246
248
  }
@@ -305,12 +307,16 @@ function buildEmptyResult(options, status, reason) {
305
307
  };
306
308
  }
307
309
  export async function migratePjmRepo(options) {
308
- if (!options.projectManagementEnabled) {
309
- return buildEmptyResult(options, 'skipped', 'project-management pack is disabled');
310
- }
311
- if (await isAlreadyMigrated(options.repoRoot)) {
310
+ const hasLegacySources = await hasRecognizedLegacySources(options.repoRoot);
311
+ if (!hasLegacySources && (await isAlreadyMigrated(options.repoRoot))) {
312
312
  return buildEmptyResult(options, 'already-migrated');
313
313
  }
314
+ if (!hasLegacySources) {
315
+ const reason = options.adoption.state === 'partial-initialization'
316
+ ? 'PJM initialization is partial and no recognized legacy migration input was found; run oat pjm init to complete initialization'
317
+ : 'No recognized PJM migration input was found; run oat pjm init to initialize this repository';
318
+ return buildEmptyResult(options, 'skipped', reason);
319
+ }
314
320
  const apply = options.apply ?? false;
315
321
  const activeMoves = await planActiveMoves(options.repoRoot);
316
322
  const judgmentProposals = await collectJudgmentProposals(options.repoRoot);
@@ -1,7 +1,8 @@
1
1
  import { rename } from 'node:fs/promises';
2
2
  import { type GitRunner } from '../../project/sync/git.js';
3
3
  import { readSyncedRecord, writeSyncedRecord } from '../../project/sync/record.js';
4
- import { commitRecordChange, preflightSyncedCheckout, removeSyncedCheckout } from '../../project/sync/ref-sync.js';
4
+ import { commitRecordChange, preflightSyncedCheckout, removeSyncedCheckout, retireSyncedRef, type SyncedRefRetirementReceipt } from '../../project/sync/ref-sync.js';
5
+ import { probeSyncedTerminalRefs } from '../../project/sync/resolve-target.js';
5
6
  import { type ProjectScope } from '../../shared/project-scope.js';
6
7
  import { copyDirectory, copySingleFile, dirExists, ensureDir, fileExists } from '../../../fs/io.js';
7
8
  export type ExecFileResult = {
@@ -104,7 +105,11 @@ interface ArchiveProjectOnCompletionDependencies extends EnsureS3ArchiveAccessDe
104
105
  writeSyncedRecord?: typeof writeSyncedRecord;
105
106
  preflightSyncedCheckout?: typeof preflightSyncedCheckout;
106
107
  removeSyncedCheckout?: typeof removeSyncedCheckout;
108
+ retireSyncedRef?: typeof retireSyncedRef;
107
109
  commitRecordChange?: typeof commitRecordChange;
110
+ removeSyncedRecord?: (recordPath: string) => Promise<void>;
111
+ afterLifecycleCommit?: () => Promise<void>;
112
+ probeSyncedTerminalRefs?: typeof probeSyncedTerminalRefs;
108
113
  }
109
114
  export interface ArchiveProjectRecapExportV1 {
110
115
  sourceRunRoot: string;
@@ -124,6 +129,8 @@ export interface ArchiveProjectOnCompletionResult {
124
129
  lifecycleCommit: string | null;
125
130
  recapExportPaths: string[];
126
131
  snapshotId: string;
132
+ terminalReceipt: SyncedRefRetirementReceipt | null;
133
+ recordRetired: boolean;
127
134
  }
128
135
  export declare const ARCHIVE_SNAPSHOT_METADATA_FILENAME = ".oat-archive-source.json";
129
136
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"archive-utils.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/archive/archive-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"archive-utils.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/archive/archive-utils.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,8 @@ import { basename, dirname, isAbsolute, join, posix as path, relative, resolve,
5
5
  import { promisify } from 'node:util';
6
6
  import { defaultGitRunner } from '../../project/sync/git.js';
7
7
  import { readSyncedRecord, writeSyncedRecord, } from '../../project/sync/record.js';
8
- import { buildSyncTarget, commitRecordChange, preflightSyncedCheckout, removeSyncedCheckout, } from '../../project/sync/ref-sync.js';
8
+ import { buildSyncTarget, commitRecordChange, preflightSyncedCheckout, removeSyncedCheckout, retireSyncedRef, } from '../../project/sync/ref-sync.js';
9
+ import { probeSyncedTerminalRefs } from '../../project/sync/resolve-target.js';
9
10
  import { canonicalizePath, resolveProjectScope, resolveScopeRoot, syncedRecordPath, } from '../../shared/project-scope.js';
10
11
  import { CliError } from '../../../errors/cli-error.js';
11
12
  import { copyDirectory, copySingleFile, dirExists, ensureDir, fileExists, } from '../../../fs/io.js';
@@ -356,6 +357,66 @@ async function verifyArchiveSnapshotMetadata(archivePath, expected) {
356
357
  throw new CliError(`Existing archive \`${archivePath}\` does not match persisted snapshot \`${expected.snapshotName}\`; refusing to overwrite it.`);
357
358
  }
358
359
  }
360
+ async function resolveRecordlessSyncedArchiveIdentity(options, target, git, dependencies) {
361
+ const tentativeTarget = await resolveArchiveProjectTarget(options, dependencies);
362
+ assertDurableArchiveProjectTarget(tentativeTarget);
363
+ const archiveRoot = dirname(tentativeTarget.archivePath);
364
+ const candidates = [];
365
+ let entries;
366
+ try {
367
+ entries = await readdir(archiveRoot, { withFileTypes: true });
368
+ }
369
+ catch {
370
+ entries = [];
371
+ }
372
+ for (const entry of entries) {
373
+ if (!entry.isDirectory())
374
+ continue;
375
+ const archivePath = join(archiveRoot, entry.name);
376
+ let metadata;
377
+ try {
378
+ metadata = JSON.parse(await readFile(join(archivePath, ARCHIVE_SNAPSHOT_METADATA_FILENAME), 'utf8'));
379
+ }
380
+ catch {
381
+ continue;
382
+ }
383
+ if (isRecord(metadata) &&
384
+ metadata.projectName === options.projectName &&
385
+ metadata.scope === 'synced' &&
386
+ typeof metadata.snapshotName === 'string' &&
387
+ typeof metadata.sourceRefSha === 'string' &&
388
+ /^[0-9a-f]{40}$/.test(metadata.sourceRefSha)) {
389
+ candidates.push({
390
+ archivePath,
391
+ metadata: {
392
+ projectName: options.projectName,
393
+ snapshotName: metadata.snapshotName,
394
+ scope: 'synced',
395
+ sourceRefSha: metadata.sourceRefSha,
396
+ },
397
+ });
398
+ }
399
+ }
400
+ if (candidates.length === 0) {
401
+ throw new CliError(`Synced project ${options.projectName} has no active record and no persisted synced archive identity; refusing to create a replacement active record or a new snapshot.`, 2);
402
+ }
403
+ const probe = dependencies.probeSyncedTerminalRefs ?? probeSyncedTerminalRefs;
404
+ const matches = [];
405
+ for (const candidate of candidates) {
406
+ const terminal = await probe(target, candidate.metadata.sourceRefSha, git);
407
+ if (terminal.completedSha === candidate.metadata.sourceRefSha &&
408
+ (terminal.activeSha === null ||
409
+ terminal.activeSha === candidate.metadata.sourceRefSha)) {
410
+ matches.push(candidate);
411
+ }
412
+ }
413
+ if (matches.length !== 1) {
414
+ throw new CliError(matches.length === 0
415
+ ? `Synced project ${options.projectName} has no active record, but no persisted archive identity matches its authoritative completed ref; refusing terminal retry before export or S3 mutation.`
416
+ : `Synced project ${options.projectName} has no active record and multiple persisted archives match its authoritative completed ref; refusing an ambiguous terminal retry.`, 2);
417
+ }
418
+ return matches[0];
419
+ }
359
420
  async function exportProjectSummary(archivePath, snapshotName, summaryExportPath, repoRoot, dependencies) {
360
421
  const summarySource = join(archivePath, 'summary.md');
361
422
  const exists = dependencies.fileExists ?? fileExists;
@@ -850,29 +911,42 @@ export async function archiveProjectOnCompletion(options, dependencies = {}) {
850
911
  const syncTarget = isSynced
851
912
  ? buildSyncTarget(options.repoRoot, options.projectsRoot, options.projectName)
852
913
  : null;
853
- const git = dependencies.gitRunner ?? defaultGitRunner;
854
914
  let activeRecord = record;
915
+ const git = dependencies.gitRunner ?? defaultGitRunner;
916
+ const recordlessIdentity = syncTarget && !activeRecord
917
+ ? await resolveRecordlessSyncedArchiveIdentity(options, syncTarget, git, dependencies)
918
+ : null;
855
919
  let sourceRefSha;
856
920
  if (syncTarget) {
857
- if (!activeRecord) {
858
- throw new CliError(`Synced project ${options.projectName} is missing its discovery record; restore the record or run oat project pull ${options.projectName} from a clean checkout to adopt the project before archiving.`, 2);
921
+ if (recordlessIdentity) {
922
+ sourceRefSha = recordlessIdentity.metadata.sourceRefSha;
859
923
  }
860
- const preflight = await (dependencies.preflightSyncedCheckout ?? preflightSyncedCheckout)(syncTarget, git);
861
- if (preflight.status !== 'clean' &&
862
- !(preflight.status === 'absent' && activeRecord.status === 'complete')) {
863
- const recoveryCommand = preflight.status === 'absent'
864
- ? `oat project pull ${options.projectName}`
865
- : `oat project push ${options.projectName}`;
866
- throw new CliError(`Synced project ${options.projectName} is ${preflight.status}; run ${recoveryCommand} before archiving.`, 1);
924
+ else {
925
+ const preflight = await (dependencies.preflightSyncedCheckout ?? preflightSyncedCheckout)(syncTarget, git);
926
+ const isBoundTerminalRetry = Boolean(preflight.sha &&
927
+ activeRecord?.archiveSourceRefSha === preflight.sha &&
928
+ preflight.status === 'unpushed');
929
+ if (preflight.status !== 'clean' &&
930
+ !(preflight.status === 'absent' &&
931
+ (activeRecord?.status === 'complete' ||
932
+ Boolean(activeRecord?.archiveSourceRefSha))) &&
933
+ !isBoundTerminalRetry) {
934
+ const recoveryCommand = preflight.status === 'absent'
935
+ ? `oat project pull ${options.projectName}`
936
+ : `oat project push ${options.projectName}`;
937
+ throw new CliError(`Synced project ${options.projectName} is ${preflight.status}; run ${recoveryCommand} before archiving.`, 1);
938
+ }
939
+ sourceRefSha =
940
+ preflight.sha ??
941
+ activeRecord?.archiveSourceRefSha ??
942
+ (await git.run(['rev-parse', syncTarget.ref], {
943
+ cwd: options.repoRoot,
944
+ })).stdout;
867
945
  }
868
- sourceRefSha =
869
- preflight.sha ??
870
- (await git.run(['rev-parse', syncTarget.ref], { cwd: options.repoRoot }))
871
- .stdout;
872
946
  if (!/^[0-9a-f]{40}$/.test(sourceRefSha)) {
873
947
  throw new CliError(`Unable to bind archive retry identity for ${options.projectName} to ${syncTarget.ref}.`, 2);
874
948
  }
875
- if (activeRecord.archiveSourceRefSha &&
949
+ if (activeRecord?.archiveSourceRefSha &&
876
950
  activeRecord.archiveSourceRefSha !== sourceRefSha) {
877
951
  throw new CliError(`Archive retry for ${options.projectName} is bound to source ref ${activeRecord.archiveSourceRefSha}, but ${syncTarget.ref} is now ${sourceRefSha}. Restore the retained ref to the recorded commit to finish this archive transaction, or preserve the existing snapshot and start a separately reviewed recovery; refusing to accept stale archive content.`, 1);
878
952
  }
@@ -881,9 +955,11 @@ export async function archiveProjectOnCompletion(options, dependencies = {}) {
881
955
  repoRoot: options.repoRoot,
882
956
  projectsRoot: options.projectsRoot,
883
957
  projectName: options.projectName,
884
- ...(activeRecord?.archiveSnapshot
958
+ ...((activeRecord?.archiveSnapshot ??
959
+ recordlessIdentity?.metadata.snapshotName)
885
960
  ? {
886
- archiveSnapshot: activeRecord.archiveSnapshot,
961
+ archiveSnapshot: activeRecord?.archiveSnapshot ??
962
+ recordlessIdentity?.metadata.snapshotName,
887
963
  archiveScope: projectScope,
888
964
  }
889
965
  : {}),
@@ -902,7 +978,9 @@ export async function archiveProjectOnCompletion(options, dependencies = {}) {
902
978
  }
903
979
  const archivePath = archiveTarget.archivePath;
904
980
  const exportIdentity = syncTarget
905
- ? (activeRecord?.archiveSnapshot ?? snapshotName)
981
+ ? (activeRecord?.archiveSnapshot ??
982
+ recordlessIdentity?.metadata.snapshotName ??
983
+ snapshotName)
906
984
  : snapshotName;
907
985
  const snapshotId = syncTarget ? exportIdentity : basename(archivePath);
908
986
  const shouldPersistArchiveSnapshot = Boolean(syncTarget &&
@@ -1000,6 +1078,9 @@ export async function archiveProjectOnCompletion(options, dependencies = {}) {
1000
1078
  env: dependencies.env,
1001
1079
  });
1002
1080
  warnings.push(...access.warnings);
1081
+ if (syncTarget && !access.ok) {
1082
+ throw new CliError(`Synced archive durability for ${options.projectName} requires the configured S3 upload to succeed before terminal cleanup. ${access.warnings.join(' ') || 'Archive S3 access could not be verified.'}`, 1);
1083
+ }
1003
1084
  if (access.ok) {
1004
1085
  const remoteRepoRoot = await resolvePrimaryRepoRoot(options.repoRoot, dependencies);
1005
1086
  s3Path = buildProjectArchiveS3Uri(options.s3Uri, remoteRepoRoot, exportIdentity);
@@ -1018,6 +1099,9 @@ export async function archiveProjectOnCompletion(options, dependencies = {}) {
1018
1099
  }
1019
1100
  catch (error) {
1020
1101
  const message = error instanceof Error ? error.message : String(error);
1102
+ if (syncTarget) {
1103
+ throw new CliError(`Synced archive durability for ${options.projectName} requires the configured S3 upload to succeed before terminal cleanup: ${message}`, 1);
1104
+ }
1021
1105
  warnings.push(`Archive S3 sync to \`${s3Path}\` failed: ${message}`);
1022
1106
  s3Path = null;
1023
1107
  }
@@ -1025,38 +1109,53 @@ export async function archiveProjectOnCompletion(options, dependencies = {}) {
1025
1109
  }
1026
1110
  let lifecycleCommit = null;
1027
1111
  let recapExportPaths = [];
1112
+ let terminalReceipt = null;
1113
+ let recordRetired = false;
1028
1114
  if (syncTarget) {
1029
- if (!activeRecord) {
1030
- throw new CliError(`Synced project ${options.projectName} is missing its discovery record.`, 2);
1115
+ if (!sourceRefSha) {
1116
+ throw new CliError(`Synced project ${options.projectName} has no verified source SHA for terminal sealing.`, 2);
1117
+ }
1118
+ terminalReceipt = await (dependencies.retireSyncedRef ?? retireSyncedRef)(syncTarget, sourceRefSha, git);
1119
+ if (terminalReceipt.state !== 'completed-only' &&
1120
+ terminalReceipt.state !== 'matching-aliases') {
1121
+ throw new CliError(`Synced project ${options.projectName} did not reach a verified terminal ref state.`, 2);
1122
+ }
1123
+ const removed = await (dependencies.removeSyncedCheckout ?? removeSyncedCheckout)(syncTarget, git, { force: true });
1124
+ if (removed.status !== 'removed' && removed.status !== 'absent') {
1125
+ throw new CliError(`Synced checkout became ${removed.status} during archive; run oat project push ${options.projectName} and retry.`, 1);
1031
1126
  }
1032
1127
  if (projectRecapExport) {
1033
1128
  recapExportPaths = (await listArchiveExportFiles(projectRecapExport.exportRoot)).filter((filePath) => basename(filePath) !== 'manifest.json' &&
1034
1129
  basename(filePath) !== 'build-record.json');
1035
1130
  }
1036
- activeRecord = {
1037
- ...activeRecord,
1038
- status: 'complete',
1039
- completedAt: activeRecord.completedAt ?? timestamp,
1040
- };
1041
- await writeRecord(recordPath, activeRecord);
1131
+ await (dependencies.removeSyncedRecord
1132
+ ? dependencies.removeSyncedRecord(recordPath)
1133
+ : removePath(recordPath, { recursive: true, force: true }));
1134
+ recordRetired = true;
1042
1135
  if (options.commit !== false) {
1043
1136
  const pathspecs = [
1044
1137
  recordPath,
1045
1138
  ...(summaryExportFile ? [summaryExportFile] : []),
1046
1139
  ...recapExportPaths,
1047
1140
  ];
1048
- const committed = await (dependencies.commitRecordChange ?? commitRecordChange)(options.repoRoot, pathspecs, `chore(oat): complete synced project ${options.projectName}`, git, {
1049
- summaryExportPath: options.summaryExportPath,
1050
- projectRoots: syncTarget,
1051
- recapExportRoot: projectRecapExport?.exportRoot,
1052
- });
1141
+ const lifecycleStatus = await git.run([
1142
+ 'status',
1143
+ '--porcelain=v1',
1144
+ '--untracked-files=all',
1145
+ '--',
1146
+ ...pathspecs.map((pathspec) => relative(canonicalizePath(resolve(options.repoRoot)), canonicalizePath(resolve(pathspec)))),
1147
+ ], { cwd: options.repoRoot });
1148
+ const committed = lifecycleStatus.stdout === ''
1149
+ ? null
1150
+ : await (dependencies.commitRecordChange ?? commitRecordChange)(options.repoRoot, pathspecs, `chore(oat): complete synced project ${options.projectName}`, git, {
1151
+ summaryExportPath: options.summaryExportPath,
1152
+ projectRoots: syncTarget,
1153
+ recapExportRoot: projectRecapExport?.exportRoot,
1154
+ });
1053
1155
  lifecycleCommit =
1054
1156
  committed?.sha ??
1055
- (await recoverSyncedLifecycleCommit(options.repoRoot, pathspecs, activeRecord, `chore(oat): complete synced project ${options.projectName}`, git));
1056
- }
1057
- const removed = await (dependencies.removeSyncedCheckout ?? removeSyncedCheckout)(syncTarget, git);
1058
- if (removed.status !== 'removed' && removed.status !== 'absent') {
1059
- throw new CliError(`Synced checkout became ${removed.status} during archive; run oat project push ${options.projectName} and retry.`, 1);
1157
+ (await recoverSyncedLifecycleCommit(options.repoRoot, pathspecs, options.projectName, `chore(oat): complete synced project ${options.projectName}`, git));
1158
+ await dependencies.afterLifecycleCommit?.();
1060
1159
  }
1061
1160
  }
1062
1161
  return {
@@ -1068,12 +1167,14 @@ export async function archiveProjectOnCompletion(options, dependencies = {}) {
1068
1167
  lifecycleCommit,
1069
1168
  recapExportPaths,
1070
1169
  snapshotId,
1170
+ terminalReceipt,
1171
+ recordRetired,
1071
1172
  };
1072
1173
  }
1073
- async function recoverSyncedLifecycleCommit(repoRoot, pathspecs, record, expectedSubject, git) {
1174
+ async function recoverSyncedLifecycleCommit(repoRoot, pathspecs, projectName, expectedSubject, git) {
1074
1175
  const [recordPathspec] = pathspecs;
1075
1176
  if (!recordPathspec) {
1076
- throw new CliError(`Unable to recover the prior lifecycle commit for ${record.slug}: no lifecycle paths were supplied.`, 2);
1177
+ throw new CliError(`Unable to recover the prior lifecycle commit for ${projectName}: no lifecycle paths were supplied.`, 2);
1077
1178
  }
1078
1179
  const recordPath = relative(resolve(repoRoot), resolve(recordPathspec))
1079
1180
  .split(sep)
@@ -1086,7 +1187,7 @@ async function recoverSyncedLifecycleCommit(repoRoot, pathspecs, record, expecte
1086
1187
  cwd: repoRoot,
1087
1188
  })).stdout;
1088
1189
  if (!/^[0-9a-f]{40}$/.test(candidate)) {
1089
- throw new CliError(`Unable to recover the prior lifecycle commit for ${record.slug}.`, 2);
1190
+ throw new CliError(`Unable to recover the prior lifecycle commit for ${projectName}.`, 2);
1090
1191
  }
1091
1192
  const subject = (await git.run(['show', '-s', '--format=%s', candidate], { cwd: repoRoot })).stdout;
1092
1193
  const ancestor = await git.run(['merge-base', '--is-ancestor', candidate, 'HEAD'], { cwd: repoRoot, allowFailure: true });
@@ -1095,20 +1196,16 @@ async function recoverSyncedLifecycleCommit(repoRoot, pathspecs, record, expecte
1095
1196
  .filter(Boolean)
1096
1197
  .sort();
1097
1198
  const contentsMatch = await git.run(['diff', '--quiet', candidate, '--', ...normalizedPathspecs], { cwd: repoRoot, allowFailure: true });
1098
- const committedRecord = await git.run(['show', `${candidate}:${recordPath}`], { cwd: repoRoot });
1099
- let parsedRecord;
1100
- try {
1101
- parsedRecord = JSON.parse(committedRecord.stdout);
1102
- }
1103
- catch {
1104
- parsedRecord = null;
1105
- }
1199
+ const committedRecord = await git.run(['show', `${candidate}:${recordPath}`], {
1200
+ cwd: repoRoot,
1201
+ allowFailure: true,
1202
+ });
1106
1203
  if (subject !== expectedSubject ||
1107
1204
  ancestor.code !== 0 ||
1108
1205
  contentsMatch.code !== 0 ||
1109
1206
  JSON.stringify(changedPaths) !== JSON.stringify(normalizedPathspecs) ||
1110
- JSON.stringify(parsedRecord) !== JSON.stringify(record)) {
1111
- throw new CliError(`Unable to recover lifecycle commit ${candidate} for ${record.slug}: subject, path set, contents, or branch relationship did not match the completed archive transaction.`, 2);
1207
+ committedRecord.code === 0) {
1208
+ throw new CliError(`Unable to recover lifecycle commit ${candidate} for ${projectName}: subject, path set, record deletion, contents, or branch relationship did not match the completed archive transaction.`, 2);
1112
1209
  }
1113
1210
  return candidate;
1114
1211
  }
@@ -1 +1 @@
1
- {"version":3,"file":"push-runner.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/archive/push-runner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOjE,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAEL,6BAA6B,EAI7B,2BAA2B,EAC3B,sBAAsB,EACtB,oCAAoC,EAEpC,KAAK,iCAAiC,EACtC,KAAK,gCAAgC,EAEtC,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,qCAAqC;IACpD,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,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,2BAA2B,EAAE,OAAO,2BAA2B,CAAC;IAChE,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,oCAAoC,EAAE,OAAO,oCAAoC,CAAC;IAClF,0BAA0B,EAAE,CAC1B,OAAO,EAAE,iCAAiC,KACvC,OAAO,CAAC,gCAAgC,CAAC,CAAC;IAC/C,6BAA6B,EAAE,OAAO,6BAA6B,CAAC;IACpE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,MAAM,MAAM,CAAC;CACzB;AAsBD,wBAAgB,4CAA4C,IAAI,qCAAqC,CAgBpG;AAuJD,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,qCAAqC,EACnD,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAyGf"}
1
+ {"version":3,"file":"push-runner.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/archive/push-runner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOjE,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAEL,6BAA6B,EAI7B,2BAA2B,EAC3B,sBAAsB,EACtB,oCAAoC,EAEpC,KAAK,iCAAiC,EACtC,KAAK,gCAAgC,EAEtC,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,qCAAqC;IACpD,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,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,2BAA2B,EAAE,OAAO,2BAA2B,CAAC;IAChE,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,oCAAoC,EAAE,OAAO,oCAAoC,CAAC;IAClF,0BAA0B,EAAE,CAC1B,OAAO,EAAE,iCAAiC,KACvC,OAAO,CAAC,gCAAgC,CAAC,CAAC;IAC/C,6BAA6B,EAAE,OAAO,6BAA6B,CAAC;IACpE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,MAAM,MAAM,CAAC;CACzB;AA0BD,wBAAgB,4CAA4C,IAAI,qCAAqC,CAgBpG;AA2JD,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,qCAAqC,EACnD,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAmHf"}
@@ -82,6 +82,10 @@ async function buildDryRunReport(dependencies, repoRoot, config, target, archive
82
82
  snapshotId: projectScope === 'synced'
83
83
  ? snapshotName
84
84
  : basename(archiveTarget.archivePath),
85
+ completedRef: null,
86
+ verifiedSourceSha: null,
87
+ activeAliasDisposition: null,
88
+ recordRetired: false,
85
89
  };
86
90
  }
87
91
  function emitArchivePushText(report, summaryExportPath, logger) {
@@ -148,10 +152,11 @@ export async function runArchivePushCommand(dependencies, projectPathArg, option
148
152
  timestamp: dependencies.timestamp,
149
153
  });
150
154
  assertDurableArchiveProjectTarget(archiveTarget);
151
- if (options.projectRecapRun?.trim()) {
155
+ const dryRun = options.dryRun === true || context.dryRun;
156
+ const recordlessSyncedApply = projectScope === 'synced' && syncedRecord === null && !dryRun;
157
+ if (options.projectRecapRun?.trim() && !recordlessSyncedApply) {
152
158
  await dependencies.verifySelectedProjectRecapForArchive(target.projectPath, options.projectRecapRun.trim());
153
159
  }
154
- const dryRun = options.dryRun === true || context.dryRun;
155
160
  if (dryRun) {
156
161
  const report = await buildDryRunReport(dependencies, repoRoot, config, target, archiveTarget, snapshotName, projectScope);
157
162
  emitArchivePushReport(report, config.archive?.summaryExportPath, context);
@@ -174,6 +179,14 @@ export async function runArchivePushCommand(dependencies, projectPathArg, option
174
179
  lifecycleCommit: result.lifecycleCommit,
175
180
  recapExportPaths: result.recapExportPaths,
176
181
  snapshotId: result.snapshotId,
182
+ completedRef: result.terminalReceipt?.completedRef ?? null,
183
+ verifiedSourceSha: result.terminalReceipt?.verifiedSha ?? null,
184
+ activeAliasDisposition: result.terminalReceipt
185
+ ? result.terminalReceipt.activeAliasRetained
186
+ ? 'retained'
187
+ : 'removed'
188
+ : null,
189
+ recordRetired: result.recordRetired,
177
190
  };
178
191
  emitArchivePushReport(report, config.archive?.summaryExportPath, context);
179
192
  process.exitCode = 0;
@@ -4,6 +4,7 @@ import { type LinksInput } from './render.js';
4
4
  export interface ComputeLinksOptions {
5
5
  durableSummaryPath?: string;
6
6
  now: Date;
7
+ ref?: string;
7
8
  }
8
9
  export declare function computeLinksInput(target: SyncTarget, git: GitRunner, options: ComputeLinksOptions): Promise<LinksInput>;
9
10
  //# sourceMappingURL=compute.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compute.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/links/compute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,mBAAmB;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,IAAI,CAAC;CACX;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CA6BrB"}
1
+ {"version":3,"file":"compute.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/links/compute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,mBAAmB;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CA8BrB"}
@@ -1,11 +1,12 @@
1
1
  import { LINKABLE_ARTIFACTS, } from './render.js';
2
2
  export async function computeLinksInput(target, git, options) {
3
- await git.run(['fetch', target.remote, `+${target.ref}:${target.ref}`], {
3
+ const selectedRef = options.ref ?? target.ref;
4
+ await git.run(['fetch', target.remote, `+${selectedRef}:${selectedRef}`], {
4
5
  cwd: target.repoRoot,
5
6
  });
6
7
  const [sha, tree, origin] = await Promise.all([
7
- git.run(['rev-parse', target.ref], { cwd: target.repoRoot }),
8
- git.run(['ls-tree', '--name-only', target.ref], { cwd: target.repoRoot }),
8
+ git.run(['rev-parse', selectedRef], { cwd: target.repoRoot }),
9
+ git.run(['ls-tree', '--name-only', selectedRef], { cwd: target.repoRoot }),
9
10
  git.run(['remote', 'get-url', target.remote], { cwd: target.repoRoot }),
10
11
  ]);
11
12
  const treeNames = new Set(tree.stdout
@@ -16,7 +17,7 @@ export async function computeLinksInput(target, git, options) {
16
17
  return {
17
18
  slug: target.slug,
18
19
  sha: sha.stdout,
19
- ref: target.ref,
20
+ ref: selectedRef,
20
21
  originUrl: origin.stdout,
21
22
  present,
22
23
  ...(options.durableSummaryPath
@@ -1,17 +1,26 @@
1
1
  import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
2
2
  import { type GitRunner } from '../../project/sync/git.js';
3
+ import { type SyncTarget } from '../../project/sync/ref-sync.js';
3
4
  import { resolveSyncedTarget } from '../../project/sync/resolve-target.js';
5
+ import { resolveProjectsRoot } from '../../shared/oat-paths.js';
4
6
  import { Command } from 'commander';
5
7
  import { computeLinksInput } from './compute.js';
6
8
  interface ProjectLinksDependencies {
7
9
  buildCommandContext: (options: GlobalOptions) => CommandContext;
8
10
  resolveProjectRoot: (cwd: string) => Promise<string>;
11
+ resolveProjectsRoot: typeof resolveProjectsRoot;
9
12
  resolveSyncedTarget: typeof resolveSyncedTarget;
13
+ resolveLinksTarget: typeof resolveLinksTarget;
10
14
  computeLinksInput: typeof computeLinksInput;
11
15
  gitRunner: GitRunner;
12
16
  processEnv: NodeJS.ProcessEnv;
13
17
  now: () => Date;
14
18
  }
19
+ interface ResolvedLinksTarget {
20
+ target: SyncTarget;
21
+ ref: string;
22
+ }
23
+ export declare function resolveLinksTarget(repoRoot: string, projectsRoot: string, pathOrSlug: string | undefined, env: NodeJS.ProcessEnv, git: GitRunner, resolveTarget: typeof resolveSyncedTarget): Promise<ResolvedLinksTarget>;
15
24
  export declare function createProjectLinksCommand(overrides?: Partial<ProjectLinksDependencies>): Command;
16
25
  export {};
17
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/links/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAI5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAQ9C,UAAU,wBAAwB;IAChC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AA4ED,wBAAgB,yBAAyB,CACvC,SAAS,GAAE,OAAO,CAAC,wBAAwB,CAAM,GAChD,OAAO,CAsBT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/links/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,mBAAmB,EACpB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AASjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAQ9C,UAAU,wBAAwB;IAChC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAmCD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AAwBD,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,EAAE,SAAS,EACd,aAAa,EAAE,OAAO,mBAAmB,GACxC,OAAO,CAAC,mBAAmB,CAAC,CAgE9B;AAoDD,wBAAgB,yBAAyB,CACvC,SAAS,GAAE,OAAO,CAAC,wBAAwB,CAAM,GAChD,OAAO,CAsBT"}