@solaqua/gji 0.12.2 → 0.12.3
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/dist/bootstrap-output.d.ts +3 -2
- package/dist/bootstrap-output.js +1 -16
- package/dist/bootstrap-preview.d.ts +2 -3
- package/dist/bootstrap-preview.js +1 -11
- package/dist/cli.js +5 -1
- package/dist/config.d.ts +3 -13
- package/dist/config.js +12 -59
- package/dist/dependency-bootstrap.d.ts +11 -45
- package/dist/dependency-bootstrap.js +142 -608
- package/dist/gji-bundle.mjs +567 -2367
- package/dist/new.d.ts +4 -5
- package/dist/new.js +10 -45
- package/dist/pr.d.ts +4 -5
- package/dist/pr.js +9 -69
- package/dist/shell-completion.js +7 -5
- package/dist/uv-validation.d.ts +4 -0
- package/dist/uv-validation.js +210 -0
- package/dist/worktree-bootstrap.d.ts +5 -16
- package/dist/worktree-bootstrap.js +1 -24
- 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 +1 -1
- package/man/man1/gji-go.1 +1 -1
- 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 +8 -5
- package/man/man1/gji-open.1 +1 -1
- package/man/man1/gji-pr.1 +6 -3
- package/man/man1/gji-remove.1 +1 -1
- 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-task.1 +1 -1
- package/man/man1/gji-undo.1 +1 -1
- package/man/man1/gji-warp.1 +1 -1
- package/man/man1/gji.1 +2 -2
- package/package.json +1 -1
- package/dist/dependency-bootstrap-prompt.d.ts +0 -24
- package/dist/dependency-bootstrap-prompt.js +0 -103
- package/dist/dir-clone.d.ts +0 -32
- package/dist/dir-clone.js +0 -641
- package/dist/install-prompt.d.ts +0 -12
- package/dist/install-prompt.js +0 -127
- package/dist/package-manager.d.ts +0 -5
- package/dist/package-manager.js +0 -159
- package/dist/sync-directories.d.ts +0 -29
- package/dist/sync-directories.js +0 -77
- package/dist/sync-plan.d.ts +0 -16
- package/dist/sync-plan.js +0 -128
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { executeDependencyBootstrap, prepareDependencyBootstrap, } from "./dependency-bootstrap.js";
|
|
3
|
-
import { cloneDir } from "./dir-clone.js";
|
|
4
3
|
import { syncFiles } from "./file-sync.js";
|
|
5
4
|
import { extractHooks, runHook } from "./hooks.js";
|
|
6
|
-
import { maybeRunInstallPrompt, } from "./install-prompt.js";
|
|
7
5
|
import { assignWorktreeSlot } from "./slots.js";
|
|
8
|
-
import { executeSyncDirectoryPlan, } from "./sync-directories.js";
|
|
9
|
-
import { prepareSyncDirectoryPlan } from "./sync-plan.js";
|
|
10
6
|
export async function bootstrapWorktree(options) {
|
|
11
|
-
const dependencyMode = options.
|
|
7
|
+
const dependencyMode = options.dependencyMode;
|
|
12
8
|
const dependencyPlan = await prepareDependencyBootstrap(dependencyMode, {
|
|
13
9
|
currentRoot: options.currentRoot,
|
|
14
10
|
detectionRoot: options.dependencyDetectionRoot,
|
|
@@ -16,15 +12,6 @@ export async function bootstrapWorktree(options) {
|
|
|
16
12
|
cargoBuildCommand: options.config.dependencyBuildCommand,
|
|
17
13
|
worktreePath: options.worktreePath,
|
|
18
14
|
});
|
|
19
|
-
const syncPlan = await prepareSyncDirectoryPlan(options.repoRoot, options.worktreePath, options.config.syncDirs ?? []);
|
|
20
|
-
const outcomes = await executeSyncDirectoryPlan(syncPlan, {
|
|
21
|
-
cloneDirectory: options.cloneDirectory ?? cloneDir,
|
|
22
|
-
reporter: options.reporter,
|
|
23
|
-
});
|
|
24
|
-
const clonedDirs = outcomes.flatMap((outcome) => outcome.kind === "cloned" ? [outcome.directory] : []);
|
|
25
|
-
const skippedDirs = outcomes.flatMap((outcome) => outcome.kind === "skipped"
|
|
26
|
-
? [{ dir: outcome.dir, reason: outcome.reason }]
|
|
27
|
-
: []);
|
|
28
15
|
const syncFileFailures = [];
|
|
29
16
|
for (const pattern of options.config.syncFiles ?? []) {
|
|
30
17
|
try {
|
|
@@ -51,26 +38,18 @@ export async function bootstrapWorktree(options) {
|
|
|
51
38
|
const dependencyBootstrap = syncFileFailures.length > 0
|
|
52
39
|
? { mode: dependencyMode, ready: false, events: [] }
|
|
53
40
|
: await executeDependencyBootstrap(dependencyPlan, {
|
|
54
|
-
cloneDirectory: options.cloneDirectory,
|
|
55
41
|
reporter: options.reporter,
|
|
56
42
|
stderr: options.commandStderr ?? options.reporter.write,
|
|
57
43
|
stdout: options.commandStdout,
|
|
58
|
-
seededDirectories: clonedDirs.map(({ dir }) => dir),
|
|
59
44
|
runCommand: options.runCommand,
|
|
60
45
|
});
|
|
61
46
|
if (!dependencyBootstrap.ready) {
|
|
62
47
|
return {
|
|
63
|
-
clonedDirs,
|
|
64
48
|
dependencyBootstrap,
|
|
65
49
|
ready: false,
|
|
66
50
|
syncFileFailures,
|
|
67
|
-
skippedDirs,
|
|
68
51
|
};
|
|
69
52
|
}
|
|
70
|
-
if (dependencyMode === "off" &&
|
|
71
|
-
["default", "legacy"].includes(options.dependencyBootstrapPolicy?.source ?? "default")) {
|
|
72
|
-
await maybeRunInstallPrompt(options.worktreePath, options.repoRoot, options.config, options.reporter.write, options.installDependencies, options.nonInteractive);
|
|
73
|
-
}
|
|
74
53
|
const hooks = extractHooks(options.config);
|
|
75
54
|
const slot = await assignWorktreeSlot(options.repoRoot, options.worktreePath);
|
|
76
55
|
await runHook(hooks["after-create"], options.worktreePath, {
|
|
@@ -82,11 +61,9 @@ export async function bootstrapWorktree(options) {
|
|
|
82
61
|
? () => undefined
|
|
83
62
|
: (options.commandStdout ?? ((chunk) => process.stdout.write(chunk))));
|
|
84
63
|
return {
|
|
85
|
-
clonedDirs,
|
|
86
64
|
dependencyBootstrap,
|
|
87
65
|
ready: true,
|
|
88
66
|
syncFileFailures: [],
|
|
89
|
-
skippedDirs,
|
|
90
67
|
};
|
|
91
68
|
}
|
|
92
69
|
function toErrorMessage(error) {
|
package/man/man1/gji-back.1
CHANGED
package/man/man1/gji-clean.1
CHANGED
package/man/man1/gji-config.1
CHANGED
package/man/man1/gji-doctor.1
CHANGED
package/man/man1/gji-done.1
CHANGED
package/man/man1/gji-go.1
CHANGED
package/man/man1/gji-history.1
CHANGED
package/man/man1/gji-init.1
CHANGED
package/man/man1/gji-ls.1
CHANGED
package/man/man1/gji-new.1
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
.TH GJI\-NEW 1 "August 2026" "gji 0.12.
|
|
1
|
+
.TH GJI\-NEW 1 "August 2026" "gji 0.12.3" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
|
-
gji\-new \- create a new branch or detached linked worktree
|
|
3
|
+
gji\-new \- create a new branch or detached linked worktree
|
|
4
4
|
.SH SYNOPSIS
|
|
5
5
|
.B gji new [\fIoptions\fR] [options] [branch]
|
|
6
6
|
.SH DESCRIPTION
|
|
7
|
-
create a new branch or detached linked worktree
|
|
7
|
+
create a new branch or detached linked worktree
|
|
8
8
|
.SH BOOTSTRAP_POLICY
|
|
9
|
-
dependencyBuildCommand overrides Cargo's default cargo check. Supported lockfiles
|
|
9
|
+
dependencyBuildCommand overrides Cargo's default cargo check. Supported lockfiles are installed automatically in new worktrees; set dependencyBootstrap to off or pass \-\-no\-install to skip setup. Sync\-file failures stop dependency installation and after\-create hooks. JSON bootstrap events include structured reasons, and failed setup reports the created worktree path.
|
|
10
10
|
|
|
11
11
|
.SH BOOTSTRAP
|
|
12
|
-
|
|
12
|
+
Dependency bootstrap never copies dependency trees or build caches. pnpm, Yarn, Bun, and npm install JavaScript dependencies; uv, Poetry, and Pipenv install Python environments; Bundler uses vendor/bundle; Composer uses vendor; Go runs go mod download; and Cargo runs cargo check. Failed setup is reported directly without an ordinary\-copy fallback or automatic retry.
|
|
13
13
|
|
|
14
14
|
.SH OPTIONS
|
|
15
15
|
.TP
|
|
@@ -43,6 +43,9 @@ editor CLI to use with \-\-open (code, cursor, zed, …)
|
|
|
43
43
|
.B \-\-dry\-run
|
|
44
44
|
show what would be created without executing any git commands or writing files
|
|
45
45
|
.TP
|
|
46
|
+
.B \-\-no\-install
|
|
47
|
+
skip automatic dependency setup in the new worktree
|
|
48
|
+
.TP
|
|
46
49
|
.B \-\-json
|
|
47
50
|
emit JSON on success or error instead of human\-readable output
|
|
48
51
|
.SH "SEE ALSO"
|
package/man/man1/gji-open.1
CHANGED
package/man/man1/gji-pr.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH GJI\-PR 1 "August 2026" "gji 0.12.
|
|
1
|
+
.TH GJI\-PR 1 "August 2026" "gji 0.12.3" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
3
|
gji\-pr \- fetch a pull request by number, #number, or URL into a linked worktree
|
|
4
4
|
.SH SYNOPSIS
|
|
@@ -6,16 +6,19 @@ gji\-pr \- fetch a pull request by number, #number, or URL into a linked worktre
|
|
|
6
6
|
.SH DESCRIPTION
|
|
7
7
|
fetch a pull request by number, #number, or URL into a linked worktree
|
|
8
8
|
.SH BOOTSTRAP_POLICY
|
|
9
|
-
dependencyBuildCommand overrides Cargo's default cargo check. Supported lockfiles
|
|
9
|
+
dependencyBuildCommand overrides Cargo's default cargo check. Supported lockfiles are installed automatically in new worktrees; set dependencyBootstrap to off or pass \-\-no\-install to skip setup. Sync\-file failures stop dependency installation and after\-create hooks. JSON bootstrap events include structured reasons, and failed setup reports the created worktree path.
|
|
10
10
|
|
|
11
11
|
.SH BOOTSTRAP
|
|
12
|
-
|
|
12
|
+
Dependency bootstrap never copies dependency trees or build caches. pnpm, Yarn, Bun, and npm install JavaScript dependencies; uv, Poetry, and Pipenv install Python environments; Bundler uses vendor/bundle; Composer uses vendor; Go runs go mod download; and Cargo runs cargo check. Failed setup is reported directly without an ordinary\-copy fallback or automatic retry.
|
|
13
13
|
|
|
14
14
|
.SH OPTIONS
|
|
15
15
|
.TP
|
|
16
16
|
.B \-\-dry\-run
|
|
17
17
|
show what would be created without executing any git commands or writing files
|
|
18
18
|
.TP
|
|
19
|
+
.B \-\-no\-install
|
|
20
|
+
skip automatic dependency setup in the PR worktree
|
|
21
|
+
.TP
|
|
19
22
|
.B \-\-json
|
|
20
23
|
emit JSON on success or error instead of human\-readable output
|
|
21
24
|
.SH SUBCOMMANDS
|
package/man/man1/gji-remove.1
CHANGED
package/man/man1/gji-root.1
CHANGED
package/man/man1/gji-run-hook.1
CHANGED
package/man/man1/gji-status.1
CHANGED
package/man/man1/gji-sync.1
CHANGED
package/man/man1/gji-task.1
CHANGED
package/man/man1/gji-undo.1
CHANGED
package/man/man1/gji-warp.1
CHANGED
package/man/man1/gji.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH GJI 1 "August 2026" "gji 0.12.
|
|
1
|
+
.TH GJI 1 "August 2026" "gji 0.12.3" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
3
|
gji \- Context switching without the mess.
|
|
4
4
|
.SH SYNOPSIS
|
|
@@ -12,7 +12,7 @@ eliminating the need for \fBgit stash\fR when switching contexts.
|
|
|
12
12
|
.SH COMMANDS
|
|
13
13
|
.TP
|
|
14
14
|
.B new [options] [branch]
|
|
15
|
-
create a new branch or detached linked worktree
|
|
15
|
+
create a new branch or detached linked worktree
|
|
16
16
|
.TP
|
|
17
17
|
.B done [options] [branch]
|
|
18
18
|
finish the current linked worktree and return safely
|
package/package.json
CHANGED
|
@@ -1,24 +0,0 @@
|
|
|
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>;
|
|
@@ -1,103 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/dir-clone.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export interface CloneDirResult {
|
|
2
|
-
bytes?: number;
|
|
3
|
-
ms: number;
|
|
4
|
-
}
|
|
5
|
-
export interface CloneRequestOptions {
|
|
6
|
-
destinationRoot?: string;
|
|
7
|
-
measureBytes?: boolean;
|
|
8
|
-
}
|
|
9
|
-
export interface CloneDirOptions extends CloneRequestOptions {
|
|
10
|
-
platform?: NodeJS.Platform;
|
|
11
|
-
runLinuxCommand?: (command: string, args: string[]) => Promise<void>;
|
|
12
|
-
copyDirectory?: (source: string, destination: string) => Promise<void>;
|
|
13
|
-
copyFile?: (source: string, destination: string) => Promise<void>;
|
|
14
|
-
}
|
|
15
|
-
export type CloneDirectory = (source: string, destination: string, options?: CloneRequestOptions) => Promise<CloneDirResult>;
|
|
16
|
-
export declare function cloneDir(source: string, destination: string, options?: CloneDirOptions): Promise<CloneDirResult>;
|
|
17
|
-
export declare class CloneDestinationExistsError extends Error {
|
|
18
|
-
readonly code = "GJI_CLONE_DESTINATION_EXISTS";
|
|
19
|
-
constructor(destination: string);
|
|
20
|
-
}
|
|
21
|
-
export declare class CloneUnsupportedError extends Error {
|
|
22
|
-
readonly code = "GJI_CLONE_UNSUPPORTED";
|
|
23
|
-
constructor(reason: string);
|
|
24
|
-
}
|
|
25
|
-
export declare class CloneInProgressError extends Error {
|
|
26
|
-
readonly code = "GJI_CLONE_IN_PROGRESS";
|
|
27
|
-
constructor(destination: string);
|
|
28
|
-
}
|
|
29
|
-
export declare function isCloneDestinationExistsError(error: unknown): error is CloneDestinationExistsError;
|
|
30
|
-
export declare function isCloneUnsupportedError(error: unknown): boolean;
|
|
31
|
-
export declare function isCloneInProgressError(error: unknown): error is CloneInProgressError;
|
|
32
|
-
export declare function directorySize(path: string): Promise<number>;
|