@open-agent-toolkit/cli 0.2.50 → 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 (60) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/reference/cli-reference.md +20 -5
  3. package/assets/docs/reference/file-locations.md +8 -2
  4. package/assets/docs/reference/oat-directory-structure.md +14 -5
  5. package/assets/docs/workflows/projects/artifacts.md +9 -4
  6. package/assets/docs/workflows/projects/lifecycle.md +17 -5
  7. package/assets/docs/workflows/projects/picking-up-projects.md +26 -9
  8. package/assets/docs/workflows/projects/pr-flow.md +5 -3
  9. package/assets/docs/workflows/projects/reviewing-oat-prs.md +12 -8
  10. package/assets/public-package-versions.json +4 -4
  11. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +2 -2
  12. package/assets/skills/oat-project-complete/SKILL.md +193 -26
  13. package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +306 -0
  14. package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +125 -0
  15. package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +106 -0
  16. package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +110 -0
  17. package/assets/skills/oat-project-complete/scripts/resolve-synced-archive-entry.mjs +164 -0
  18. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +2 -2
  19. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +2 -2
  20. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +2 -2
  21. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +2 -2
  22. package/dist/commands/project/archive/archive-utils.d.ts +8 -1
  23. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  24. package/dist/commands/project/archive/archive-utils.js +147 -50
  25. package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
  26. package/dist/commands/project/archive/push-runner.js +15 -2
  27. package/dist/commands/project/links/compute.d.ts +1 -0
  28. package/dist/commands/project/links/compute.d.ts.map +1 -1
  29. package/dist/commands/project/links/compute.js +5 -4
  30. package/dist/commands/project/links/index.d.ts +9 -0
  31. package/dist/commands/project/links/index.d.ts.map +1 -1
  32. package/dist/commands/project/links/index.js +71 -2
  33. package/dist/commands/project/list.d.ts +10 -2
  34. package/dist/commands/project/list.d.ts.map +1 -1
  35. package/dist/commands/project/list.js +205 -29
  36. package/dist/commands/project/open/index.d.ts +2 -0
  37. package/dist/commands/project/open/index.d.ts.map +1 -1
  38. package/dist/commands/project/open/index.js +16 -1
  39. package/dist/commands/project/prune/index.d.ts +10 -4
  40. package/dist/commands/project/prune/index.d.ts.map +1 -1
  41. package/dist/commands/project/prune/index.js +85 -15
  42. package/dist/commands/project/pull/index.d.ts +4 -0
  43. package/dist/commands/project/pull/index.d.ts.map +1 -1
  44. package/dist/commands/project/pull/index.js +82 -3
  45. package/dist/commands/project/sync/git.d.ts +9 -0
  46. package/dist/commands/project/sync/git.d.ts.map +1 -1
  47. package/dist/commands/project/sync/git.js +18 -0
  48. package/dist/commands/project/sync/ref-sync.d.ts +16 -1
  49. package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
  50. package/dist/commands/project/sync/ref-sync.js +288 -17
  51. package/dist/commands/project/sync/resolve-target.d.ts +10 -0
  52. package/dist/commands/project/sync/resolve-target.d.ts.map +1 -1
  53. package/dist/commands/project/sync/resolve-target.js +56 -1
  54. package/dist/commands/shared/project-scope.d.ts +2 -0
  55. package/dist/commands/shared/project-scope.d.ts.map +1 -1
  56. package/dist/commands/shared/project-scope.js +5 -0
  57. package/dist/commands/state/generate.d.ts +2 -0
  58. package/dist/commands/state/generate.d.ts.map +1 -1
  59. package/dist/commands/state/generate.js +93 -28
  60. package/package.json +2 -2
@@ -2,10 +2,12 @@ import { stat } from 'node:fs/promises';
2
2
  import { basename, extname, isAbsolute, join, relative, resolve, sep, } from 'node:path';
3
3
  import { buildCommandContext, } from '../../app/command-context.js';
4
4
  import { defaultGitRunner } from '../project/sync/git.js';
5
- import { listSyncedRecords } from '../project/sync/record.js';
5
+ import { listSyncedRecords, } from '../project/sync/record.js';
6
+ import { buildSyncTarget, classifyRemoteRefLookup, } from '../project/sync/ref-sync.js';
7
+ import { probeSyncedTerminalRefs, } from '../project/sync/resolve-target.js';
6
8
  import { parseFrontmatterField } from '../shared/frontmatter.js';
7
9
  import { resolveProjectsRoot } from '../shared/oat-paths.js';
8
- import { PROJECT_SCOPES, resolveScopeRoot, } from '../shared/project-scope.js';
10
+ import { completedSyncedRefName, PROJECT_SCOPES, resolveScopeRoot, } from '../shared/project-scope.js';
9
11
  import { readGlobalOptions } from '../shared/shared.utils.js';
10
12
  import { CliError } from '../../errors/cli-error.js';
