@solaqua/gji 0.12.5 → 0.14.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 +12 -0
- package/dist/application/worktree/catalog.d.ts +31 -0
- package/dist/application/worktree/catalog.js +88 -0
- package/dist/application/worktree/context-card.d.ts +14 -0
- package/dist/application/worktree/context-card.js +12 -0
- package/dist/{go-resolver.d.ts → application/worktree/go-resolution.d.ts} +12 -4
- package/dist/{go-resolver.js → application/worktree/go-resolution.js} +50 -46
- package/dist/application/worktree/read-models.d.ts +31 -0
- package/dist/application/worktree/sources.d.ts +16 -0
- package/dist/application/worktree/sources.js +86 -0
- package/dist/{back.d.ts → cli/commands/back.d.ts} +3 -1
- package/dist/{back.js → cli/commands/back.js} +8 -6
- package/dist/{clean.d.ts → cli/commands/clean.d.ts} +6 -3
- package/dist/cli/commands/clean.js +555 -0
- package/dist/{completion.js → cli/commands/completion.js} +2 -2
- package/dist/{config-command.d.ts → cli/commands/config-command.d.ts} +2 -0
- package/dist/{config-command.js → cli/commands/config-command.js} +7 -5
- package/dist/{doctor.d.ts → cli/commands/doctor.d.ts} +2 -0
- package/dist/{doctor.js → cli/commands/doctor.js} +23 -25
- package/dist/{done.d.ts → cli/commands/done.d.ts} +2 -0
- package/dist/{done.js → cli/commands/done.js} +26 -23
- package/dist/{go.d.ts → cli/commands/go.d.ts} +8 -2
- package/dist/{go.js → cli/commands/go.js} +68 -48
- package/dist/{history-command.d.ts → cli/commands/history-command.d.ts} +2 -0
- package/dist/{history-command.js → cli/commands/history-command.js} +3 -1
- package/dist/{hub.d.ts → cli/commands/hub.d.ts} +7 -3
- package/dist/{hub.js → cli/commands/hub.js} +26 -33
- package/dist/{init.d.ts → cli/commands/init.d.ts} +3 -1
- package/dist/{init.js → cli/commands/init.js} +37 -34
- package/dist/{ls.d.ts → cli/commands/ls.d.ts} +4 -2
- package/dist/{ls.js → cli/commands/ls.js} +8 -5
- package/dist/{new.d.ts → cli/commands/new.d.ts} +6 -2
- package/dist/{new.js → cli/commands/new.js} +237 -109
- package/dist/{open.d.ts → cli/commands/open.d.ts} +4 -2
- package/dist/{open.js → cli/commands/open.js} +19 -12
- package/dist/{pr-open.d.ts → cli/commands/pr-open.d.ts} +4 -2
- package/dist/{pr-open.js → cli/commands/pr-open.js} +20 -18
- package/dist/{pr.d.ts → cli/commands/pr.d.ts} +5 -3
- package/dist/{pr.js → cli/commands/pr.js} +34 -41
- package/dist/{remove.d.ts → cli/commands/remove.d.ts} +4 -2
- package/dist/{remove.js → cli/commands/remove.js} +23 -17
- package/dist/{root.d.ts → cli/commands/root.d.ts} +2 -0
- package/dist/{root.js → cli/commands/root.js} +4 -2
- package/dist/{run-hook.d.ts → cli/commands/run-hook.d.ts} +2 -0
- package/dist/{run-hook.js → cli/commands/run-hook.js} +7 -4
- package/dist/{status.d.ts → cli/commands/status.d.ts} +2 -0
- package/dist/{status.js → cli/commands/status.js} +10 -7
- package/dist/{sync-files-command.d.ts → cli/commands/sync-files-command.d.ts} +2 -0
- package/dist/{sync-files-command.js → cli/commands/sync-files-command.js} +11 -9
- package/dist/{sync.d.ts → cli/commands/sync.d.ts} +2 -0
- package/dist/{sync.js → cli/commands/sync.js} +7 -4
- package/dist/{task-command.d.ts → cli/commands/task-command.d.ts} +2 -0
- package/dist/{task-command.js → cli/commands/task-command.js} +4 -2
- package/dist/{undo.d.ts → cli/commands/undo.d.ts} +8 -6
- package/dist/{undo.js → cli/commands/undo.js} +44 -34
- package/dist/{warp.d.ts → cli/commands/warp.d.ts} +5 -2
- package/dist/{warp.js → cli/commands/warp.js} +25 -13
- package/dist/cli/dependencies.d.ts +114 -0
- package/dist/cli/dependencies.js +118 -0
- package/dist/{cli.d.ts → cli/program.d.ts} +4 -2
- package/dist/{cli.js → cli/program.js} +108 -124
- package/dist/domain/repository/context.d.ts +6 -0
- package/dist/domain/repository/context.js +1 -0
- package/dist/domain/repository/registry.d.ts +5 -0
- package/dist/domain/repository/registry.js +1 -0
- package/dist/domain/shared/concurrency.d.ts +1 -0
- package/dist/domain/shared/concurrency.js +20 -0
- package/dist/domain/worktree/matching.d.ts +4 -0
- package/dist/domain/worktree/matching.js +79 -0
- package/dist/domain/worktree/policy.d.ts +2 -0
- package/dist/domain/worktree/policy.js +58 -0
- package/dist/domain/worktree/pr-reference.d.ts +1 -0
- package/dist/domain/worktree/pr-reference.js +9 -0
- package/dist/{worktree-source.d.ts → domain/worktree/source.d.ts} +1 -1
- package/dist/domain/worktree/source.js +1 -0
- package/dist/domain/worktree/types.d.ts +5 -0
- package/dist/domain/worktree/types.js +1 -0
- package/dist/gji-bundle.mjs +8756 -7666
- package/dist/index.js +3 -3
- package/dist/{dependency-bootstrap.d.ts → infrastructure/bootstrap/dependency-bootstrap.d.ts} +3 -30
- package/dist/{dependency-bootstrap.js → infrastructure/bootstrap/dependency-bootstrap.js} +4 -4
- package/dist/infrastructure/git/health.d.ts +9 -0
- package/dist/infrastructure/git/health.js +44 -0
- package/dist/infrastructure/git/refs.d.ts +6 -0
- package/dist/{git.js → infrastructure/git/refs.js} +4 -62
- package/dist/infrastructure/git/runner.d.ts +2 -0
- package/dist/infrastructure/git/runner.js +16 -0
- package/dist/{editor.d.ts → infrastructure/integrations/editor.d.ts} +2 -6
- package/dist/{pull-requests.d.ts → infrastructure/integrations/pull-requests.d.ts} +3 -11
- package/dist/{config.d.ts → infrastructure/persistence/config.d.ts} +3 -17
- package/dist/{config.js → infrastructure/persistence/config.js} +2 -17
- package/dist/{history.d.ts → infrastructure/persistence/history.d.ts} +2 -5
- package/dist/{task.js → infrastructure/persistence/task.js} +1 -1
- package/dist/infrastructure/process/command-runner.d.ts +3 -0
- package/dist/{hooks.d.ts → infrastructure/process/hooks.d.ts} +2 -12
- package/dist/infrastructure/repository/adapters.d.ts +7 -0
- package/dist/infrastructure/repository/adapters.js +23 -0
- package/dist/infrastructure/repository/context.d.ts +3 -0
- package/dist/infrastructure/repository/context.js +20 -0
- package/dist/infrastructure/repository/refs.d.ts +3 -0
- package/dist/infrastructure/repository/refs.js +37 -0
- package/dist/{repo-registry.d.ts → infrastructure/repository/registry.d.ts} +1 -5
- package/dist/{repo-registry.js → infrastructure/repository/registry.js} +1 -1
- package/dist/infrastructure/repository/worktrees.d.ts +2 -0
- package/dist/infrastructure/repository/worktrees.js +48 -0
- package/dist/{uv-validation.d.ts → infrastructure/validation/uv.d.ts} +1 -1
- package/dist/{uv-validation.js → infrastructure/validation/uv.js} +1 -1
- package/dist/infrastructure/worktree/bootstrap.d.ts +3 -0
- package/dist/{worktree-bootstrap.js → infrastructure/worktree/bootstrap.js} +7 -10
- package/dist/infrastructure/worktree/info.d.ts +6 -0
- package/dist/infrastructure/worktree/info.js +61 -0
- package/dist/{worktree-management.d.ts → infrastructure/worktree/lifecycle.d.ts} +0 -6
- package/dist/{worktree-management.js → infrastructure/worktree/lifecycle.js} +1 -9
- package/dist/ports/bootstrap.d.ts +61 -0
- package/dist/ports/bootstrap.js +1 -0
- package/dist/ports/config.d.ts +21 -0
- package/dist/ports/config.js +17 -0
- package/dist/ports/editor.d.ts +6 -0
- package/dist/ports/editor.js +1 -0
- package/dist/ports/git.d.ts +8 -0
- package/dist/ports/git.js +1 -0
- package/dist/ports/history.d.ts +5 -0
- package/dist/ports/history.js +1 -0
- package/dist/ports/hooks.d.ts +12 -0
- package/dist/ports/hooks.js +1 -0
- package/dist/{command-runner.d.ts → ports/process.d.ts} +0 -1
- package/dist/ports/process.js +1 -0
- package/dist/ports/pull-requests.d.ts +11 -0
- package/dist/ports/pull-requests.js +1 -0
- package/dist/ports/repository.d.ts +17 -0
- package/dist/ports/repository.js +1 -0
- package/dist/{bootstrap-output.d.ts → presentation/bootstrap/output.d.ts} +1 -1
- package/dist/presentation/bootstrap/preview.d.ts +2 -0
- package/dist/{bootstrap-preview.js → presentation/bootstrap/preview.js} +0 -4
- package/dist/{shell-completion.js → presentation/shell/completion.js} +5 -4
- package/dist/presentation/terminal/context-card.d.ts +2 -0
- package/dist/{context-card.js → presentation/terminal/context-card.js} +3 -12
- package/dist/presentation/worktree/format.d.ts +4 -0
- package/dist/presentation/worktree/format.js +41 -0
- package/dist/{worktree-picker.d.ts → presentation/worktree/picker.d.ts} +16 -17
- package/dist/{worktree-picker.js → presentation/worktree/picker.js} +30 -184
- 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 +6 -3
- package/man/man1/gji-open.1 +1 -1
- package/man/man1/gji-pr.1 +1 -1
- 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/bootstrap-preview.d.ts +0 -9
- package/dist/clean.js +0 -313
- package/dist/context-card.d.ts +0 -1
- package/dist/format-bytes.d.ts +0 -1
- package/dist/format-bytes.js +0 -14
- package/dist/git.d.ts +0 -18
- package/dist/repo.d.ts +0 -20
- package/dist/repo.js +0 -161
- package/dist/worktree-bootstrap.d.ts +0 -24
- package/dist/worktree-info.d.ts +0 -37
- package/dist/worktree-info.js +0 -123
- package/dist/worktree-sources.d.ts +0 -4
- package/dist/worktree-sources.js +0 -77
- /package/dist/{worktree-source.js → application/worktree/read-models.js} +0 -0
- /package/dist/{completion.d.ts → cli/commands/completion.d.ts} +0 -0
- /package/dist/{headless.d.ts → cli/runtime/headless.d.ts} +0 -0
- /package/dist/{headless.js → cli/runtime/headless.js} +0 -0
- /package/dist/{paths.d.ts → domain/shared/paths.d.ts} +0 -0
- /package/dist/{paths.js → domain/shared/paths.js} +0 -0
- /package/dist/{file-sync.d.ts → infrastructure/filesystem/file-sync.d.ts} +0 -0
- /package/dist/{file-sync.js → infrastructure/filesystem/file-sync.js} +0 -0
- /package/dist/{fs-utils.d.ts → infrastructure/filesystem/fs-utils.d.ts} +0 -0
- /package/dist/{fs-utils.js → infrastructure/filesystem/fs-utils.js} +0 -0
- /package/dist/{safe-destination.d.ts → infrastructure/filesystem/safe-destination.d.ts} +0 -0
- /package/dist/{safe-destination.js → infrastructure/filesystem/safe-destination.js} +0 -0
- /package/dist/{browser.d.ts → infrastructure/integrations/browser.d.ts} +0 -0
- /package/dist/{browser.js → infrastructure/integrations/browser.js} +0 -0
- /package/dist/{editor.js → infrastructure/integrations/editor.js} +0 -0
- /package/dist/{pull-requests.js → infrastructure/integrations/pull-requests.js} +0 -0
- /package/dist/{history.js → infrastructure/persistence/history.js} +0 -0
- /package/dist/{slots.d.ts → infrastructure/persistence/slots.d.ts} +0 -0
- /package/dist/{slots.js → infrastructure/persistence/slots.js} +0 -0
- /package/dist/{task.d.ts → infrastructure/persistence/task.d.ts} +0 -0
- /package/dist/{command-runner.js → infrastructure/process/command-runner.js} +0 -0
- /package/dist/{hooks.js → infrastructure/process/hooks.js} +0 -0
- /package/dist/{bootstrap-output.js → presentation/bootstrap/output.js} +0 -0
- /package/dist/{conflict.d.ts → presentation/prompts/path-conflict.d.ts} +0 -0
- /package/dist/{conflict.js → presentation/prompts/path-conflict.js} +0 -0
- /package/dist/{shell-completion.d.ts → presentation/shell/completion.d.ts} +0 -0
- /package/dist/{shell-handoff.d.ts → presentation/shell/handoff.d.ts} +0 -0
- /package/dist/{shell-handoff.js → presentation/shell/handoff.js} +0 -0
- /package/dist/{shell-setup.d.ts → presentation/shell/setup.d.ts} +0 -0
- /package/dist/{shell-setup.js → presentation/shell/setup.js} +0 -0
- /package/dist/{shell.d.ts → presentation/shell/shell.d.ts} +0 -0
- /package/dist/{shell.js → presentation/shell/shell.js} +0 -0
- /package/dist/{navigation-output.d.ts → presentation/terminal/navigation.d.ts} +0 -0
- /package/dist/{navigation-output.js → presentation/terminal/navigation.js} +0 -0
- /package/dist/{terminal-text.d.ts → presentation/terminal/text.d.ts} +0 -0
- /package/dist/{terminal-text.js → presentation/terminal/text.js} +0 -0
- /package/dist/{worktree-prompts.d.ts → presentation/worktree/prompts.d.ts} +0 -0
- /package/dist/{worktree-prompts.js → presentation/worktree/prompts.js} +0 -0
|
@@ -5,17 +5,14 @@ import { homedir } from "node:os";
|
|
|
5
5
|
import { basename, dirname, join } from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { confirm, isCancel } from "@clack/prompts";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { loadRegistry, REGISTRY_FILE_PATH, removeMissingRegistryEntries, } from "./repo-registry.js";
|
|
13
|
-
import { resolveSupportedShell } from "./shell.js";
|
|
14
|
-
import { executableExists, hasShellIntegration, resolveCompletionPath, resolveShellConfigPath, } from "./shell-setup.js";
|
|
15
|
-
import { loadSlots } from "./slots.js";
|
|
8
|
+
import { executableExists, hasShellIntegration, resolveCompletionPath, resolveShellConfigPath, } from "../../presentation/shell/setup.js";
|
|
9
|
+
import { resolveSupportedShell, } from "../../presentation/shell/shell.js";
|
|
10
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
11
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
16
12
|
const execFileAsync = promisify(execFile);
|
|
17
13
|
const MINIMUM_GIT_VERSION = { major: 2, minor: 17 };
|
|
18
14
|
export async function runDoctorCommand(options) {
|
|
15
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
19
16
|
if (options.yes && !options.fix) {
|
|
20
17
|
const message = "--yes requires --fix";
|
|
21
18
|
if (options.json) {
|
|
@@ -28,14 +25,14 @@ export async function runDoctorCommand(options) {
|
|
|
28
25
|
}
|
|
29
26
|
const home = options.home ?? homedir();
|
|
30
27
|
const shell = resolveSupportedShell(undefined, options.shell ?? process.env.SHELL);
|
|
31
|
-
let inspection = await collectDoctorInspection(options.cwd, home, shell);
|
|
28
|
+
let inspection = await collectDoctorInspection(options.cwd, home, shell, runtime);
|
|
32
29
|
let fixes = [];
|
|
33
30
|
if (options.fix) {
|
|
34
31
|
fixes = buildDoctorFixes(inspection.missingRegistryPaths);
|
|
35
32
|
if (fixes.length > 0) {
|
|
36
33
|
const approval = await requestFixApproval(fixes, options);
|
|
37
34
|
if (approval === "apply") {
|
|
38
|
-
fixes = await applyDoctorFixes(fixes, home);
|
|
35
|
+
fixes = await applyDoctorFixes(fixes, home, runtime.registry);
|
|
39
36
|
}
|
|
40
37
|
else {
|
|
41
38
|
fixes = fixes.map((fix) => ({
|
|
@@ -47,7 +44,7 @@ export async function runDoctorCommand(options) {
|
|
|
47
44
|
}));
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
|
-
inspection = await collectDoctorInspection(options.cwd, home, shell);
|
|
47
|
+
inspection = await collectDoctorInspection(options.cwd, home, shell, runtime);
|
|
51
48
|
}
|
|
52
49
|
const problems = inspection.checks.filter((check) => check.status === "fail").length;
|
|
53
50
|
if (options.json) {
|
|
@@ -64,14 +61,15 @@ export async function runDoctorCommand(options) {
|
|
|
64
61
|
}
|
|
65
62
|
return problems > 0 ? 1 : 0;
|
|
66
63
|
}
|
|
67
|
-
async function collectDoctorInspection(cwd, home, shell) {
|
|
68
|
-
const
|
|
64
|
+
async function collectDoctorInspection(cwd, home, shell, runtime) {
|
|
65
|
+
const { CONFIG_FILE_NAME, GLOBAL_CONFIG_FILE_PATH, KNOWN_CONFIG_KEYS, KNOWN_GLOBAL_CONFIG_KEYS, } = runtime.configStore;
|
|
66
|
+
const repository = await detectRepositoryOrSkip(cwd, runtime.repositoryContext.detectRepository);
|
|
69
67
|
const globalConfig = await inspectConfig(GLOBAL_CONFIG_FILE_PATH(home), "global", KNOWN_GLOBAL_CONFIG_KEYS);
|
|
70
68
|
const localConfig = repository
|
|
71
69
|
? await inspectConfig(join(repository.repoRoot, CONFIG_FILE_NAME), "local", KNOWN_CONFIG_KEYS)
|
|
72
70
|
: null;
|
|
73
71
|
const effectiveConfig = resolveEffectiveConfig(globalConfig.config, localConfig?.config ?? {}, repository?.repoRoot, home);
|
|
74
|
-
const registry = await inspectRegistry(home);
|
|
72
|
+
const registry = await inspectRegistry(home, runtime.registry);
|
|
75
73
|
return {
|
|
76
74
|
checks: [
|
|
77
75
|
await checkGitVersion(),
|
|
@@ -82,13 +80,13 @@ async function collectDoctorInspection(cwd, home, shell) {
|
|
|
82
80
|
skippedCheck("local-config", "local config not checked outside a Git repository"),
|
|
83
81
|
await checkWorktreeBase(repository, effectiveConfig, home),
|
|
84
82
|
registry.check,
|
|
85
|
-
await checkEditor(effectiveConfig),
|
|
86
|
-
await checkOrphanSlots(home),
|
|
83
|
+
await checkEditor(effectiveConfig, runtime.integrations.EDITORS),
|
|
84
|
+
await checkOrphanSlots(home, runtime.slots.loadSlots),
|
|
87
85
|
],
|
|
88
86
|
missingRegistryPaths: registry.missingPaths,
|
|
89
87
|
};
|
|
90
88
|
}
|
|
91
|
-
async function checkOrphanSlots(home) {
|
|
89
|
+
async function checkOrphanSlots(home, loadSlots) {
|
|
92
90
|
const slots = await loadSlots(home);
|
|
93
91
|
const missing = (await Promise.all(Object.keys(slots).map(async (path) => {
|
|
94
92
|
try {
|
|
@@ -142,10 +140,10 @@ function isDoctorInteractive(options) {
|
|
|
142
140
|
return options.interactive;
|
|
143
141
|
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
144
142
|
}
|
|
145
|
-
async function applyDoctorFixes(fixes, home) {
|
|
143
|
+
async function applyDoctorFixes(fixes, home, registry) {
|
|
146
144
|
return Promise.all(fixes.map(async (fix) => {
|
|
147
145
|
try {
|
|
148
|
-
const result = await removeMissingRegistryEntries(new Set(fix.paths ?? []), home);
|
|
146
|
+
const result = await registry.removeMissingRegistryEntries(new Set(fix.paths ?? []), home);
|
|
149
147
|
const removedCount = result.removedPaths.length;
|
|
150
148
|
const skippedCount = result.skippedPaths.length;
|
|
151
149
|
return {
|
|
@@ -168,7 +166,7 @@ async function applyDoctorFixes(fixes, home) {
|
|
|
168
166
|
}
|
|
169
167
|
}));
|
|
170
168
|
}
|
|
171
|
-
async function detectRepositoryOrSkip(cwd) {
|
|
169
|
+
async function detectRepositoryOrSkip(cwd, detectRepository) {
|
|
172
170
|
try {
|
|
173
171
|
return await detectRepository(cwd);
|
|
174
172
|
}
|
|
@@ -342,8 +340,8 @@ async function findNearestExistingPath(path) {
|
|
|
342
340
|
}
|
|
343
341
|
}
|
|
344
342
|
}
|
|
345
|
-
async function inspectRegistry(home) {
|
|
346
|
-
const entries = await loadRegistry(home);
|
|
343
|
+
async function inspectRegistry(home, registry) {
|
|
344
|
+
const entries = await registry.loadRegistry(home);
|
|
347
345
|
const missingEntries = await Promise.all(entries.map(async (entry) => ({
|
|
348
346
|
entry,
|
|
349
347
|
status: await inspectRegistryPath(entry.path),
|
|
@@ -368,7 +366,7 @@ async function inspectRegistry(home) {
|
|
|
368
366
|
}
|
|
369
367
|
return {
|
|
370
368
|
check: failedCheck("repo-registry", `${entries.length} repos registered, ${messageParts.join(", ")}`, missingCount > 0
|
|
371
|
-
? `remove confirmed stale entries from ${REGISTRY_FILE_PATH(home)}; check permissions for inaccessible paths`
|
|
369
|
+
? `remove confirmed stale entries from ${registry.REGISTRY_FILE_PATH(home)}; check permissions for inaccessible paths`
|
|
372
370
|
: "check permissions for inaccessible paths before removing registry entries"),
|
|
373
371
|
missingPaths,
|
|
374
372
|
};
|
|
@@ -399,7 +397,7 @@ async function pathExists(path) {
|
|
|
399
397
|
return false;
|
|
400
398
|
}
|
|
401
399
|
}
|
|
402
|
-
async function checkEditor(config) {
|
|
400
|
+
async function checkEditor(config, editors) {
|
|
403
401
|
const editor = config.editor;
|
|
404
402
|
if (typeof editor !== "string" || editor.length === 0) {
|
|
405
403
|
return skippedCheck("editor", "editor not configured (optional)");
|
|
@@ -407,7 +405,7 @@ async function checkEditor(config) {
|
|
|
407
405
|
if (await executableExists(editor)) {
|
|
408
406
|
return okCheck("editor", `editor "${editor}" found on PATH`);
|
|
409
407
|
}
|
|
410
|
-
const knownEditor =
|
|
408
|
+
const knownEditor = editors.some(({ cli }) => cli === editor);
|
|
411
409
|
return failedCheck("editor", `editor "${editor}" was not found on PATH`, knownEditor
|
|
412
410
|
? `install ${editor} or choose another editor with: gji open --save`
|
|
413
411
|
: "choose another editor with: gji open --save");
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export interface DoneCommandOptions {
|
|
2
3
|
branch?: string;
|
|
3
4
|
cwd: string;
|
|
4
5
|
force?: boolean;
|
|
5
6
|
json?: boolean;
|
|
6
7
|
keepBranch?: boolean;
|
|
8
|
+
runtime?: CliRuntime<"git" | "config" | "configStore" | "historyStore" | "slots" | "hooks" | "repositoryContext" | "worktrees" | "worktreeLifecycle">;
|
|
7
9
|
stderr: (chunk: string) => void;
|
|
8
10
|
stdout: (chunk: string) => void;
|
|
9
11
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { confirm, isCancel } from "@clack/prompts";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { isHeadless } from "
|
|
6
|
-
import { loadHistory } from "./history.js";
|
|
7
|
-
import { extractHooks, runHook } from "./hooks.js";
|
|
8
|
-
import { detectRepository, listWorktrees } from "./repo.js";
|
|
9
|
-
import { writeShellOutput } from "./shell-handoff.js";
|
|
10
|
-
import { releaseWorktreeSlot } from "./slots.js";
|
|
3
|
+
import { writeShellOutput } from "../../presentation/shell/handoff.js";
|
|
4
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
5
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
11
6
|
import { finalizeUndoOperation, recordUndoOperation } from "./undo.js";
|
|
12
|
-
import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeForceRemovalError, removeWorktree, } from "./worktree-management.js";
|
|
13
7
|
const DONE_OUTPUT_FILE_ENV = "GJI_DONE_OUTPUT_FILE";
|
|
14
8
|
export async function runDoneCommand(options) {
|
|
9
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
10
|
+
const { readWorktreeHealth, isBranchMergedInto, resolveRemoteDefaultBranch, runGit, } = runtime.git;
|
|
11
|
+
const { loadEffectiveConfig, resolveConfigString } = runtime.config;
|
|
12
|
+
const { loadHistory } = runtime.historyStore;
|
|
13
|
+
const { releaseWorktreeSlot } = runtime.slots;
|
|
14
|
+
const { extractHooks, runHook } = runtime.hooks;
|
|
15
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
16
|
+
const { listWorktrees } = runtime.worktrees;
|
|
17
|
+
const { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeForceRemovalError, removeWorktree, } = runtime.worktreeLifecycle;
|
|
15
18
|
const repository = await detectRepository(options.cwd);
|
|
16
19
|
if (!repository.isWorktree && !options.branch)
|
|
17
20
|
return doneError(options, "gji done: not inside a linked worktree");
|
|
@@ -24,12 +27,12 @@ export async function runDoneCommand(options) {
|
|
|
24
27
|
? `gji done: no linked worktree found for ${options.branch}`
|
|
25
28
|
: "gji done: not inside a linked worktree");
|
|
26
29
|
const config = await loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
|
|
27
|
-
await refreshUpstream(repository.repoRoot, target.path);
|
|
30
|
+
await refreshUpstream(repository.repoRoot, target.path, runGit);
|
|
28
31
|
const health = await readWorktreeHealth(target.path);
|
|
29
32
|
if (target.branch &&
|
|
30
33
|
!options.force &&
|
|
31
34
|
!options.keepBranch &&
|
|
32
|
-
!(await isSafeToComplete(repository.repoRoot, target.branch, health, config))) {
|
|
35
|
+
!(await isSafeToComplete(repository.repoRoot, target.branch, health, config, resolveConfigString, resolveRemoteDefaultBranch, isBranchMergedInto, runGit))) {
|
|
33
36
|
if (options.json || isHeadless())
|
|
34
37
|
return doneError(options, "branch is not merged and its upstream is not gone; use --force");
|
|
35
38
|
const choice = await confirm({
|
|
@@ -55,7 +58,7 @@ export async function runDoneCommand(options) {
|
|
|
55
58
|
}
|
|
56
59
|
let journal;
|
|
57
60
|
try {
|
|
58
|
-
journal = await recordUndoOperation("done", repository.repoRoot, [target]);
|
|
61
|
+
journal = await recordUndoOperation("done", repository.repoRoot, [target], undefined, runtime);
|
|
59
62
|
}
|
|
60
63
|
catch (error) {
|
|
61
64
|
return doneError(options, `could not write undo journal; no worktree was removed: ${toMessage(error)}`);
|
|
@@ -72,12 +75,12 @@ export async function runDoneCommand(options) {
|
|
|
72
75
|
}
|
|
73
76
|
catch (error) {
|
|
74
77
|
if (!isWorktreeForceRemovalError(error)) {
|
|
75
|
-
await finalizeUndoOperation(journal, []);
|
|
78
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
76
79
|
return doneError(options, `Failed to remove worktree at ${target.path}: ${toMessage(error)}`);
|
|
77
80
|
}
|
|
78
81
|
if (!options.force) {
|
|
79
82
|
if (options.json || isHeadless()) {
|
|
80
|
-
await finalizeUndoOperation(journal, []);
|
|
83
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
81
84
|
return doneError(options, "worktree requires force removal; use --force");
|
|
82
85
|
}
|
|
83
86
|
const choice = await confirm({
|
|
@@ -87,7 +90,7 @@ export async function runDoneCommand(options) {
|
|
|
87
90
|
initialValue: false,
|
|
88
91
|
});
|
|
89
92
|
if (isCancel(choice) || !choice) {
|
|
90
|
-
await finalizeUndoOperation(journal, []);
|
|
93
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
91
94
|
return doneError(options, "Aborted");
|
|
92
95
|
}
|
|
93
96
|
}
|
|
@@ -95,7 +98,7 @@ export async function runDoneCommand(options) {
|
|
|
95
98
|
await forceRemoveWorktree(repository.repoRoot, target.path);
|
|
96
99
|
}
|
|
97
100
|
catch (forceError) {
|
|
98
|
-
await finalizeUndoOperation(journal, []);
|
|
101
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
99
102
|
return doneError(options, `Failed to remove worktree at ${target.path}: ${toMessage(forceError)}`);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
@@ -122,12 +125,12 @@ export async function runDoneCommand(options) {
|
|
|
122
125
|
}
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
|
-
await finalizeUndoOperation(journal, [target]);
|
|
128
|
+
await finalizeUndoOperation(journal, [target], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
126
129
|
await releaseWorktreeSlot(target.path);
|
|
127
130
|
await runGit(repository.repoRoot, ["worktree", "prune"]).catch(() => undefined);
|
|
128
131
|
const shouldMove = options.branch === undefined || target.path === repository.currentRoot;
|
|
129
132
|
const movedTo = shouldMove
|
|
130
|
-
? await resolveDoneDestination(repository.repoRoot, target.path)
|
|
133
|
+
? await resolveDoneDestination(repository.repoRoot, target.path, loadHistory, runGit)
|
|
131
134
|
: null;
|
|
132
135
|
if (!options.json && movedTo)
|
|
133
136
|
await writeShellOutput(DONE_OUTPUT_FILE_ENV, movedTo, options.stdout);
|
|
@@ -139,7 +142,7 @@ export async function runDoneCommand(options) {
|
|
|
139
142
|
options.stderr(`✓ removed ${target.branch ?? "detached worktree"} (worktree${branchDeleted ? " + branch" : ""})${movedTo ? ` → back at ${movedTo}` : ""} · undo: gji undo\n`);
|
|
140
143
|
return 0;
|
|
141
144
|
}
|
|
142
|
-
async function isSafeToComplete(repoRoot, branch, health, config) {
|
|
145
|
+
async function isSafeToComplete(repoRoot, branch, health, config, resolveConfigString, resolveRemoteDefaultBranch, isBranchMergedInto, runGit) {
|
|
143
146
|
if (health.upstreamGone)
|
|
144
147
|
return true;
|
|
145
148
|
const remote = resolveConfigString(config, "syncRemote") ?? "origin";
|
|
@@ -148,7 +151,7 @@ async function isSafeToComplete(repoRoot, branch, health, config) {
|
|
|
148
151
|
(await resolveRemoteDefaultBranch(repoRoot, remote).catch(() => null));
|
|
149
152
|
if (remoteDefault) {
|
|
150
153
|
for (const base of [`${remote}/${remoteDefault}`, remoteDefault]) {
|
|
151
|
-
if (await gitRefExists(repoRoot, base))
|
|
154
|
+
if (await gitRefExists(repoRoot, base, runGit))
|
|
152
155
|
return isBranchMergedInto(repoRoot, branch, base).catch(() => false);
|
|
153
156
|
}
|
|
154
157
|
return false;
|
|
@@ -162,7 +165,7 @@ async function isSafeToComplete(repoRoot, branch, health, config) {
|
|
|
162
165
|
? false
|
|
163
166
|
: isBranchMergedInto(repoRoot, branch, base).catch(() => false);
|
|
164
167
|
}
|
|
165
|
-
async function gitRefExists(repoRoot, ref) {
|
|
168
|
+
async function gitRefExists(repoRoot, ref, runGit) {
|
|
166
169
|
try {
|
|
167
170
|
await runGit(repoRoot, ["rev-parse", "--verify", ref]);
|
|
168
171
|
return true;
|
|
@@ -171,7 +174,7 @@ async function gitRefExists(repoRoot, ref) {
|
|
|
171
174
|
return false;
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
|
-
async function refreshUpstream(repoRoot, worktreePath) {
|
|
177
|
+
async function refreshUpstream(repoRoot, worktreePath, runGit) {
|
|
175
178
|
const upstream = await runGit(worktreePath, [
|
|
176
179
|
"rev-parse",
|
|
177
180
|
"--abbrev-ref",
|
|
@@ -183,7 +186,7 @@ async function refreshUpstream(repoRoot, worktreePath) {
|
|
|
183
186
|
return;
|
|
184
187
|
await runGit(repoRoot, ["fetch", "--prune", "--quiet", remote]).catch(() => undefined);
|
|
185
188
|
}
|
|
186
|
-
async function resolveDoneDestination(repoRoot, removedPath) {
|
|
189
|
+
async function resolveDoneDestination(repoRoot, removedPath, loadHistory, runGit) {
|
|
187
190
|
const history = await loadHistory();
|
|
188
191
|
for (const entry of history) {
|
|
189
192
|
if (entry.path !== removedPath && entry.path !== repoRoot) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { WorktreeCatalogDependencies } from "../../application/worktree/catalog.js";
|
|
2
|
+
import { type QueryWorktreePullRequests, type WorktreePromptEntry, type WorktreePromptScope } from "../../presentation/worktree/picker.js";
|
|
3
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
2
4
|
export interface GoCommandOptions {
|
|
3
5
|
branch?: string;
|
|
4
6
|
cwd: string;
|
|
@@ -8,11 +10,15 @@ export interface GoCommandOptions {
|
|
|
8
10
|
quiet?: boolean;
|
|
9
11
|
stderr: (chunk: string) => void;
|
|
10
12
|
stdout: (chunk: string) => void;
|
|
13
|
+
runtime?: GoRuntime;
|
|
11
14
|
}
|
|
15
|
+
type GoRuntime = CliRuntime<"bootstrap" | "config" | "contextCard" | "git" | "history" | "historyStore" | "hooks" | "integrations" | "repositoryContext" | "repositoryRefs" | "repositoryRegistry" | "slots" | "tasks" | "worktrees" | "worktreeCatalog">;
|
|
12
16
|
export interface GoCommandDependencies {
|
|
13
17
|
confirmBranchCreation: (branch: string) => Promise<boolean>;
|
|
14
|
-
promptForWorktree: (worktrees: WorktreePromptEntry[], scope?: WorktreePromptScope) => Promise<string | null>;
|
|
18
|
+
promptForWorktree: (worktrees: WorktreePromptEntry[], scope?: WorktreePromptScope, catalog?: WorktreeCatalogDependencies) => Promise<string | null>;
|
|
15
19
|
queryPullRequests: QueryWorktreePullRequests;
|
|
20
|
+
runtime: GoRuntime;
|
|
16
21
|
}
|
|
17
22
|
export declare function createGoCommand(dependencies?: Partial<GoCommandDependencies>): (options: GoCommandOptions) => Promise<number>;
|
|
18
23
|
export declare const runGoCommand: (options: GoCommandOptions) => Promise<number>;
|
|
24
|
+
export {};
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { confirm, isCancel } from "@clack/prompts";
|
|
3
|
+
import { loadContextCardModel } from "../../application/worktree/context-card.js";
|
|
4
|
+
import { resolveGoBranch, } from "../../application/worktree/go-resolution.js";
|
|
5
|
+
import { listRegisteredWorktreeSources } from "../../application/worktree/sources.js";
|
|
6
|
+
import { writeShellOutput } from "../../presentation/shell/handoff.js";
|
|
7
|
+
import { renderContextCard } from "../../presentation/terminal/context-card.js";
|
|
8
|
+
import { createNavigationRepository, createNavigationTarget, } from "../../presentation/terminal/navigation.js";
|
|
9
|
+
import { buildWorktreePromptEntries, promptForSingleWorktree, } from "../../presentation/worktree/picker.js";
|
|
10
|
+
import { defaultCliDependencies, withPullRequestQueries, } from "../dependencies.js";
|
|
11
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
3
12
|
import { runBackCommand } from "./back.js";
|
|
4
|
-
import { loadEffectiveConfig } from "./config.js";
|
|
5
|
-
import { renderContextCard } from "./context-card.js";
|
|
6
|
-
import { resolveGoBranch } from "./go-resolver.js";
|
|
7
|
-
import { isHeadless } from "./headless.js";
|
|
8
|
-
import { recordWorktreeUsage } from "./history.js";
|
|
9
|
-
import { extractHooks, runHook } from "./hooks.js";
|
|
10
|
-
import { createNavigationRepository, createNavigationTarget, } from "./navigation-output.js";
|
|
11
13
|
import { runNewCommand } from "./new.js";
|
|
12
14
|
import { runPrCommand } from "./pr.js";
|
|
13
|
-
import { detectRepository, listWorktrees, } from "./repo.js";
|
|
14
|
-
import { writeShellOutput } from "./shell-handoff.js";
|
|
15
|
-
import { getWorktreeSlot } from "./slots.js";
|
|
16
|
-
import { buildWorktreePromptEntries, promptForSingleWorktree, } from "./worktree-picker.js";
|
|
17
|
-
import { listRegisteredWorktreeSources } from "./worktree-sources.js";
|
|
18
15
|
const GO_OUTPUT_FILE_ENV = "GJI_GO_OUTPUT_FILE";
|
|
19
16
|
export function createGoCommand(dependencies = {}) {
|
|
20
17
|
const confirmBranchCreation = dependencies.confirmBranchCreation ?? defaultConfirmBranchCreation;
|
|
21
18
|
const prompt = dependencies.promptForWorktree ?? promptForWorktree;
|
|
19
|
+
const configuredRuntime = dependencies.runtime;
|
|
22
20
|
return async function runGoCommand(options) {
|
|
21
|
+
const runtime = configuredRuntime ?? options.runtime ?? defaultCliDependencies;
|
|
23
22
|
if (options.root) {
|
|
24
23
|
if (options.branch !== undefined) {
|
|
25
24
|
return emitError(options, "--root cannot be combined with a branch");
|
|
26
25
|
}
|
|
27
|
-
return navigateToRepositoryRoot(options);
|
|
26
|
+
return navigateToRepositoryRoot(options, runtime);
|
|
28
27
|
}
|
|
29
28
|
if (options.branch === "-") {
|
|
30
29
|
return runBackCommand({
|
|
@@ -34,9 +33,10 @@ export function createGoCommand(dependencies = {}) {
|
|
|
34
33
|
outputEnv: GO_OUTPUT_FILE_ENV,
|
|
35
34
|
stderr: options.stderr,
|
|
36
35
|
stdout: options.stdout,
|
|
36
|
+
runtime,
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
-
const [repository, currentWorktrees] = await readCurrentRepository(options.cwd);
|
|
39
|
+
const [repository, currentWorktrees] = await readCurrentRepository(options.cwd, runtime);
|
|
40
40
|
const currentSources = repository
|
|
41
41
|
? toPromptSources(repository.repoRoot, repository.repoName, currentWorktrees)
|
|
42
42
|
: [];
|
|
@@ -49,9 +49,16 @@ export function createGoCommand(dependencies = {}) {
|
|
|
49
49
|
let promptSources = currentSources;
|
|
50
50
|
const loadAllSources = async () => {
|
|
51
51
|
if (registeredSources === null) {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const registered = await listRegisteredWorktreeSources({
|
|
53
|
+
cwd: options.cwd,
|
|
54
|
+
repositoryPort: {
|
|
55
|
+
...runtime.repositoryContext,
|
|
56
|
+
...runtime.repositoryRegistry,
|
|
57
|
+
...runtime.worktrees,
|
|
58
|
+
},
|
|
54
59
|
});
|
|
60
|
+
registeredSources = registered.sources;
|
|
61
|
+
skippedRegisteredRepos = registered.skipped.length;
|
|
55
62
|
}
|
|
56
63
|
return deduplicateSources([...currentSources, ...registeredSources]);
|
|
57
64
|
};
|
|
@@ -75,10 +82,8 @@ export function createGoCommand(dependencies = {}) {
|
|
|
75
82
|
currentRepositoryScope = nextCurrentRepositoryScope;
|
|
76
83
|
promptSources = nextSources;
|
|
77
84
|
return {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
queryPullRequests: dependencies.queryPullRequests,
|
|
81
|
-
}),
|
|
85
|
+
sources: promptSources,
|
|
86
|
+
metadata: currentRepositoryScope ? "full" : "fast",
|
|
82
87
|
label: currentRepositoryScope
|
|
83
88
|
? "current repository"
|
|
84
89
|
: "all repositories",
|
|
@@ -91,38 +96,48 @@ export function createGoCommand(dependencies = {}) {
|
|
|
91
96
|
}
|
|
92
97
|
const promptEntries = await buildWorktreePromptEntries(promptSources, {
|
|
93
98
|
metadata: repository ? "full" : "fast",
|
|
94
|
-
|
|
99
|
+
catalog: withPullRequestQueries(runtime, dependencies.queryPullRequests),
|
|
95
100
|
});
|
|
96
|
-
const selectedPath = await prompt(promptEntries, scope);
|
|
101
|
+
const selectedPath = await prompt(promptEntries, scope, withPullRequestQueries(runtime, dependencies.queryPullRequests));
|
|
97
102
|
if (!selectedPath) {
|
|
98
103
|
options.stderr("Aborted\n");
|
|
99
104
|
return 1;
|
|
100
105
|
}
|
|
101
106
|
const selected = promptSources.find((source) => source.worktree.path === selectedPath);
|
|
102
|
-
return navigateToExistingWorktree(options, selectedPath, selected?.worktree);
|
|
107
|
+
return navigateToExistingWorktree(options, selectedPath, selected?.worktree, runtime);
|
|
103
108
|
}
|
|
104
109
|
const resolution = await resolveGoBranch({
|
|
105
110
|
branch: options.branch,
|
|
106
|
-
configStderr: options.json ? undefined : options.stderr,
|
|
107
111
|
cwd: options.cwd,
|
|
108
112
|
currentSources,
|
|
109
113
|
repository,
|
|
114
|
+
repositoryPort: runtime.repositoryRefs,
|
|
115
|
+
sourceDependencies: {
|
|
116
|
+
...runtime.repositoryContext,
|
|
117
|
+
...runtime.repositoryRegistry,
|
|
118
|
+
...runtime.worktrees,
|
|
119
|
+
},
|
|
120
|
+
configPort: runtime.config,
|
|
110
121
|
});
|
|
111
|
-
return handleGoBranchResolution(options, resolution, confirmBranchCreation, prompt, dependencies.queryPullRequests);
|
|
122
|
+
return handleGoBranchResolution(options, resolution, confirmBranchCreation, prompt, runtime, dependencies.queryPullRequests);
|
|
112
123
|
};
|
|
113
124
|
}
|
|
114
125
|
export const runGoCommand = createGoCommand();
|
|
115
|
-
async function handleGoBranchResolution(options, resolution, confirmBranchCreation, prompt, queryPullRequests) {
|
|
126
|
+
async function handleGoBranchResolution(options, resolution, confirmBranchCreation, prompt, runtime, queryPullRequests) {
|
|
127
|
+
if (!options.json) {
|
|
128
|
+
for (const warning of resolution.configWarnings ?? [])
|
|
129
|
+
options.stderr(warning);
|
|
130
|
+
}
|
|
116
131
|
switch (resolution.kind) {
|
|
117
132
|
case "existing":
|
|
118
|
-
return navigateToExistingWorktree(options, resolution.source.worktree.path, resolution.source.worktree);
|
|
133
|
+
return navigateToExistingWorktree(options, resolution.source.worktree.path, resolution.source.worktree, runtime);
|
|
119
134
|
case "ambiguous": {
|
|
120
135
|
if (options.json || isHeadless() || options.print) {
|
|
121
136
|
return emitAmbiguousCrossRepoError(options, resolution.matches);
|
|
122
137
|
}
|
|
123
138
|
const candidates = await buildWorktreePromptEntries(resolution.matches, {
|
|
124
139
|
metadata: "fast",
|
|
125
|
-
queryPullRequests,
|
|
140
|
+
catalog: withPullRequestQueries(runtime, queryPullRequests),
|
|
126
141
|
});
|
|
127
142
|
const selectedPath = await prompt(candidates);
|
|
128
143
|
if (!selectedPath) {
|
|
@@ -130,10 +145,10 @@ async function handleGoBranchResolution(options, resolution, confirmBranchCreati
|
|
|
130
145
|
return 1;
|
|
131
146
|
}
|
|
132
147
|
const selected = resolution.matches.find((source) => source.worktree.path === selectedPath);
|
|
133
|
-
return navigateToExistingWorktree(options, selectedPath, selected?.worktree);
|
|
148
|
+
return navigateToExistingWorktree(options, selectedPath, selected?.worktree, runtime);
|
|
134
149
|
}
|
|
135
150
|
case "create":
|
|
136
|
-
return createExistingBranchWorktree(options, confirmBranchCreation, resolution.repository, resolution.branch, resolution.mode, resolution.remote);
|
|
151
|
+
return createExistingBranchWorktree(options, confirmBranchCreation, resolution.repository, resolution.branch, resolution.mode, resolution.remote, runtime);
|
|
137
152
|
case "pull-request":
|
|
138
153
|
if (options.json || isHeadless() || options.print) {
|
|
139
154
|
return emitError(options, "PR navigation creates a worktree; use `gji pr <ref>` in an interactive shell");
|
|
@@ -144,6 +159,7 @@ async function handleGoBranchResolution(options, resolution, confirmBranchCreati
|
|
|
144
159
|
outputEnv: GO_OUTPUT_FILE_ENV,
|
|
145
160
|
stderr: options.stderr,
|
|
146
161
|
stdout: options.stdout,
|
|
162
|
+
runtime,
|
|
147
163
|
});
|
|
148
164
|
case "no-repository":
|
|
149
165
|
return emitNoRepositoryError(options, resolution.staleRegisteredRepos);
|
|
@@ -153,11 +169,11 @@ async function handleGoBranchResolution(options, resolution, confirmBranchCreati
|
|
|
153
169
|
return emitError(options, resolution.message);
|
|
154
170
|
}
|
|
155
171
|
}
|
|
156
|
-
async function readCurrentRepository(cwd) {
|
|
172
|
+
async function readCurrentRepository(cwd, runtime) {
|
|
157
173
|
try {
|
|
158
174
|
const [repository, worktrees] = await Promise.all([
|
|
159
|
-
detectRepository(cwd),
|
|
160
|
-
listWorktrees(cwd),
|
|
175
|
+
runtime.repositoryContext.detectRepository(cwd),
|
|
176
|
+
runtime.worktrees.listWorktrees(cwd),
|
|
161
177
|
]);
|
|
162
178
|
return [repository, worktrees];
|
|
163
179
|
}
|
|
@@ -179,7 +195,7 @@ function deduplicateSources(sources) {
|
|
|
179
195
|
}
|
|
180
196
|
return deduplicated;
|
|
181
197
|
}
|
|
182
|
-
async function createExistingBranchWorktree(options, confirmBranchCreation, repository, branch, mode, remote) {
|
|
198
|
+
async function createExistingBranchWorktree(options, confirmBranchCreation, repository, branch, mode, remote, runtime) {
|
|
183
199
|
if (options.json || isHeadless() || options.print) {
|
|
184
200
|
return emitError(options, `branch "${branch}" exists but has no worktree; use interactive gji go ${branch} to create one`);
|
|
185
201
|
}
|
|
@@ -195,41 +211,42 @@ async function createExistingBranchWorktree(options, confirmBranchCreation, repo
|
|
|
195
211
|
remote,
|
|
196
212
|
stderr: options.stderr,
|
|
197
213
|
stdout: options.stdout,
|
|
214
|
+
runtime,
|
|
198
215
|
});
|
|
199
216
|
}
|
|
200
|
-
async function navigateToExistingWorktree(options, path, worktree) {
|
|
201
|
-
const repository = await detectRepository(path);
|
|
217
|
+
async function navigateToExistingWorktree(options, path, worktree, runtime) {
|
|
218
|
+
const repository = await runtime.repositoryContext.detectRepository(path);
|
|
202
219
|
if (options.json) {
|
|
203
220
|
options.stdout(`${JSON.stringify(createNavigationTarget(createNavigationRepository(repository.repoName, repository.repoRoot), path, worktree?.branch ?? null), null, 2)}\n`);
|
|
204
221
|
return 0;
|
|
205
222
|
}
|
|
206
|
-
const config = await loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
|
|
207
|
-
const hooks = extractHooks(config);
|
|
208
|
-
await runHook(hooks["after-enter"], path, {
|
|
223
|
+
const config = await runtime.config.loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
|
|
224
|
+
const hooks = runtime.hooks.extractHooks(config);
|
|
225
|
+
await runtime.hooks.runHook(hooks["after-enter"], path, {
|
|
209
226
|
branch: worktree?.branch ?? undefined,
|
|
210
227
|
path,
|
|
211
228
|
repo: basename(repository.repoRoot),
|
|
212
|
-
slot: await getWorktreeSlot(path),
|
|
229
|
+
slot: await runtime.slots.getWorktreeSlot(path),
|
|
213
230
|
}, options.stderr);
|
|
214
231
|
if (!options.quiet) {
|
|
215
|
-
const card = await
|
|
232
|
+
const card = await loadContextCardModel(path, runtime.contextCard);
|
|
216
233
|
if (card)
|
|
217
|
-
options.stderr(`${card}\n`);
|
|
234
|
+
options.stderr(`${renderContextCard(card)}\n`);
|
|
218
235
|
}
|
|
219
|
-
await recordWorktreeUsage(path, worktree?.branch ?? null);
|
|
236
|
+
await runtime.history.recordWorktreeUsage(path, worktree?.branch ?? null);
|
|
220
237
|
await writeShellOutput(GO_OUTPUT_FILE_ENV, path, options.stdout);
|
|
221
238
|
return 0;
|
|
222
239
|
}
|
|
223
|
-
async function navigateToRepositoryRoot(options) {
|
|
240
|
+
async function navigateToRepositoryRoot(options, runtime) {
|
|
224
241
|
let repository;
|
|
225
242
|
try {
|
|
226
|
-
repository = await detectRepository(options.cwd);
|
|
243
|
+
repository = await runtime.repositoryContext.detectRepository(options.cwd);
|
|
227
244
|
}
|
|
228
245
|
catch {
|
|
229
246
|
return emitError(options, "not inside a git repository");
|
|
230
247
|
}
|
|
231
248
|
if (options.json) {
|
|
232
|
-
const rootWorktree = (await listWorktrees(repository.repoRoot)).find((worktree) => worktree.path === repository.repoRoot);
|
|
249
|
+
const rootWorktree = (await runtime.worktrees.listWorktrees(repository.repoRoot)).find((worktree) => worktree.path === repository.repoRoot);
|
|
233
250
|
options.stdout(`${JSON.stringify(createNavigationTarget(createNavigationRepository(repository.repoName, repository.repoRoot), repository.repoRoot, rootWorktree?.branch ?? null), null, 2)}\n`);
|
|
234
251
|
return 0;
|
|
235
252
|
}
|
|
@@ -288,6 +305,9 @@ async function defaultConfirmBranchCreation(branch) {
|
|
|
288
305
|
});
|
|
289
306
|
return !isCancel(choice) && choice;
|
|
290
307
|
}
|
|
291
|
-
async function promptForWorktree(worktrees, scope) {
|
|
292
|
-
return promptForSingleWorktree("Choose a worktree", worktrees, {
|
|
308
|
+
async function promptForWorktree(worktrees, scope, catalog) {
|
|
309
|
+
return promptForSingleWorktree("Choose a worktree", worktrees, {
|
|
310
|
+
catalog,
|
|
311
|
+
scope,
|
|
312
|
+
});
|
|
293
313
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export interface HistoryCommandOptions {
|
|
2
3
|
cwd: string;
|
|
3
4
|
home?: string;
|
|
4
5
|
json?: boolean;
|
|
6
|
+
runtime?: CliRuntime<"historyStore">;
|
|
5
7
|
stdout: (chunk: string) => void;
|
|
6
8
|
}
|
|
7
9
|
export declare function runHistoryCommand(options: HistoryCommandOptions): Promise<number>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { defaultCliDependencies } from "../dependencies.js";
|
|
1
2
|
import { formatHistoryList } from "./back.js";
|
|
2
|
-
import { loadHistory } from "./history.js";
|
|
3
3
|
export async function runHistoryCommand(options) {
|
|
4
|
+
const { loadHistory } = (options.runtime ?? defaultCliDependencies)
|
|
5
|
+
.historyStore;
|
|
4
6
|
const history = await loadHistory(options.home);
|
|
5
7
|
if (options.json) {
|
|
6
8
|
options.stdout(`${JSON.stringify(history, null, 2)}\n`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { WorktreeInfo } from "../../application/worktree/read-models.js";
|
|
2
|
+
import type { PullRequestInfo } from "../../ports/pull-requests.js";
|
|
3
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
3
4
|
export interface HubCommandOptions {
|
|
4
5
|
cwd: string;
|
|
5
6
|
columns?: number;
|
|
@@ -7,6 +8,7 @@ export interface HubCommandOptions {
|
|
|
7
8
|
now?: number;
|
|
8
9
|
stderr: (chunk: string) => void;
|
|
9
10
|
stdout: (chunk: string) => void;
|
|
11
|
+
runtime?: HubRuntime;
|
|
10
12
|
}
|
|
11
13
|
export interface HubCommandDependencies {
|
|
12
14
|
queryRepositoryPullRequests: (repoRoot: string) => Promise<PullRequestInfo[]>;
|
|
@@ -22,10 +24,12 @@ export interface HubRepository {
|
|
|
22
24
|
root: string;
|
|
23
25
|
worktrees: HubWorktree[];
|
|
24
26
|
}
|
|
27
|
+
type HubRuntime = CliRuntime<"pullRequests" | "repositoryContext" | "repositoryRegistry" | "worktrees" | "worktreeCatalog">;
|
|
25
28
|
export interface HubData {
|
|
26
29
|
currentRepository: string | null;
|
|
27
30
|
repositories: HubRepository[];
|
|
28
31
|
}
|
|
29
|
-
export declare function buildHubData(cwd: string, dependencies?: Partial<HubCommandDependencies
|
|
32
|
+
export declare function buildHubData(cwd: string, dependencies?: Partial<HubCommandDependencies>, runtime?: HubRuntime): Promise<HubData>;
|
|
30
33
|
export declare function runHubCommand(options: HubCommandOptions, dependencies?: Partial<HubCommandDependencies>): Promise<number>;
|
|
31
34
|
export declare function formatHubOutput(data: HubData, columns?: number, now?: number): string;
|
|
35
|
+
export {};
|