@solaqua/gji 0.9.0 → 0.11.0
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/README.md +93 -20
- package/dist/back.d.ts +3 -0
- package/dist/back.js +19 -7
- package/dist/bootstrap-output.d.ts +3 -0
- package/dist/bootstrap-output.js +27 -0
- package/dist/bootstrap-preview.d.ts +10 -0
- package/dist/bootstrap-preview.js +21 -0
- package/dist/clean.js +25 -3
- package/dist/cli.js +105 -19
- package/dist/clone-failure-store.d.ts +18 -0
- package/dist/clone-failure-store.js +230 -0
- package/dist/command-runner.d.ts +6 -0
- package/dist/command-runner.js +38 -0
- package/dist/completion.js +7 -2
- package/dist/config-command.d.ts +1 -0
- package/dist/config-command.js +7 -1
- package/dist/config.d.ts +26 -1
- package/dist/config.js +110 -6
- package/dist/dependency-bootstrap-prompt.d.ts +24 -0
- package/dist/dependency-bootstrap-prompt.js +103 -0
- package/dist/dependency-bootstrap.d.ts +129 -0
- package/dist/dependency-bootstrap.js +566 -0
- package/dist/dir-clone.d.ts +33 -0
- package/dist/dir-clone.js +505 -0
- package/dist/done.d.ts +10 -0
- package/dist/done.js +208 -0
- package/dist/file-sync.js +71 -22
- package/dist/format-bytes.d.ts +1 -0
- package/dist/format-bytes.js +14 -0
- package/dist/fs-utils.d.ts +4 -0
- package/dist/fs-utils.js +26 -0
- package/dist/git.d.ts +5 -0
- package/dist/git.js +50 -0
- package/dist/gji-bundle.mjs +5489 -1206
- package/dist/go-resolver.d.ts +35 -0
- package/dist/go-resolver.js +185 -0
- package/dist/go.d.ts +5 -2
- package/dist/go.js +262 -62
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +13 -7
- package/dist/init.js +30 -22
- package/dist/install-prompt.d.ts +1 -0
- package/dist/install-prompt.js +3 -25
- package/dist/navigation-output.d.ts +11 -0
- package/dist/navigation-output.js +6 -0
- package/dist/new.d.ts +14 -2
- package/dist/new.js +434 -36
- package/dist/open.d.ts +1 -0
- package/dist/open.js +14 -2
- package/dist/pr.d.ts +6 -2
- package/dist/pr.js +135 -30
- package/dist/remove.js +20 -0
- package/dist/repo.d.ts +3 -0
- package/dist/repo.js +57 -6
- package/dist/safe-destination.d.ts +15 -0
- package/dist/safe-destination.js +166 -0
- package/dist/shell-completion.js +47 -14
- package/dist/sync-directories.d.ts +33 -0
- package/dist/sync-directories.js +97 -0
- package/dist/sync-plan.d.ts +16 -0
- package/dist/sync-plan.js +128 -0
- package/dist/sync.js +7 -13
- package/dist/undo.d.ts +36 -0
- package/dist/undo.js +392 -0
- package/dist/warp.d.ts +3 -1
- package/dist/warp.js +32 -125
- package/dist/worktree-bootstrap.d.ts +35 -0
- package/dist/worktree-bootstrap.js +93 -0
- package/dist/worktree-management.d.ts +1 -0
- package/dist/worktree-management.js +23 -4
- package/dist/worktree-picker.d.ts +21 -8
- package/dist/worktree-picker.js +107 -15
- package/dist/worktree-prompts.js +1 -1
- package/dist/worktree-source.d.ts +6 -0
- package/dist/worktree-source.js +1 -0
- package/dist/worktree-sources.d.ts +3 -0
- package/dist/worktree-sources.js +31 -0
- package/man/man1/gji-back.1 +1 -1
- package/man/man1/gji-clean.1 +1 -1
- package/man/man1/gji-completion.1 +1 -1
- package/man/man1/gji-config.1 +1 -1
- package/man/man1/gji-doctor.1 +1 -1
- package/man/man1/gji-done.1 +19 -0
- package/man/man1/gji-go.1 +9 -3
- package/man/man1/gji-history.1 +1 -1
- package/man/man1/gji-init.1 +1 -1
- package/man/man1/gji-ls.1 +1 -1
- package/man/man1/gji-new.1 +21 -3
- package/man/man1/gji-open.1 +4 -1
- package/man/man1/gji-pr.1 +7 -1
- package/man/man1/gji-remove.1 +3 -3
- package/man/man1/gji-root.1 +1 -1
- package/man/man1/gji-run-hook.1 +1 -1
- package/man/man1/gji-status.1 +1 -1
- package/man/man1/gji-sync-files.1 +1 -1
- package/man/man1/gji-sync.1 +1 -1
- package/man/man1/gji-undo.1 +16 -0
- package/man/man1/gji-warp.1 +3 -6
- package/man/man1/gji.1 +13 -5
- package/package.json +3 -1
package/dist/config.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute, join, normalize, resolve, win32, } from "node:path";
|
|
4
4
|
export const CONFIG_FILE_NAME = ".gji.json";
|
|
5
5
|
export const GLOBAL_CONFIG_DIRECTORY = ".config/gji";
|
|
6
6
|
export const GLOBAL_CONFIG_NAME = "config.json";
|
|
7
7
|
export const KNOWN_CONFIG_KEYS = new Set([
|
|
8
8
|
"branchPrefix",
|
|
9
|
+
"dependencyBuildCommand",
|
|
10
|
+
"dependencyBootstrap",
|
|
9
11
|
"editor",
|
|
10
12
|
"hooks",
|
|
11
13
|
"installSaveTarget",
|
|
12
14
|
"shellIntegration",
|
|
13
15
|
"skipInstallPrompt",
|
|
16
|
+
"syncDirs",
|
|
14
17
|
"syncDefaultBranch",
|
|
15
18
|
"syncFiles",
|
|
16
19
|
"syncRemote",
|
|
@@ -26,6 +29,9 @@ export async function loadConfig(root) {
|
|
|
26
29
|
return loadConfigFile(path);
|
|
27
30
|
}
|
|
28
31
|
export async function loadEffectiveConfig(root, home = homedir(), onWarning) {
|
|
32
|
+
return (await loadEffectiveConfigResult(root, home, onWarning)).config;
|
|
33
|
+
}
|
|
34
|
+
export async function loadEffectiveConfigResult(root, home = homedir(), onWarning) {
|
|
29
35
|
const [globalConfig, localConfig] = await Promise.all([
|
|
30
36
|
loadGlobalConfig(home),
|
|
31
37
|
loadConfig(root),
|
|
@@ -36,6 +42,7 @@ export async function loadEffectiveConfig(root, home = homedir(), onWarning) {
|
|
|
36
42
|
const perRepoConfig = isPlainObject(repos)
|
|
37
43
|
? findPerRepoConfig(repos, root, home)
|
|
38
44
|
: {};
|
|
45
|
+
validateSyncDirsConfig(perRepoConfig.syncDirs);
|
|
39
46
|
// Strip the internal `repos` registry from the global base before merging.
|
|
40
47
|
const globalBase = { ...globalConfig.config };
|
|
41
48
|
delete globalBase.repos;
|
|
@@ -76,12 +83,18 @@ export async function loadEffectiveConfig(root, home = homedir(), onWarning) {
|
|
|
76
83
|
Object.keys(localHooks).length > 0) {
|
|
77
84
|
merged.hooks = { ...globalHooks, ...perRepoHooks, ...localHooks };
|
|
78
85
|
}
|
|
79
|
-
return
|
|
86
|
+
return {
|
|
87
|
+
config: toEffectiveConfig(merged),
|
|
88
|
+
dependencyBootstrapExplicit: Object.hasOwn(globalBase, "dependencyBootstrap") ||
|
|
89
|
+
Object.hasOwn(perRepoConfig, "dependencyBootstrap") ||
|
|
90
|
+
Object.hasOwn(localConfig.config, "dependencyBootstrap"),
|
|
91
|
+
};
|
|
80
92
|
}
|
|
81
93
|
export async function loadGlobalConfig(home = homedir()) {
|
|
82
94
|
return loadConfigFile(GLOBAL_CONFIG_FILE_PATH(home));
|
|
83
95
|
}
|
|
84
96
|
export async function saveLocalConfig(root, config) {
|
|
97
|
+
validateConfigValues(config);
|
|
85
98
|
const path = join(root, CONFIG_FILE_NAME);
|
|
86
99
|
await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
87
100
|
return path;
|
|
@@ -96,6 +109,7 @@ export async function updateLocalConfigKey(root, key, value) {
|
|
|
96
109
|
return nextConfig;
|
|
97
110
|
}
|
|
98
111
|
export async function saveGlobalConfig(config, home = homedir()) {
|
|
112
|
+
validateConfigValues(config);
|
|
99
113
|
const path = GLOBAL_CONFIG_FILE_PATH(home);
|
|
100
114
|
await mkdir(dirname(path), { recursive: true });
|
|
101
115
|
await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
@@ -149,10 +163,68 @@ export function resolveConfigString(config, key) {
|
|
|
149
163
|
const value = config[key];
|
|
150
164
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
151
165
|
}
|
|
166
|
+
export function validateSyncDirsConfig(value) {
|
|
167
|
+
normalizeSyncDirsConfig(value);
|
|
168
|
+
}
|
|
169
|
+
export function normalizeDependencyBootstrap(value) {
|
|
170
|
+
if (value === undefined)
|
|
171
|
+
return;
|
|
172
|
+
if (value !== "off" &&
|
|
173
|
+
value !== "cow-then-repair" &&
|
|
174
|
+
value !== "install-only") {
|
|
175
|
+
throw new Error('dependencyBootstrap: expected "off", "cow-then-repair", or "install-only"');
|
|
176
|
+
}
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
export function normalizeSyncDirsConfig(value) {
|
|
180
|
+
if (value === undefined)
|
|
181
|
+
return;
|
|
182
|
+
if (!Array.isArray(value)) {
|
|
183
|
+
throw new Error("syncDirs: expected an array of relative directory paths");
|
|
184
|
+
}
|
|
185
|
+
return value.map((pattern) => {
|
|
186
|
+
if (typeof pattern !== "string") {
|
|
187
|
+
throw new Error("syncDirs: every entry must be a string");
|
|
188
|
+
}
|
|
189
|
+
return validateSyncDirPattern(pattern);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function validateConfigValues(config) {
|
|
193
|
+
validateSyncDirsConfig(config.syncDirs);
|
|
194
|
+
normalizeDependencyBootstrap(config.dependencyBootstrap);
|
|
195
|
+
const repos = config.repos;
|
|
196
|
+
if (!isPlainObject(repos))
|
|
197
|
+
return;
|
|
198
|
+
for (const repoConfig of Object.values(repos)) {
|
|
199
|
+
if (isPlainObject(repoConfig)) {
|
|
200
|
+
validateConfigValues(repoConfig);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export function validateSyncDirPattern(pattern) {
|
|
205
|
+
if (pattern.length === 0 ||
|
|
206
|
+
isAbsolute(pattern) ||
|
|
207
|
+
win32.isAbsolute(pattern)) {
|
|
208
|
+
throw new Error(`syncDirs: pattern must be a relative path, got: ${pattern}`);
|
|
209
|
+
}
|
|
210
|
+
const segments = pattern.split(/[\\/]+/u);
|
|
211
|
+
if (segments.includes("..")) {
|
|
212
|
+
throw new Error(`syncDirs: pattern must not contain '..' segments, got: ${pattern}`);
|
|
213
|
+
}
|
|
214
|
+
if (segments.some((segment) => segment.toLowerCase() === ".git")) {
|
|
215
|
+
throw new Error(`syncDirs: pattern must not include .git, got: ${pattern}`);
|
|
216
|
+
}
|
|
217
|
+
const normalized = normalize(pattern);
|
|
218
|
+
if (normalized === ".") {
|
|
219
|
+
throw new Error(`syncDirs: pattern must name a directory below the repository root, got: ${pattern}`);
|
|
220
|
+
}
|
|
221
|
+
return normalized;
|
|
222
|
+
}
|
|
152
223
|
async function loadConfigFile(path) {
|
|
153
224
|
try {
|
|
154
225
|
const rawConfig = await readFile(path, "utf8");
|
|
155
226
|
const parsedConfig = JSON.parse(rawConfig);
|
|
227
|
+
validateConfigValues(parsedConfig);
|
|
156
228
|
return {
|
|
157
229
|
config: mergeConfig(parsedConfig),
|
|
158
230
|
exists: true,
|
|
@@ -171,10 +243,42 @@ async function loadConfigFile(path) {
|
|
|
171
243
|
}
|
|
172
244
|
}
|
|
173
245
|
function mergeConfig(...values) {
|
|
174
|
-
return values.reduce((config, value) => ({
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
246
|
+
return values.reduce((config, value) => Object.assign(config, value), { ...DEFAULT_CONFIG });
|
|
247
|
+
}
|
|
248
|
+
function toEffectiveConfig(config) {
|
|
249
|
+
const effective = { ...config };
|
|
250
|
+
if (config.dependencyBootstrap !== undefined) {
|
|
251
|
+
effective.dependencyBootstrap = normalizeDependencyBootstrap(config.dependencyBootstrap);
|
|
252
|
+
}
|
|
253
|
+
for (const key of [
|
|
254
|
+
"branchPrefix",
|
|
255
|
+
"dependencyBuildCommand",
|
|
256
|
+
"editor",
|
|
257
|
+
"installSaveTarget",
|
|
258
|
+
"shellIntegration",
|
|
259
|
+
"syncDefaultBranch",
|
|
260
|
+
"syncRemote",
|
|
261
|
+
"worktreePath",
|
|
262
|
+
]) {
|
|
263
|
+
if (effective[key] !== undefined && typeof effective[key] !== "string") {
|
|
264
|
+
delete effective[key];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (effective.skipInstallPrompt !== undefined &&
|
|
268
|
+
typeof effective.skipInstallPrompt !== "boolean") {
|
|
269
|
+
delete effective.skipInstallPrompt;
|
|
270
|
+
}
|
|
271
|
+
if (config.syncDirs !== undefined) {
|
|
272
|
+
effective.syncDirs = normalizeSyncDirsConfig(config.syncDirs);
|
|
273
|
+
}
|
|
274
|
+
if (Array.isArray(config.syncFiles)) {
|
|
275
|
+
effective.syncFiles = config.syncFiles.filter((value) => typeof value === "string");
|
|
276
|
+
}
|
|
277
|
+
else
|
|
278
|
+
delete effective.syncFiles;
|
|
279
|
+
if (!isPlainObject(config.hooks))
|
|
280
|
+
delete effective.hooks;
|
|
281
|
+
return effective;
|
|
178
282
|
}
|
|
179
283
|
function findPerRepoConfig(repos, repoRoot, home) {
|
|
180
284
|
for (const [key, value] of Object.entries(repos)) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type DependencyBootstrapMode, type EffectiveGjiConfig } from "./config.js";
|
|
2
|
+
import { type DependencyBootstrapCandidate } from "./dependency-bootstrap.js";
|
|
3
|
+
export interface DependencyBootstrapPromptDependencies {
|
|
4
|
+
promptForDependencyBootstrap?: (candidate: DependencyBootstrapCandidate) => Promise<DependencyBootstrapMode | null>;
|
|
5
|
+
writeConfigKey?: (root: string, key: string, value: unknown) => Promise<void>;
|
|
6
|
+
writeGlobalRepoConfigKey?: (repoRoot: string, key: string, value: unknown) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export interface DependencyBootstrapPolicyResolution {
|
|
9
|
+
mode: DependencyBootstrapMode;
|
|
10
|
+
prompted: boolean;
|
|
11
|
+
source: "explicit" | "prompted" | "default" | "legacy";
|
|
12
|
+
}
|
|
13
|
+
export declare function resolveDependencyBootstrapPolicy(context: {
|
|
14
|
+
repoRoot: string;
|
|
15
|
+
currentRoot?: string;
|
|
16
|
+
detectionRoot?: string;
|
|
17
|
+
worktreePath: string;
|
|
18
|
+
}, config: EffectiveGjiConfig, dependencyBootstrapExplicit: boolean, options: {
|
|
19
|
+
dryRun?: boolean;
|
|
20
|
+
legacyInstallPromptConfigured?: boolean;
|
|
21
|
+
nonInteractive?: boolean;
|
|
22
|
+
stderr: (chunk: string) => void;
|
|
23
|
+
dependencies?: DependencyBootstrapPromptDependencies;
|
|
24
|
+
}): Promise<DependencyBootstrapPolicyResolution>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { isCancel, select } from "@clack/prompts";
|
|
2
|
+
import { updateGlobalRepoConfigKey, updateLocalConfigKey, } from "./config.js";
|
|
3
|
+
import { detectDependencyBootstrapCandidate, } from "./dependency-bootstrap.js";
|
|
4
|
+
import { isHeadless } from "./headless.js";
|
|
5
|
+
export async function resolveDependencyBootstrapPolicy(context, config, dependencyBootstrapExplicit, options) {
|
|
6
|
+
if (dependencyBootstrapExplicit) {
|
|
7
|
+
return {
|
|
8
|
+
mode: config.dependencyBootstrap ?? "off",
|
|
9
|
+
prompted: false,
|
|
10
|
+
source: "explicit",
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
if (options.dryRun || options.nonInteractive || isHeadless()) {
|
|
14
|
+
return { mode: "off", prompted: false, source: "default" };
|
|
15
|
+
}
|
|
16
|
+
if (options.legacyInstallPromptConfigured &&
|
|
17
|
+
!options.dependencies?.promptForDependencyBootstrap) {
|
|
18
|
+
return { mode: "off", prompted: false, source: "legacy" };
|
|
19
|
+
}
|
|
20
|
+
let candidate;
|
|
21
|
+
try {
|
|
22
|
+
candidate = await detectDependencyBootstrapCandidate(context);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
options.stderr(`gji: dependency setup detection failed: ${toErrorMessage(error)}\n`);
|
|
26
|
+
return { mode: "off", prompted: false, source: "default" };
|
|
27
|
+
}
|
|
28
|
+
if (!candidate)
|
|
29
|
+
return { mode: "off", prompted: false, source: "default" };
|
|
30
|
+
const prompt = options.dependencies?.promptForDependencyBootstrap ??
|
|
31
|
+
defaultPromptForDependencyBootstrap;
|
|
32
|
+
const choice = await prompt(candidate);
|
|
33
|
+
if (choice === null)
|
|
34
|
+
return { mode: "off", prompted: true, source: "prompted" };
|
|
35
|
+
const mode = choice;
|
|
36
|
+
await persistDependencyBootstrapPolicy(context.repoRoot, config, mode, options.dependencies, options.stderr);
|
|
37
|
+
return { mode, prompted: true, source: "prompted" };
|
|
38
|
+
}
|
|
39
|
+
async function persistDependencyBootstrapPolicy(repoRoot, config, mode, dependencies, stderr) {
|
|
40
|
+
const writeLocal = dependencies?.writeConfigKey ??
|
|
41
|
+
(async (root, key, value) => {
|
|
42
|
+
await updateLocalConfigKey(root, key, value);
|
|
43
|
+
});
|
|
44
|
+
const writeGlobal = dependencies?.writeGlobalRepoConfigKey ??
|
|
45
|
+
(async (root, key, value) => {
|
|
46
|
+
await updateGlobalRepoConfigKey(root, key, value);
|
|
47
|
+
});
|
|
48
|
+
try {
|
|
49
|
+
if (config.installSaveTarget === "global") {
|
|
50
|
+
await writeGlobal(repoRoot, "dependencyBootstrap", mode);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
await writeLocal(repoRoot, "dependencyBootstrap", mode);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
stderr(`gji: failed to save dependencyBootstrap: ${toErrorMessage(error)}\n`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function defaultPromptForDependencyBootstrap(candidate) {
|
|
61
|
+
const reuseHint = formatReuseHint(candidate);
|
|
62
|
+
const subject = candidate.kind === "build-cache"
|
|
63
|
+
? "dependencies and build state"
|
|
64
|
+
: "dependencies";
|
|
65
|
+
const choice = await select({
|
|
66
|
+
message: `Set up ${candidate.adapter} ${subject} for new worktrees? (${candidate.lockfile})`,
|
|
67
|
+
options: [
|
|
68
|
+
{
|
|
69
|
+
value: "cow-then-repair",
|
|
70
|
+
label: "Reuse and repair (recommended)",
|
|
71
|
+
hint: reuseHint,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
value: "install-only",
|
|
75
|
+
label: "Install fresh each time",
|
|
76
|
+
hint: candidate.repairCommand,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
value: "off",
|
|
80
|
+
label: "Skip dependency setup",
|
|
81
|
+
hint: "leave dependencies to project hooks or manual setup",
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
if (isCancel(choice))
|
|
86
|
+
return null;
|
|
87
|
+
return choice;
|
|
88
|
+
}
|
|
89
|
+
function formatReuseHint(candidate) {
|
|
90
|
+
if (candidate.adapter === "pnpm") {
|
|
91
|
+
return "reuse local node_modules through CoW, then run pnpm install --frozen-lockfile";
|
|
92
|
+
}
|
|
93
|
+
if (candidate.adapter === "npm") {
|
|
94
|
+
return "npm uses a clean install because node_modules is not seeded";
|
|
95
|
+
}
|
|
96
|
+
if (!candidate.seedable) {
|
|
97
|
+
return `repair ${candidate.target} with ${candidate.repairCommand}`;
|
|
98
|
+
}
|
|
99
|
+
return `reuse ${candidate.target} through CoW, then run ${candidate.repairCommand}`;
|
|
100
|
+
}
|
|
101
|
+
function toErrorMessage(error) {
|
|
102
|
+
return error instanceof Error ? error.message : String(error);
|
|
103
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { type CloneFailureStore } from "./clone-failure-store.js";
|
|
2
|
+
import { type CommandRunner } from "./command-runner.js";
|
|
3
|
+
import type { DependencyBootstrapMode } from "./config.js";
|
|
4
|
+
import { type CloneDirectory } from "./dir-clone.js";
|
|
5
|
+
export type BootstrapKind = "dependency" | "build-cache" | "sync-file";
|
|
6
|
+
export type BootstrapState = "seeded" | "repaired" | "installed" | "fallback" | "skipped" | "failed";
|
|
7
|
+
export type BootstrapStrategy = "cow-then-repair" | "repair-only" | "install-only";
|
|
8
|
+
export type BootstrapCommandRunner = CommandRunner;
|
|
9
|
+
export interface BootstrapPreparationContext {
|
|
10
|
+
detectionRoot?: string;
|
|
11
|
+
sourceRoot: string;
|
|
12
|
+
worktreePath: string;
|
|
13
|
+
}
|
|
14
|
+
export interface BootstrapExecutionContext {
|
|
15
|
+
runCommand: BootstrapCommandRunner;
|
|
16
|
+
stderr: (chunk: string) => void;
|
|
17
|
+
stdout: (chunk: string) => void;
|
|
18
|
+
}
|
|
19
|
+
export interface BootstrapTarget {
|
|
20
|
+
adapter: string;
|
|
21
|
+
kind: BootstrapKind;
|
|
22
|
+
relativePath: string;
|
|
23
|
+
sourceRoot: string;
|
|
24
|
+
worktreePath: string;
|
|
25
|
+
sourcePath: string;
|
|
26
|
+
targetPath: string;
|
|
27
|
+
repairCommand: string;
|
|
28
|
+
repairState: "repaired" | "installed";
|
|
29
|
+
existingBeforeBootstrap: boolean;
|
|
30
|
+
}
|
|
31
|
+
export type BootstrapTargetOwnership = "adapter" | "syncDirs" | "empty" | "preserve";
|
|
32
|
+
export interface BootstrapRepairFailureContext {
|
|
33
|
+
target: BootstrapTarget;
|
|
34
|
+
ownership: BootstrapTargetOwnership;
|
|
35
|
+
targetExistedBeforeRepair: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface BootstrapOutputMetadata {
|
|
38
|
+
adapter: string;
|
|
39
|
+
kind: BootstrapKind;
|
|
40
|
+
target: string;
|
|
41
|
+
repairCommand: string;
|
|
42
|
+
}
|
|
43
|
+
export interface BootstrapAdapter {
|
|
44
|
+
readonly kind: BootstrapKind;
|
|
45
|
+
readonly lockfile: string;
|
|
46
|
+
readonly name: string;
|
|
47
|
+
readonly relativePath: string;
|
|
48
|
+
detect(context: BootstrapPreparationContext): Promise<BootstrapTarget | null>;
|
|
49
|
+
seedPath(target: BootstrapTarget): string;
|
|
50
|
+
repair(target: BootstrapTarget, context: BootstrapExecutionContext): Promise<void>;
|
|
51
|
+
canSeed(target: BootstrapTarget): Promise<boolean>;
|
|
52
|
+
output(target: BootstrapTarget): BootstrapOutputMetadata;
|
|
53
|
+
shouldRetryAfterRepairFailure(context: BootstrapRepairFailureContext): boolean;
|
|
54
|
+
cleanupAfterRepairFailure(context: BootstrapRepairFailureContext): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
export interface DependencyBootstrapPlan {
|
|
57
|
+
mode: DependencyBootstrapMode;
|
|
58
|
+
targets: readonly PlannedBootstrapTarget[];
|
|
59
|
+
}
|
|
60
|
+
export interface PlannedBootstrapTarget {
|
|
61
|
+
adapter: BootstrapAdapter;
|
|
62
|
+
target: BootstrapTarget;
|
|
63
|
+
seedable: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface BootstrapEvent {
|
|
66
|
+
adapter: string;
|
|
67
|
+
kind: BootstrapKind;
|
|
68
|
+
reason?: string;
|
|
69
|
+
state: BootstrapState;
|
|
70
|
+
target: string;
|
|
71
|
+
message: string;
|
|
72
|
+
}
|
|
73
|
+
export interface DependencyBootstrapReporter {
|
|
74
|
+
readonly measureCloneSize: boolean;
|
|
75
|
+
dependency(event: BootstrapEvent): void;
|
|
76
|
+
}
|
|
77
|
+
export interface DependencyBootstrapReport {
|
|
78
|
+
mode: DependencyBootstrapMode;
|
|
79
|
+
ready: boolean;
|
|
80
|
+
events: readonly BootstrapEvent[];
|
|
81
|
+
}
|
|
82
|
+
export interface DependencyBootstrapDependencies {
|
|
83
|
+
cloneDirectory?: CloneDirectory;
|
|
84
|
+
failureStore?: CloneFailureStore;
|
|
85
|
+
runCommand?: BootstrapCommandRunner;
|
|
86
|
+
stderr?: (chunk: string) => void;
|
|
87
|
+
stdout?: (chunk: string) => void;
|
|
88
|
+
seededDirectories?: readonly string[];
|
|
89
|
+
}
|
|
90
|
+
export interface DependencyBootstrapPreview {
|
|
91
|
+
mode: DependencyBootstrapMode;
|
|
92
|
+
targets: readonly {
|
|
93
|
+
adapter: string;
|
|
94
|
+
kind: BootstrapKind;
|
|
95
|
+
target: string;
|
|
96
|
+
repairCommand: string;
|
|
97
|
+
seedable: boolean;
|
|
98
|
+
strategy: BootstrapStrategy;
|
|
99
|
+
}[];
|
|
100
|
+
}
|
|
101
|
+
export interface DependencyBootstrapCandidate {
|
|
102
|
+
adapter: string;
|
|
103
|
+
kind: BootstrapKind;
|
|
104
|
+
lockfile: string;
|
|
105
|
+
target: string;
|
|
106
|
+
repairCommand: string;
|
|
107
|
+
seedable: boolean;
|
|
108
|
+
}
|
|
109
|
+
export declare function prepareDependencyBootstrap(mode: DependencyBootstrapMode, context: {
|
|
110
|
+
repoRoot: string;
|
|
111
|
+
currentRoot?: string;
|
|
112
|
+
detectionRoot?: string;
|
|
113
|
+
worktreePath: string;
|
|
114
|
+
checkUvRuntime?: (target: BootstrapTarget) => Promise<boolean>;
|
|
115
|
+
cargoBuildCommand?: string;
|
|
116
|
+
}): Promise<DependencyBootstrapPlan>;
|
|
117
|
+
export declare function detectDependencyBootstrapCandidate(context: {
|
|
118
|
+
repoRoot: string;
|
|
119
|
+
currentRoot?: string;
|
|
120
|
+
detectionRoot?: string;
|
|
121
|
+
worktreePath: string;
|
|
122
|
+
checkUvRuntime?: (target: BootstrapTarget) => Promise<boolean>;
|
|
123
|
+
cargoBuildCommand?: string;
|
|
124
|
+
}): Promise<DependencyBootstrapCandidate | null>;
|
|
125
|
+
export declare function previewDependencyBootstrap(plan: DependencyBootstrapPlan): DependencyBootstrapPreview;
|
|
126
|
+
export declare function executeDependencyBootstrap(plan: DependencyBootstrapPlan, options: DependencyBootstrapDependencies & {
|
|
127
|
+
repoRoot: string;
|
|
128
|
+
reporter: DependencyBootstrapReporter;
|
|
129
|
+
}): Promise<DependencyBootstrapReport>;
|