11
13
  import { resolveProjectRoot } from '../../fs/paths.js';
@@ -17,6 +19,8 @@ const DEFAULT_DEPENDENCIES = {
17
19
  resolveProjectsRoot,
18
20
  listProjects,
19
21
  listSyncedRecords,
22
+ probeSyncedTerminalRefs,
23
+ probeAuthoritativeCompletedRef,
20
24
  directoryExists,
21
25
  readProjectMetadata,
22
26
  processEnv: process.env,
@@ -83,7 +87,13 @@ function formatProjectTable(projects) {
83
87
  : '—'
84
88
  : project.kind === 'recorded-invalid'
85
89
  ? 'restore record from Git'
86
- : `oat project pull ${project.name}`,
90
+ : project.kind === 'recorded-terminal'
91
+ ? project.archiveSnapshot
92
+ ? 'retry completion cleanup'
93
+ : 'retry archive completion'
94
+ : project.kind === 'terminal-invalid'
95
+ ? 'repair terminal ref mismatch'
96
+ : `oat project pull ${project.name}`,
87
97
  }));
88
98
  const widths = {
89
99
  name: Math.max('NAME'.length, ...rows.map((row) => row.name.length)),
@@ -119,6 +129,127 @@ function formatProjectTable(projects) {
119
129
  ].join(' '));
120
130
  return [header, divider, ...lines];
121
131
  }
132
+ const ARCHIVE_RETRY_REASON = 'archive snapshot is incomplete; retry oat-project-complete before retiring terminal state';
133
+ const RETIREMENT_RETRY_REASON = 'archive is durable but legacy terminal cleanup remains; retry oat-project-complete';
134
+ const AUTHORITATIVE_COMPLETION_REASON = 'completed ref is authoritative; remove the stale local checkout or active record with oat-project-complete';
135
+ function terminalMismatchReason(activeSha, completedSha, expectedSha) {
136
+ return `terminal ref SHA mismatch: active ${activeSha ?? 'absent'}, completed ${completedSha ?? 'absent'}${expectedSha ? `, expected archived source ${expectedSha}` : ''}; repair the ref mismatch before retrying completion or prune`;
137
+ }
138
+ export function classifyLegacySyncedRecord(record, path, probe) {
139
+ if (probe?.state === 'wrong-sha') {
140
+ return {
141
+ kind: 'terminal-invalid',
142
+ name: record.slug,
143
+ path,
144
+ scope: 'synced',
145
+ checkout: 'invalid',
146
+ terminalState: 'ref-sha-mismatch',
147
+ activeRef: probe.activeRef,
148
+ completedRef: probe.completedRef,
149
+ activeSha: probe.activeSha,
150
+ completedSha: probe.completedSha,
151
+ expectedSha: probe.expectedSha,
152
+ phase: null,
153
+ phaseStatus: null,
154
+ workflowMode: null,
155
+ lifecycle: 'complete',
156
+ progress: null,
157
+ recommendation: {
158
+ skill: 'none',
159
+ reason: terminalMismatchReason(probe.activeSha, probe.completedSha, probe.expectedSha),
160
+ },
161
+ };
162
+ }
163
+ if (probe?.state === 'completed-only' || probe?.state === 'both') {
164
+ return {
165
+ kind: 'recorded-terminal',
166
+ name: record.slug,
167
+ path,
168
+ scope: 'synced',
169
+ checkout: 'absent',
170
+ terminalState: 'authoritative-completion',
171
+ archiveSnapshot: record.archiveSnapshot ?? null,
172
+ phase: null,
173
+ phaseStatus: null,
174
+ workflowMode: null,
175
+ lifecycle: 'complete',
176
+ progress: null,
177
+ recommendation: {
178
+ skill: 'none',
179
+ reason: AUTHORITATIVE_COMPLETION_REASON,
180
+ },
181
+ };
182
+ }
183
+ if (record.status === 'complete') {
184
+ return {
185
+ kind: 'recorded-terminal',
186
+ name: record.slug,
187
+ path,
188
+ scope: 'synced',
189
+ checkout: 'absent',
190
+ terminalState: 'legacy-completion',
191
+ archiveSnapshot: record.archiveSnapshot ?? null,
192
+ phase: null,
193
+ phaseStatus: null,
194
+ workflowMode: null,
195
+ lifecycle: 'complete',
196
+ progress: null,
197
+ recommendation: {
198
+ skill: 'none',
199
+ reason: record.archiveSnapshot
200
+ ? RETIREMENT_RETRY_REASON
201
+ : ARCHIVE_RETRY_REASON,
202
+ },
203
+ };
204
+ }
205
+ return {
206
+ kind: 'recorded-absent',
207
+ name: record.slug,
208
+ path,
209
+ scope: 'synced',
210
+ checkout: 'absent',
211
+ phase: null,
212
+ phaseStatus: null,
213
+ workflowMode: null,
214
+ lifecycle: null,
215
+ progress: null,
216
+ recommendation: {
217
+ skill: 'oat project pull',
218
+ reason: 'checkout absent',
219
+ },
220
+ };
221
+ }
222
+ export async function probeAuthoritativeCompletedRef(target, git) {
223
+ const completedRef = completedSyncedRefName(target.slug);
224
+ const lookup = await git.run(['ls-remote', '--exit-code', target.remote, completedRef], { cwd: target.repoRoot, allowFailure: true });
225
+ if (classifyRemoteRefLookup(lookup, target.remote, completedRef) === 'absent') {
226
+ return null;
227
+ }
228
+ const rows = lookup.stdout.split('\n').filter(Boolean);
229
+ const [sha, ref] = rows[0]?.trim().split(/\s+/) ?? [];
230
+ if (rows.length !== 1 ||
231
+ ref !== completedRef ||
232
+ !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(sha ?? '')) {
233
+ throw new CliError(`Unable to reconcile completed authority for ${target.slug}: origin returned a malformed completed-ref advertisement.`, 2);
234
+ }
235
+ return probeSyncedTerminalRefs(target, sha, git);
236
+ }
237
+ function classifyMaterializedTerminalRow(row, probe) {
238
+ const record = {
239
+ schemaVersion: 1,
240
+ slug: row.name,
241
+ scope: 'synced',
242
+ ref: probe.activeRef,
243
+ remote: 'origin',
244
+ status: 'active',
245
+ createdAt: new Date(0).toISOString(),
246
+ completedAt: null,
247
+ };
248
+ const classified = classifyLegacySyncedRecord(record, row.path, probe);
249
+ return classified.kind === 'recorded-terminal'
250
+ ? { ...classified, checkout: 'present' }
251
+ : classified;
252
+ }
122
253
  function displayPath(repoRoot, absolutePath) {
123
254
  return relative(repoRoot, absolutePath).split(sep).join('/');
124
255
  }
