@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.
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/tool-packs.md +35 -24
- package/assets/docs/reference/cli-reference.md +20 -5
- package/assets/docs/reference/file-locations.md +8 -2
- package/assets/docs/reference/oat-directory-structure.md +14 -5
- package/assets/docs/reference/troubleshooting.md +22 -4
- package/assets/docs/workflows/projects/artifacts.md +9 -4
- package/assets/docs/workflows/projects/lifecycle.md +17 -5
- package/assets/docs/workflows/projects/picking-up-projects.md +26 -9
- package/assets/docs/workflows/projects/pr-flow.md +5 -3
- package/assets/docs/workflows/projects/reviewing-oat-prs.md +12 -8
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +2 -2
- package/assets/skills/oat-project-complete/SKILL.md +193 -26
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +306 -0
- package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +125 -0
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +106 -0
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +110 -0
- package/assets/skills/oat-project-complete/scripts/resolve-synced-archive-entry.mjs +164 -0
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +2 -2
- package/dist/commands/doctor/index.d.ts +7 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +44 -23
- package/dist/commands/pjm/index.d.ts +0 -2
- package/dist/commands/pjm/index.d.ts.map +1 -1
- package/dist/commands/pjm/index.js +5 -4
- package/dist/commands/pjm/migrate.d.ts +2 -1
- package/dist/commands/pjm/migrate.d.ts.map +1 -1
- package/dist/commands/pjm/migrate.js +23 -17
- package/dist/commands/project/archive/archive-utils.d.ts +8 -1
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +147 -50
- package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
- package/dist/commands/project/archive/push-runner.js +15 -2
- package/dist/commands/project/links/compute.d.ts +1 -0
- package/dist/commands/project/links/compute.d.ts.map +1 -1
- package/dist/commands/project/links/compute.js +5 -4
- package/dist/commands/project/links/index.d.ts +9 -0
- package/dist/commands/project/links/index.d.ts.map +1 -1
- package/dist/commands/project/links/index.js +71 -2
- package/dist/commands/project/list.d.ts +10 -2
- package/dist/commands/project/list.d.ts.map +1 -1
- package/dist/commands/project/list.js +205 -29
- package/dist/commands/project/open/index.d.ts +2 -0
- package/dist/commands/project/open/index.d.ts.map +1 -1
- package/dist/commands/project/open/index.js +16 -1
- package/dist/commands/project/prune/index.d.ts +10 -4
- package/dist/commands/project/prune/index.d.ts.map +1 -1
- package/dist/commands/project/prune/index.js +85 -15
- package/dist/commands/project/pull/index.d.ts +4 -0
- package/dist/commands/project/pull/index.d.ts.map +1 -1
- package/dist/commands/project/pull/index.js +82 -3
- package/dist/commands/project/sync/git.d.ts +9 -0
- package/dist/commands/project/sync/git.d.ts.map +1 -1
- package/dist/commands/project/sync/git.js +18 -0
- package/dist/commands/project/sync/ref-sync.d.ts +16 -1
- package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
- package/dist/commands/project/sync/ref-sync.js +288 -17
- package/dist/commands/project/sync/resolve-target.d.ts +10 -0
- package/dist/commands/project/sync/resolve-target.d.ts.map +1 -1
- package/dist/commands/project/sync/resolve-target.js +56 -1
- package/dist/commands/shared/project-scope.d.ts +2 -0
- package/dist/commands/shared/project-scope.d.ts.map +1 -1
- package/dist/commands/shared/project-scope.js +5 -0
- package/dist/commands/state/generate.d.ts +2 -0
- package/dist/commands/state/generate.d.ts.map +1 -1
- package/dist/commands/state/generate.js +93 -28
- package/dist/commands/status/index.d.ts +3 -1
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +77 -10
- package/dist/commands/tools/shared/pack-inventory.d.ts +10 -0
- package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-inventory.js +71 -21
- package/package.json +2 -2
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
2
2
|
import { buildCommandContext, } from '../../../app/command-context.js';
|
|
3
3
|
import { defaultGitRunner } from '../../project/sync/git.js';
|
|
4
|
-
import {
|
|
4
|
+
import { buildSyncTarget, classifyRemoteRefLookup, } from '../../project/sync/ref-sync.js';
|
|
5
|
+
import { probeSyncedTerminalRefs, resolveSyncedTarget, } from '../../project/sync/resolve-target.js';
|
|
6
|
+
import { resolveProjectsRoot } from '../../shared/oat-paths.js';
|
|
7
|
+
import { completedSyncedRefName, resolveProjectScope, resolveScopeRoot, } from '../../shared/project-scope.js';
|
|
5
8
|
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
6
9
|
import { CliError } from '../../../errors/cli-error.js';
|
|
7
10
|
import { resolveProjectRoot } from '../../../fs/paths.js';
|
|
@@ -11,7 +14,9 @@ import { renderLinksBlock } from './render.js';
|
|
|
11
14
|
const DEFAULT_DEPENDENCIES = {
|
|
12
15
|
buildCommandContext,
|
|
13
16
|
resolveProjectRoot,
|
|
17
|
+
resolveProjectsRoot,
|
|
14
18
|
resolveSyncedTarget,
|
|
19
|
+
resolveLinksTarget,
|
|
15
20
|
computeLinksInput,
|
|
16
21
|
gitRunner: defaultGitRunner,
|
|
17
22
|
processEnv: process.env,
|
|
@@ -30,13 +35,77 @@ function normalizeDurableSummaryPath(repoRoot, durableSummaryPath) {
|
|
|
30
35
|
}
|
|
31
36
|
return repositoryRelative.split(sep).join('/');
|
|
32
37
|
}
|
|
38
|
+
function linksSlug(repoRoot, projectsRoot, pathOrSlug) {
|
|
39
|
+
if (!pathOrSlug.includes('/') && !pathOrSlug.includes('\\')) {
|
|
40
|
+
return pathOrSlug;
|
|
41
|
+
}
|
|
42
|
+
const absolute = isAbsolute(pathOrSlug)
|
|
43
|
+
? resolve(pathOrSlug)
|
|
44
|
+
: resolve(repoRoot, pathOrSlug);
|
|
45
|
+
const sharedRoot = resolveScopeRoot(repoRoot, projectsRoot, 'shared');
|
|
46
|
+
if (resolveProjectScope(absolute, sharedRoot, repoRoot) !== 'synced') {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const child = relative(resolveScopeRoot(repoRoot, projectsRoot, 'synced'), absolute);
|
|
50
|
+
return child && !child.includes(sep) ? child : null;
|
|
51
|
+
}
|
|
52
|
+
export async function resolveLinksTarget(repoRoot, projectsRoot, pathOrSlug, env, git, resolveTarget) {
|
|
53
|
+
let resolved = null;
|
|
54
|
+
let resolutionError;
|
|
55
|
+
try {
|
|
56
|
+
resolved = await resolveTarget({ repoRoot, env }, pathOrSlug, { gitRunner: git }, { allowMissingCheckout: true });
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
resolutionError = error;
|
|
60
|
+
if (!(error instanceof CliError) ||
|
|
61
|
+
error.exitCode !== 1 ||
|
|
62
|
+
!error.message.startsWith('No synced project named ')) {
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const slug = pathOrSlug
|
|
67
|
+
? linksSlug(repoRoot, projectsRoot, pathOrSlug)
|
|
68
|
+
: resolved?.slug;
|
|
69
|
+
if (!slug) {
|
|
70
|
+
if (resolutionError)
|
|
71
|
+
throw resolutionError;
|
|
72
|
+
throw new CliError('No synced project is available for link rendering.', 1);
|
|
73
|
+
}
|
|
74
|
+
const target = resolved ?? buildSyncTarget(repoRoot, projectsRoot, slug);
|
|
75
|
+
const completedRef = completedSyncedRefName(slug);
|
|
76
|
+
const lookup = await git.run(['ls-remote', '--exit-code', target.remote, completedRef], { cwd: repoRoot, allowFailure: true });
|
|
77
|
+
if (classifyRemoteRefLookup(lookup, target.remote, completedRef) === 'absent') {
|
|
78
|
+
if (resolutionError)
|
|
79
|
+
throw resolutionError;
|
|
80
|
+
return { target, ref: target.ref };
|
|
81
|
+
}
|
|
82
|
+
const rows = lookup.stdout.split('\n').filter(Boolean);
|
|
83
|
+
const [sha, ref] = rows[0]?.trim().split(/\s+/) ?? [];
|
|
84
|
+
if (rows.length !== 1 ||
|
|
85
|
+
ref !== completedRef ||
|
|
86
|
+
!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(sha ?? '')) {
|
|
87
|
+
throw new CliError(`Unable to verify terminal links for ${slug}: origin returned a malformed completed-ref advertisement.`, 2);
|
|
88
|
+
}
|
|
89
|
+
const probe = await probeSyncedTerminalRefs(target, sha, git);
|
|
90
|
+
if (probe.state === 'wrong-sha') {
|
|
91
|
+
throw new CliError(`Cannot refresh links for ${slug}: active ${probe.activeRef} is ${probe.activeSha ?? 'absent'} while completed ${probe.completedRef} is ${probe.completedSha ?? 'absent'}. Repair the terminal ref mismatch first.`, 1);
|
|
92
|
+
}
|
|
93
|
+
if (probe.state === 'completed-only' || probe.state === 'both') {
|
|
94
|
+
return { target, ref: completedRef };
|
|
95
|
+
}
|
|
96
|
+
if (resolutionError)
|
|
97
|
+
throw resolutionError;
|
|
98
|
+
return { target, ref: target.ref };
|
|
99
|
+
}
|
|
33
100
|
async function runLinks(context, pathOrSlug, options, dependencies) {
|
|
34
101
|
try {
|
|
35
102
|
const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
36
|
-
const
|
|
103
|
+
const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
|
|
104
|
+
const { target, ref } = await dependencies.resolveLinksTarget(repoRoot, projectsRoot, pathOrSlug, dependencies.processEnv, dependencies.gitRunner, dependencies.resolveSyncedTarget);
|
|
37
105
|
const input = await dependencies.computeLinksInput(target, dependencies.gitRunner, {
|
|
38
106
|
durableSummaryPath: normalizeDurableSummaryPath(repoRoot, options.durableSummary),
|
|
39
107
|
now: dependencies.now(),
|
|
108
|
+
ref,
|
|
40
109
|
});
|
|
41
110
|
const markdown = renderLinksBlock(input);
|
|
42
111
|
if (context.json || options.format === 'json') {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type CommandContext, type GlobalOptions } from '../../app/command-context.js';
|
|
2
2
|
import { type GitRunner } from '../project/sync/git.js';
|
|
3
|
-
import { listSyncedRecords } from '../project/sync/record.js';
|
|
4
|
-
import {
|
|
3
|
+
import { listSyncedRecords, type SyncedProjectRecord } from '../project/sync/record.js';
|
|
4
|
+
import { buildSyncTarget } from '../project/sync/ref-sync.js';
|
|
5
|
+
import { probeSyncedTerminalRefs, type SyncedTerminalRefProbe } from '../project/sync/resolve-target.js';
|
|
6
|
+
import { type ProjectListRow, type ProjectSummary } from '@open-agent-toolkit/control-plane';
|
|
5
7
|
import { Command } from 'commander';
|
|
6
8
|
interface ProjectListDependencies {
|
|
7
9
|
buildCommandContext: (options: GlobalOptions) => CommandContext;
|
|
@@ -9,6 +11,8 @@ interface ProjectListDependencies {
|
|
|
9
11
|
resolveProjectsRoot: (repoRoot: string, env: NodeJS.ProcessEnv) => Promise<string>;
|
|
10
12
|
listProjects: (projectsRoot: string) => Promise<ProjectSummary[]>;
|
|
11
13
|
listSyncedRecords: typeof listSyncedRecords;
|
|
14
|
+
probeSyncedTerminalRefs: typeof probeSyncedTerminalRefs;
|
|
15
|
+
probeAuthoritativeCompletedRef: typeof probeAuthoritativeCompletedRef;
|
|
12
16
|
directoryExists: (path: string) => Promise<boolean>;
|
|
13
17
|
readProjectMetadata: (projectPath: string) => Promise<ProjectListMetadata>;
|
|
14
18
|
processEnv: NodeJS.ProcessEnv;
|
|
@@ -19,6 +23,10 @@ interface ProjectListMetadata {
|
|
|
19
23
|
phase: string;
|
|
20
24
|
phaseStatus: string;
|
|
21
25
|
}
|
|
26
|
+
export declare function classifyLegacySyncedRecord(record: SyncedProjectRecord, path: string, probe?: SyncedTerminalRefProbe): Extract<ProjectListRow, {
|
|
27
|
+
kind: 'recorded-absent' | 'recorded-terminal' | 'terminal-invalid';
|
|
28
|
+
}>;
|
|
29
|
+
export declare function probeAuthoritativeCompletedRef(target: ReturnType<typeof buildSyncTarget>, git: GitRunner): Promise<SyncedTerminalRefProbe | null>;
|
|
22
30
|
export declare function createProjectListCommand(overrides?: Partial<ProjectListDependencies>): Command;
|
|
23
31
|
export {};
|
|
24
32
|
//# sourceMappingURL=list.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/project/list.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/project/list.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,iBAAiB,EACjB,KAAK,mBAAmB,EACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,eAAe,EAEhB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,uBAAuB,EACvB,KAAK,sBAAsB,EAC5B,MAAM,uCAAuC,CAAC;AAY/C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAE5C,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,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAClE,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,8BAA8B,EAAE,OAAO,8BAA8B,CAAC;IACtE,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,mBAAmB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC3E,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AA6KD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,sBAAsB,GAC7B,OAAO,CACR,cAAc,EACd;IAAE,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,kBAAkB,CAAA;CAAE,CACvE,CA0FA;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,EAC1C,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAwBxC;AA8TD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAwBT"}
|
|
@@ -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
|
-
:
|
|
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 (!
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
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
|
-
|
|
231
|
-
if (
|
|
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;
|
|
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
|
-
|
|
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,
|
|
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"}
|