@@ -152,9 +283,6 @@ async function collectProjectRows(repoRoot, projectsRoot, options, context, depe
152
283
  })));
153
284
  }
154
285
  if (root.scope === 'synced') {
155
- const materialized = new Set(rows
156
- .filter((row) => row.scope === 'synced' && row.kind === 'materialized')
157
- .map((row) => row.name));
158
286
  const records = await dependencies.listSyncedRecords(root.path, {
159
287
  onInvalid: (path, error) => {
160
288
  const message = error instanceof Error ? error.message : String(error);
@@ -191,24 +319,24 @@ async function collectProjectRows(repoRoot, projectsRoot, options, context, depe
191
319
  context.logger.warn(`Skipping invalid synced project record ${displayPath(repoRoot, path)}: ${message}`);
192
320
  },
193
321
  });
322
+ for (const row of rows.filter((candidate) => candidate.scope === 'synced' && candidate.kind === 'materialized')) {
323
+ const target = buildSyncTarget(repoRoot, projectsRoot, row.name);
324
+ const probe = await dependencies.probeAuthoritativeCompletedRef(target, dependencies.gitRunner);
325
+ if (!probe)
326
+ continue;
327
+ const index = rows.indexOf(row);
328
+ rows[index] = classifyMaterializedTerminalRow(row, probe);
329
+ }
330
+ const represented = new Set(rows.filter((row) => row.scope === 'synced').map((row) => row.name));
194
331
  for (const record of records) {
195
- if (!materialized.has(record.slug)) {
196
- rows.push({
197
- kind: 'recorded-absent',
198
- name: record.slug,
199
- path: displayPath(repoRoot, join(root.path, record.slug)),
200
- scope: 'synced',
201
- checkout: 'absent',
202
- phase: null,
203
- phaseStatus: null,
204
- workflowMode: null,
205
- lifecycle: null,
206
- progress: null,
207
- recommendation: {
208
- skill: 'oat project pull',
209
- reason: 'checkout absent',
210
- },
211
- });
332
+ if (!represented.has(record.slug)) {
333
+ const target = buildSyncTarget(repoRoot, projectsRoot, record.slug);
334
+ const authoritative = await dependencies.probeAuthoritativeCompletedRef(target, dependencies.gitRunner);
335
+ const probe = authoritative ??
336
+ (record.archiveSourceRefSha
337
+ ? await dependencies.probeSyncedTerminalRefs(target, record.archiveSourceRefSha, dependencies.gitRunner)
338
+ : undefined);
339
+ rows.push(classifyLegacySyncedRecord(record, displayPath(repoRoot, join(root.path, record.slug)), probe));
212
340
  }
213
341
  }
214
342
  }
@@ -217,26 +345,74 @@ async function collectProjectRows(repoRoot, projectsRoot, options, context, depe
217
345
  left.scope.localeCompare(right.scope));
218
346
  }
219
347
  async function appendRemoteRows(rows, repoRoot, context, dependencies) {
220
- const remote = await dependencies.gitRunner.run(['ls-remote', 'origin', 'refs/oat/projects/*'], { cwd: repoRoot, allowFailure: true });
348
+ const remote = await dependencies.gitRunner.run(['ls-remote', 'origin', 'refs/oat/projects/*', 'refs/oat/completed/*'], { cwd: repoRoot, allowFailure: true });
221
349
  if (remote.code !== 0) {
222
350
  context.logger.warn(`Warning: unable to list remote synced projects: ${remote.stderr || remote.stdout || 'origin is unreachable'}`);
223
351
  return rows;
224
352
  }
225
353
  const localSlugs = new Set(rows.filter((row) => row.scope === 'synced').map((row) => row.name));
354
+ const refs = new Map();
226
355
  for (const line of remote.stdout.split('\n')) {
227
- const [, ref] = line.trim().split(/\s+/);
228
- if (!ref?.startsWith('refs/oat/projects/'))
356
+ const [sha, ref] = line.trim().split(/\s+/);
357
+ const activePrefix = 'refs/oat/projects/';
358
+ const completedPrefix = 'refs/oat/completed/';
359
+ const prefix = ref?.startsWith(activePrefix)
360
+ ? activePrefix
361
+ : ref?.startsWith(completedPrefix)
362
+ ? completedPrefix
363
+ : null;
364
+ if (!sha || !ref || !prefix)
365
+ continue;
366
+ const name = ref.slice(prefix.length);
367
+ if (!name)
368
+ continue;
369
+ const entry = refs.get(name) ?? {};
370
+ if (prefix === activePrefix) {
371
+ entry.activeRef = ref;
372
+ entry.activeSha = sha;
373
+ }
374
+ else {
375
+ entry.completedRef = ref;
376
+ entry.completedSha = sha;
377
+ }
378
+ refs.set(name, entry);
379
+ }
380
+ for (const [name, refState] of refs) {
381
+ if (localSlugs.has(name) || !refState.activeRef)
382
+ continue;
383
+ if (refState.completedRef && refState.completedSha === refState.activeSha) {
229
384
  continue;
230
- const name = ref.slice('refs/oat/projects/'.length);
231
- if (!name || localSlugs.has(name))
385
+ }
386
+ if (refState.completedRef && refState.completedSha && refState.activeSha) {
387
+ rows.push({
388
+ kind: 'terminal-invalid',
389
+ name,
390
+ scope: 'synced',
391
+ checkout: 'invalid',
392
+ terminalState: 'ref-sha-mismatch',
393
+ activeRef: refState.activeRef,
394
+ completedRef: refState.completedRef,
395
+ activeSha: refState.activeSha,
396
+ completedSha: refState.completedSha,
397
+ phase: null,
398
+ phaseStatus: null,
399
+ workflowMode: null,
400
+ lifecycle: 'complete',
401
+ progress: null,
402
+ recommendation: {
403
+ skill: 'none',
404
+ reason: terminalMismatchReason(refState.activeSha, refState.completedSha),
405
+ },
406
+ });
232
407
  continue;
408
+ }
233
409
  rows.push({
234
410
  kind: 'remote',
235
411
  name,
236
412
  scope: 'synced',
237
413
  origin: 'remote',
238
414
  checkout: 'absent',
239
- ref,
415
+ ref: refState.activeRef,
240
416
  phase: null,
241
417
  phaseStatus: null,
242
418
  workflowMode: null,
@@ -1,5 +1,6 @@
1
1
  import { readFile as defaultReadFile, writeFile as defaultWriteFile } from 'node:fs/promises';
2
2
  import { buildCommandContext, type CommandContext } from '../../../app/command-context.js';
3
+ import { guardSyncedTerminalTarget } from '../../project/pull/index.js';
3
4
  import { type GitRunner } from '../../project/sync/git.js';
4
5
  import { readSyncedRecord } from '../../project/sync/record.js';
5
6
  import { commitRecordChange as defaultCommitRecordChange, type PullResult, type PushResult, type AdoptionRecordState, type SyncTarget } from '../../project/sync/ref-sync.js';
@@ -12,6 +13,7 @@ interface ProjectOpenDependencies {
12
13
  buildCommandContext: (options: Parameters<typeof buildCommandContext>[0]) => CommandContext;
13
14
  resolveProjectRoot: (cwd: string) => Promise<string>;
14
15
  resolveProjectsRoot: (repoRoot: string, env: NodeJS.ProcessEnv) => Promise<string>;
16
+ guardSyncedTerminalTarget: typeof guardSyncedTerminalTarget;
15
17
  readOatLocalConfig: (repoRoot: string) => Promise<OatLocalConfig>;
16
18
  writeOatLocalConfig: (repoRoot: string, config: OatLocalConfig) => Promise<void>;
17
19
  setActiveProject: (repoRoot: string, path: string) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/open/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EACL,kBAAkB,IAAI,yBAAyB,EAG/C,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,mBAAmB,EAEpB,MAAM,uCAAuC,CAAC;AAmB/C,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,UAAU,uBAAuB;IAC/B,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,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,sBAAsB,EAAE,CAAC,OAAO,EAAE;QAChC,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,UAAU,EAAE,CACV,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,cAAc,CAAC,EAAE,mBAAmB,CAAC;QACrC,GAAG,CAAC,EAAE,IAAI,CAAC;KACZ,KACE,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,UAAU,EAAE,CACV,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAC1B,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,kBAAkB,EAAE,OAAO,yBAAyB,CAAC;IACrD,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAgXD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAkBT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/open/index.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { readFile as defaultReadFile, writeFile as defaultWriteFile, } from 'node:fs/promises';
2
2
  import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
3
3
  import { buildCommandContext } from '../../../app/command-context.js';
4
+ import { guardSyncedTerminalTarget } from '../../project/pull/index.js';
4
5
  import { defaultGitRunner } from '../../project/sync/git.js';
5
6
  import { readSyncedRecord } from '../../project/sync/record.js';
6
7
  import { commitRecordChange as defaultCommitRecordChange, pullSynced as defaultPullSynced, pushSynced as defaultPushSynced, } from '../../project/sync/ref-sync.js';
@@ -21,6 +22,7 @@ const DEFAULT_DEPENDENCIES = {
21
22
  buildCommandContext,
22
23
  resolveProjectRoot,
23
24
  resolveProjectsRoot,
25
+ guardSyncedTerminalTarget,
24
26
  readOatLocalConfig,
25
27
  writeOatLocalConfig,
26
28
  setActiveProject,
@@ -77,7 +79,18 @@ async function resolveProject(repoRoot, projectsRoot, input, dependencies) {
77
79
  const projectName = basename(fullProjectPath);
78
80
  let syncTarget;
79
81
  if (scope === 'synced') {
80
- syncTarget = await dependencies.resolveSyncedTarget({ repoRoot, env: dependencies.processEnv }, fullProjectPath, {}, { allowMissingCheckout: true });
82
+ try {
83
+ syncTarget = await dependencies.resolveSyncedTarget({ repoRoot, env: dependencies.processEnv }, fullProjectPath, {}, { allowMissingCheckout: true });
84
+ }
85
+ catch (error) {
86
+ if (error instanceof CliError &&
87
+ error.exitCode === 1 &&
88
+ error.message.startsWith('No synced project named ')) {
89
+ await dependencies.guardSyncedTerminalTarget(repoRoot, projectsRoot, input, 'open', dependencies.gitRunner);
90
+ }
91
+ throw error;
92
+ }
93
+ await dependencies.guardSyncedTerminalTarget(repoRoot, projectsRoot, input, 'open', dependencies.gitRunner);
81
94
  if (!(await dependencies.dirExists(fullProjectPath)) ||
82
95
  syncTarget.adopt) {
83
96
  await materializeSyncedProject(repoRoot, syncTarget, dependencies);
@@ -124,12 +137,14 @@ async function resolveProject(repoRoot, projectsRoot, input, dependencies) {
124
137
  }
125
138
  catch (error) {
126
139
  if (error instanceof CliError && error.exitCode === 1) {
140
+ await dependencies.guardSyncedTerminalTarget(repoRoot, projectsRoot, input, 'open', dependencies.gitRunner);
127
141
  throw new CliError(`Project not found: ${input}`, 1);
128
142
  }
129
143
  throw error;
130
144
  }
131
145
  }
132
146
  if (candidate.scope === 'synced') {
147
+ await dependencies.guardSyncedTerminalTarget(repoRoot, projectsRoot, input, 'open', dependencies.gitRunner);
133
148
  syncTarget ??= await dependencies.resolveSyncedTarget({ repoRoot, env: dependencies.processEnv }, candidate.fullProjectPath, {}, { allowMissingCheckout: true });
134
149
  if (!candidate.present || syncTarget.adopt) {
135
150
  await materializeSyncedProject(repoRoot, syncTarget, dependencies);
@@ -1,21 +1,27 @@
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 PruneResult, type SyncTarget } from '../../project/sync/ref-sync.js';
4
- import { resolveSyncedTarget } from '../../project/sync/resolve-target.js';
3
+ import { deleteCompletedSyncedRefForPrune, type PruneResult, type SyncTarget } from '../../project/sync/ref-sync.js';
4
+ import { resolveSyncedTarget, type SyncedTerminalRefProbe } from '../../project/sync/resolve-target.js';
5
+ import { resolveProjectsRoot } from '../../shared/oat-paths.js';
5
6
  import { Command } from 'commander';
6
7
  interface ProjectPruneDependencies {
7
8
  buildCommandContext: (options: GlobalOptions) => CommandContext;
8
9
  resolveProjectRoot: (cwd: string) => Promise<string>;
10
+ resolveProjectsRoot: typeof resolveProjectsRoot;
9
11
  resolveSyncedTarget: typeof resolveSyncedTarget;
12
+ inspectTerminalRefs: typeof inspectTerminalRefs;
13
+ deleteCompletedSyncedRefForPrune: typeof deleteCompletedSyncedRefForPrune;
10
14
  pruneSynced: (target: SyncTarget, git: GitRunner, options: {
11
15
  force: boolean;
12
16
  commit: boolean;
17
+ expectedActiveAliasSha?: string;
13
18
  }) => Promise<PruneResult>;
14
- readProjectState: (target: SyncTarget, git: GitRunner) => Promise<string | null>;
19
+ readProjectState: (target: SyncTarget, git: GitRunner, ref?: string) => Promise<string | null>;
15
20
  gitRunner: GitRunner;
16
21
  processEnv: NodeJS.ProcessEnv;
17
22
  }
18
- export declare function readPruneProjectState(target: SyncTarget, git: GitRunner): Promise<string | null>;
23
+ export declare function readPruneProjectState(target: SyncTarget, git: GitRunner, ref?: string): Promise<string | null>;
24
+ export declare function inspectTerminalRefs(target: SyncTarget, git: GitRunner): Promise<SyncedTerminalRefProbe | null>;
19
25
  export declare function createProjectPruneCommand(overrides?: Partial<ProjectPruneDependencies>): Command;
20
26
  export {};
21
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/prune/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE9E,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAS5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,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,WAAW,EAAE,CACX,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,KACzC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1B,gBAAgB,EAAE,CAChB,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,KACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BxB;AAmED,wBAAgB,yBAAyB,CACvC,SAAS,GAAE,OAAO,CAAC,wBAAwB,CAAM,GAChD,OAAO,CAmBT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/prune/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE9E,OAAO,EAGL,gCAAgC,EAEhC,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,mBAAmB,EACnB,KAAK,sBAAsB,EAC5B,MAAM,uCAAuC,CAAC;AAK/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAUjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,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,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,gCAAgC,EAAE,OAAO,gCAAgC,CAAC;IAC1E,WAAW,EAAE,CACX,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE;QACP,KAAK,EAAE,OAAO,CAAC;QACf,MAAM,EAAE,OAAO,CAAC;QAChB,sBAAsB,CAAC,EAAE,MAAM,CAAC;KACjC,KACE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1B,gBAAgB,EAAE,CAChB,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,GAAG,CAAC,EAAE,MAAM,KACT,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,GAAG,SAAa,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BxB;AAyCD,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CA+BxC;AAgGD,wBAAgB,yBAAyB,CACvC,SAAS,GAAE,OAAO,CAAC,wBAAwB,CAAM,GAChD,OAAO,CAmBT"}
@@ -1,26 +1,30 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
2
+ import { basename, isAbsolute, join, resolve } from 'node:path';
3
3
  import { buildCommandContext, } from '../../../app/command-context.js';
4
4
  import { defaultGitRunner } from '../../project/sync/git.js';
5
5
  import { readSyncedRecord } from '../../project/sync/record.js';
6
- import { pruneSynced, } from '../../project/sync/ref-sync.js';
7
- import { resolveSyncedTarget } from '../../project/sync/resolve-target.js';
6
+ import { buildSyncTarget, classifyRemoteRefLookup, deleteCompletedSyncedRefForPrune, pruneSynced, } from '../../project/sync/ref-sync.js';
7
+ import { probeSyncedTerminalRefs, resolveSyncedTarget, } from '../../project/sync/resolve-target.js';
8
8
  import { getFrontmatterBlock, parseFrontmatterScalarFields, } from '../../shared/frontmatter.js';
9
- import { syncedRecordPath } from '../../shared/project-scope.js';
9
+ import { resolveProjectsRoot } from '../../shared/oat-paths.js';
10
+ import { completedSyncedRefName, resolveProjectScope, resolveScopeRoot, syncedRecordPath, } from '../../shared/project-scope.js';
10
11
  import { readGlobalOptions } from '../../shared/shared.utils.js';
11
12
  import { CliError } from '../../../errors/cli-error.js';
12
13
  import { resolveProjectRoot } from '../../../fs/paths.js';
13
14
  import { Command } from 'commander';
14
- export async function readPruneProjectState(target, git) {
15
+ export async function readPruneProjectState(target, git, ref = target.ref) {
15
16
  try {
16
17
  return await readFile(join(target.projectPath, 'state.md'), 'utf8');
17
18
  }
18
19
  catch {
19
20
  // A completed or not-yet-pulled project may have no checkout.
20
21
  }
21
- const fetched = await git.run(['fetch', target.remote, `+${target.ref}:${target.ref}`], { cwd: target.repoRoot, allowFailure: true });
22
+ const fetched = await git.run(['fetch', target.remote, `+${ref}:${ref}`], {
23
+ cwd: target.repoRoot,
24
+ allowFailure: true,
25
+ });
22
26
  if (fetched.code === 0) {
23
- const shown = await git.run(['show', `${target.ref}:state.md`], {
27
+ const shown = await git.run(['show', `${ref}:state.md`], {
24
28
  cwd: target.repoRoot,
25
29
  allowFailure: true,
26
30
  });
@@ -39,7 +43,10 @@ export async function readPruneProjectState(target, git) {
39
43
  const DEFAULT_DEPENDENCIES = {
40
44
  buildCommandContext,
41
45
  resolveProjectRoot,
46
+ resolveProjectsRoot,
42
47
  resolveSyncedTarget,
48
+ inspectTerminalRefs,
49
+ deleteCompletedSyncedRefForPrune,
43
50
  pruneSynced,
44
51
  readProjectState: readPruneProjectState,
45
52
  gitRunner: defaultGitRunner,
@@ -52,20 +59,83 @@ function hasOpenPr(state) {
52
59
  const parsed = parseFrontmatterScalarFields(frontmatter, ['oat_pr_status']);
53
60
  return parsed.valid && parsed.values.oat_pr_status === 'open';
54
61
  }
62
+ function pruneSlug(repoRoot, projectsRoot, pathOrSlug) {
63
+ if (!pathOrSlug.includes('/') && !pathOrSlug.includes('\\')) {
64
+ return pathOrSlug;
65
+ }
66
+ const absolute = isAbsolute(pathOrSlug)
67
+ ? resolve(pathOrSlug)
68
+ : resolve(repoRoot, pathOrSlug);
69
+ const scope = resolveProjectScope(absolute, resolveScopeRoot(repoRoot, projectsRoot, 'shared'), repoRoot);
70
+ return scope === 'synced' ? basename(absolute) : null;
71
+ }
72
+ export async function inspectTerminalRefs(target, git) {
73
+ const completedRef = completedSyncedRefName(target.slug);
74
+ const lookup = await git.run(['ls-remote', '--exit-code', target.remote, completedRef], { cwd: target.repoRoot, allowFailure: true });
75
+ if (classifyRemoteRefLookup(lookup, target.remote, completedRef) === 'absent') {
76
+ return null;
77
+ }
78
+ const rows = lookup.stdout.split('\n').filter(Boolean);
79
+ const [sha, ref] = rows[0]?.trim().split(/\s+/) ?? [];
80
+ if (rows.length !== 1 ||
81
+ ref !== completedRef ||
82
+ !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(sha ?? '')) {
83
+ throw new CliError(`Unable to verify terminal refs for ${target.slug}: origin returned a malformed completed-ref advertisement.`, 2);
84
+ }
85
+ const probe = await probeSyncedTerminalRefs(target, sha, git);
86
+ if (probe.state === 'wrong-sha') {
87
+ throw new CliError(`Refusing destructive prune for ${target.slug}: active ${probe.activeRef} is ${probe.activeSha ?? 'absent'} while completed ${probe.completedRef} is ${probe.completedSha ?? 'absent'}. Repair the terminal ref mismatch before retrying; both refs were retained.`, 1);
88
+ }
89
+ return probe;
90
+ }
55
91
  async function runPrune(context, pathOrSlug, options, dependencies) {
56
92
  try {
57
93
  const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
58
- const target = await dependencies.resolveSyncedTarget({ repoRoot, env: dependencies.processEnv }, pathOrSlug, { gitRunner: dependencies.gitRunner }, {
59
- allowMissingCheckout: true,
60
- allowStagedPruneDeletion: true,
61
- });
62
- const state = await dependencies.readProjectState(target, dependencies.gitRunner);
94
+ const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
95
+ let target;
96
+ try {
97
+ target = await dependencies.resolveSyncedTarget({ repoRoot, env: dependencies.processEnv }, pathOrSlug, { gitRunner: dependencies.gitRunner }, {
98
+ allowMissingCheckout: true,
99
+ allowStagedPruneDeletion: true,
100
+ });
101
+ }
102
+ catch (error) {
103
+ const slug = pathOrSlug
104
+ ? pruneSlug(repoRoot, projectsRoot, pathOrSlug)
105
+ : null;
106
+ if (!slug ||
107
+ !(error instanceof CliError) ||
108
+ error.exitCode !== 1 ||
109
+ !error.message.startsWith('No synced project named ')) {
110
+ throw error;
111
+ }
112
+ target = buildSyncTarget(repoRoot, projectsRoot, slug);
113
+ }
114
+ const terminal = await dependencies.inspectTerminalRefs(target, dependencies.gitRunner);
115
+ const terminalRef = terminal?.completedRef;
116
+ const state = await dependencies.readProjectState(target, dependencies.gitRunner, terminalRef);
63
117
  if (!options.force && hasOpenPr(state)) {
64
118
  throw new CliError(`Refusing to prune ${target.slug} while its PR is open. Pass --force to confirm; pinned links will stop resolving.`, 1);
65
119
  }
66
- context.logger.warn(`Pruning ${target.slug} removes ${target.ref}; pinned links will stop resolving.`);
67
- const result = await dependencies.pruneSynced(target, dependencies.gitRunner, { force: options.force, commit: options.commit });
68
- const payload = { ...result, ref: target.ref };
120
+ const removedRefs = terminal?.state === 'completed-only'
121
+ ? terminal.completedRef
122
+ : terminalRef
123
+ ? `${target.ref} and ${terminalRef}`
124
+ : target.ref;
125
+ context.logger.warn(`Pruning ${target.slug} removes ${removedRefs}; pinned links will stop resolving. Durable local/S3 archives are preserved.`);
126
+ const result = await dependencies.pruneSynced(target, dependencies.gitRunner, {
127
+ force: options.force,
128
+ commit: options.commit,
129
+ ...(terminal ? { expectedActiveAliasSha: terminal.expectedSha } : {}),
130
+ });
131
+ const completedDeletion = terminalRef
132
+ ? await dependencies.deleteCompletedSyncedRefForPrune(target, dependencies.gitRunner)
133
+ : null;
134
+ const payload = {
135
+ ...result,
136
+ ref: target.ref,
137
+ completedRef: completedDeletion?.completedRef ?? null,
138
+ };
69
139
  if (context.json)
70
140
  context.logger.json(payload);
71
141
  else
@@ -2,10 +2,13 @@ import { type CommandContext, type GlobalOptions } from '../../../app/command-co
2
2
  import { type GitRunner } from '../../project/sync/git.js';
3
3
  import { commitRecordChange as defaultCommitRecordChange, type PullChildResult, type PullResult, type AdoptionRecordState, type SyncTarget } from '../../project/sync/ref-sync.js';
4
4
  import { resolveSyncedTarget } from '../../project/sync/resolve-target.js';
5
+ import { resolveProjectsRoot } from '../../shared/oat-paths.js';
5
6
  import { Command } from 'commander';
6
7
  interface ProjectPullDependencies {
7
8
  buildCommandContext: (options: GlobalOptions) => CommandContext;
8
9
  resolveProjectRoot: (cwd: string) => Promise<string>;
10
+ resolveProjectsRoot: typeof resolveProjectsRoot;
11
+ guardSyncedTerminalTarget: typeof guardSyncedTerminalTarget;
9
12
  resolveSyncedTarget: typeof resolveSyncedTarget;
10
13
  pullSynced: (target: SyncTarget, git: GitRunner, options?: {
11
14
  adopt?: boolean;
@@ -23,6 +26,7 @@ interface ProjectPullDependencies {
23
26
  gitRunner: GitRunner;
24
27
  processEnv: NodeJS.ProcessEnv;
25
28
  }
29
+ export declare function guardSyncedTerminalTarget(repoRoot: string, projectsRoot: string, pathOrSlug: string | undefined, action: 'pull' | 'open', git: GitRunner): Promise<void>;
26
30
  export declare function createProjectPullCommand(overrides?: Partial<ProjectPullDependencies>): Command;
27
31
  export {};
28
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/pull/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAEL,kBAAkB,IAAI,yBAAyB,EAI/C,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,mBAAmB,EAEpB,MAAM,uCAAuC,CAAC;AAI/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,UAAU,uBAAuB;IAC/B,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,UAAU,EAAE,CACV,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,cAAc,CAAC,EAAE,mBAAmB,CAAC;QACrC,GAAG,CAAC,EAAE,IAAI,CAAC;KACZ,KACE,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,YAAY,EAAE,CACZ,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,KACX,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAChC,kBAAkB,EAAE,OAAO,yBAAyB,CAAC;IACrD,cAAc,EAAE,CACd,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,cAAc,CAAC,EAAE,mBAAmB,CAAC;QACrC,GAAG,CAAC,EAAE,IAAI,CAAC;KACZ,KACE,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAiLD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAqBT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/pull/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;AAE9E,OAAO,EAEL,kBAAkB,IAAI,yBAAyB,EAI/C,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAGhB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,mBAAmB,EAEpB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAUjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,UAAU,uBAAuB;IAC/B,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,yBAAyB,EAAE,OAAO,yBAAyB,CAAC;IAC5D,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,UAAU,EAAE,CACV,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,cAAc,CAAC,EAAE,mBAAmB,CAAC;QACrC,GAAG,CAAC,EAAE,IAAI,CAAC;KACZ,KACE,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,YAAY,EAAE,CACZ,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,KACX,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAChC,kBAAkB,EAAE,OAAO,yBAAyB,CAAC;IACrD,cAAc,EAAE,CACd,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,cAAc,CAAC,EAAE,mBAAmB,CAAC;QACrC,GAAG,CAAC,EAAE,IAAI,CAAC;KACZ,KACE,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAgED,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,IAAI,CAAC,CA8Df;AA6LD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAqBT"}