@solaqua/gji 0.12.4 → 0.13.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} +46 -44
- package/dist/application/worktree/read-models.d.ts +31 -0
- package/dist/application/worktree/sources.d.ts +7 -0
- package/dist/{worktree-sources.js → application/worktree/sources.js} +12 -24
- 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} +4 -2
- package/dist/{clean.js → cli/commands/clean.js} +32 -24
- 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} +6 -1
- package/dist/{go.js → cli/commands/go.js} +57 -41
- 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} +23 -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} +20 -10
- 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 +15 -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 +7100 -6265
- 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} +1 -51
- 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} +7 -11
- package/dist/{worktree-picker.js → presentation/worktree/picker.js} +36 -191
- 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/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-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
|
@@ -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,7 +49,11 @@ export function createGoCommand(dependencies = {}) {
|
|
|
49
49
|
let promptSources = currentSources;
|
|
50
50
|
const loadAllSources = async () => {
|
|
51
51
|
if (registeredSources === null) {
|
|
52
|
-
registeredSources = await listRegisteredWorktreeSources(options.cwd,
|
|
52
|
+
registeredSources = await listRegisteredWorktreeSources(options.cwd, {
|
|
53
|
+
...runtime.repositoryContext,
|
|
54
|
+
...runtime.repositoryRegistry,
|
|
55
|
+
...runtime.worktrees,
|
|
56
|
+
}, () => {
|
|
53
57
|
skippedRegisteredRepos++;
|
|
54
58
|
});
|
|
55
59
|
}
|
|
@@ -77,7 +81,7 @@ export function createGoCommand(dependencies = {}) {
|
|
|
77
81
|
return {
|
|
78
82
|
entries: await buildWorktreePromptEntries(promptSources, {
|
|
79
83
|
metadata: currentRepositoryScope ? "full" : "fast",
|
|
80
|
-
|
|
84
|
+
catalog: withPullRequestQueries(runtime, dependencies.queryPullRequests),
|
|
81
85
|
}),
|
|
82
86
|
label: currentRepositoryScope
|
|
83
87
|
? "current repository"
|
|
@@ -91,7 +95,7 @@ export function createGoCommand(dependencies = {}) {
|
|
|
91
95
|
}
|
|
92
96
|
const promptEntries = await buildWorktreePromptEntries(promptSources, {
|
|
93
97
|
metadata: repository ? "full" : "fast",
|
|
94
|
-
|
|
98
|
+
catalog: withPullRequestQueries(runtime, dependencies.queryPullRequests),
|
|
95
99
|
});
|
|
96
100
|
const selectedPath = await prompt(promptEntries, scope);
|
|
97
101
|
if (!selectedPath) {
|
|
@@ -99,30 +103,40 @@ export function createGoCommand(dependencies = {}) {
|
|
|
99
103
|
return 1;
|
|
100
104
|
}
|
|
101
105
|
const selected = promptSources.find((source) => source.worktree.path === selectedPath);
|
|
102
|
-
return navigateToExistingWorktree(options, selectedPath, selected?.worktree);
|
|
106
|
+
return navigateToExistingWorktree(options, selectedPath, selected?.worktree, runtime);
|
|
103
107
|
}
|
|
104
108
|
const resolution = await resolveGoBranch({
|
|
105
109
|
branch: options.branch,
|
|
106
|
-
configStderr: options.json ? undefined : options.stderr,
|
|
107
110
|
cwd: options.cwd,
|
|
108
111
|
currentSources,
|
|
109
112
|
repository,
|
|
113
|
+
repositoryPort: runtime.repositoryRefs,
|
|
114
|
+
sourceDependencies: {
|
|
115
|
+
...runtime.repositoryContext,
|
|
116
|
+
...runtime.repositoryRegistry,
|
|
117
|
+
...runtime.worktrees,
|
|
118
|
+
},
|
|
119
|
+
configPort: runtime.config,
|
|
110
120
|
});
|
|
111
|
-
return handleGoBranchResolution(options, resolution, confirmBranchCreation, prompt, dependencies.queryPullRequests);
|
|
121
|
+
return handleGoBranchResolution(options, resolution, confirmBranchCreation, prompt, runtime, dependencies.queryPullRequests);
|
|
112
122
|
};
|
|
113
123
|
}
|
|
114
124
|
export const runGoCommand = createGoCommand();
|
|
115
|
-
async function handleGoBranchResolution(options, resolution, confirmBranchCreation, prompt, queryPullRequests) {
|
|
125
|
+
async function handleGoBranchResolution(options, resolution, confirmBranchCreation, prompt, runtime, queryPullRequests) {
|
|
126
|
+
if (!options.json) {
|
|
127
|
+
for (const warning of resolution.configWarnings ?? [])
|
|
128
|
+
options.stderr(warning);
|
|
129
|
+
}
|
|
116
130
|
switch (resolution.kind) {
|
|
117
131
|
case "existing":
|
|
118
|
-
return navigateToExistingWorktree(options, resolution.source.worktree.path, resolution.source.worktree);
|
|
132
|
+
return navigateToExistingWorktree(options, resolution.source.worktree.path, resolution.source.worktree, runtime);
|
|
119
133
|
case "ambiguous": {
|
|
120
134
|
if (options.json || isHeadless() || options.print) {
|
|
121
135
|
return emitAmbiguousCrossRepoError(options, resolution.matches);
|
|
122
136
|
}
|
|
123
137
|
const candidates = await buildWorktreePromptEntries(resolution.matches, {
|
|
124
138
|
metadata: "fast",
|
|
125
|
-
queryPullRequests,
|
|
139
|
+
catalog: withPullRequestQueries(runtime, queryPullRequests),
|
|
126
140
|
});
|
|
127
141
|
const selectedPath = await prompt(candidates);
|
|
128
142
|
if (!selectedPath) {
|
|
@@ -130,10 +144,10 @@ async function handleGoBranchResolution(options, resolution, confirmBranchCreati
|
|
|
130
144
|
return 1;
|
|
131
145
|
}
|
|
132
146
|
const selected = resolution.matches.find((source) => source.worktree.path === selectedPath);
|
|
133
|
-
return navigateToExistingWorktree(options, selectedPath, selected?.worktree);
|
|
147
|
+
return navigateToExistingWorktree(options, selectedPath, selected?.worktree, runtime);
|
|
134
148
|
}
|
|
135
149
|
case "create":
|
|
136
|
-
return createExistingBranchWorktree(options, confirmBranchCreation, resolution.repository, resolution.branch, resolution.mode, resolution.remote);
|
|
150
|
+
return createExistingBranchWorktree(options, confirmBranchCreation, resolution.repository, resolution.branch, resolution.mode, resolution.remote, runtime);
|
|
137
151
|
case "pull-request":
|
|
138
152
|
if (options.json || isHeadless() || options.print) {
|
|
139
153
|
return emitError(options, "PR navigation creates a worktree; use `gji pr <ref>` in an interactive shell");
|
|
@@ -144,6 +158,7 @@ async function handleGoBranchResolution(options, resolution, confirmBranchCreati
|
|
|
144
158
|
outputEnv: GO_OUTPUT_FILE_ENV,
|
|
145
159
|
stderr: options.stderr,
|
|
146
160
|
stdout: options.stdout,
|
|
161
|
+
runtime,
|
|
147
162
|
});
|
|
148
163
|
case "no-repository":
|
|
149
164
|
return emitNoRepositoryError(options, resolution.staleRegisteredRepos);
|
|
@@ -153,11 +168,11 @@ async function handleGoBranchResolution(options, resolution, confirmBranchCreati
|
|
|
153
168
|
return emitError(options, resolution.message);
|
|
154
169
|
}
|
|
155
170
|
}
|
|
156
|
-
async function readCurrentRepository(cwd) {
|
|
171
|
+
async function readCurrentRepository(cwd, runtime) {
|
|
157
172
|
try {
|
|
158
173
|
const [repository, worktrees] = await Promise.all([
|
|
159
|
-
detectRepository(cwd),
|
|
160
|
-
listWorktrees(cwd),
|
|
174
|
+
runtime.repositoryContext.detectRepository(cwd),
|
|
175
|
+
runtime.worktrees.listWorktrees(cwd),
|
|
161
176
|
]);
|
|
162
177
|
return [repository, worktrees];
|
|
163
178
|
}
|
|
@@ -179,7 +194,7 @@ function deduplicateSources(sources) {
|
|
|
179
194
|
}
|
|
180
195
|
return deduplicated;
|
|
181
196
|
}
|
|
182
|
-
async function createExistingBranchWorktree(options, confirmBranchCreation, repository, branch, mode, remote) {
|
|
197
|
+
async function createExistingBranchWorktree(options, confirmBranchCreation, repository, branch, mode, remote, runtime) {
|
|
183
198
|
if (options.json || isHeadless() || options.print) {
|
|
184
199
|
return emitError(options, `branch "${branch}" exists but has no worktree; use interactive gji go ${branch} to create one`);
|
|
185
200
|
}
|
|
@@ -195,41 +210,42 @@ async function createExistingBranchWorktree(options, confirmBranchCreation, repo
|
|
|
195
210
|
remote,
|
|
196
211
|
stderr: options.stderr,
|
|
197
212
|
stdout: options.stdout,
|
|
213
|
+
runtime,
|
|
198
214
|
});
|
|
199
215
|
}
|
|
200
|
-
async function navigateToExistingWorktree(options, path, worktree) {
|
|
201
|
-
const repository = await detectRepository(path);
|
|
216
|
+
async function navigateToExistingWorktree(options, path, worktree, runtime) {
|
|
217
|
+
const repository = await runtime.repositoryContext.detectRepository(path);
|
|
202
218
|
if (options.json) {
|
|
203
219
|
options.stdout(`${JSON.stringify(createNavigationTarget(createNavigationRepository(repository.repoName, repository.repoRoot), path, worktree?.branch ?? null), null, 2)}\n`);
|
|
204
220
|
return 0;
|
|
205
221
|
}
|
|
206
|
-
const config = await loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
|
|
207
|
-
const hooks = extractHooks(config);
|
|
208
|
-
await runHook(hooks["after-enter"], path, {
|
|
222
|
+
const config = await runtime.config.loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
|
|
223
|
+
const hooks = runtime.hooks.extractHooks(config);
|
|
224
|
+
await runtime.hooks.runHook(hooks["after-enter"], path, {
|
|
209
225
|
branch: worktree?.branch ?? undefined,
|
|
210
226
|
path,
|
|
211
227
|
repo: basename(repository.repoRoot),
|
|
212
|
-
slot: await getWorktreeSlot(path),
|
|
228
|
+
slot: await runtime.slots.getWorktreeSlot(path),
|
|
213
229
|
}, options.stderr);
|
|
214
230
|
if (!options.quiet) {
|
|
215
|
-
const card = await
|
|
231
|
+
const card = await loadContextCardModel(path, runtime.contextCard);
|
|
216
232
|
if (card)
|
|
217
|
-
options.stderr(`${card}\n`);
|
|
233
|
+
options.stderr(`${renderContextCard(card)}\n`);
|
|
218
234
|
}
|
|
219
|
-
await recordWorktreeUsage(path, worktree?.branch ?? null);
|
|
235
|
+
await runtime.history.recordWorktreeUsage(path, worktree?.branch ?? null);
|
|
220
236
|
await writeShellOutput(GO_OUTPUT_FILE_ENV, path, options.stdout);
|
|
221
237
|
return 0;
|
|
222
238
|
}
|
|
223
|
-
async function navigateToRepositoryRoot(options) {
|
|
239
|
+
async function navigateToRepositoryRoot(options, runtime) {
|
|
224
240
|
let repository;
|
|
225
241
|
try {
|
|
226
|
-
repository = await detectRepository(options.cwd);
|
|
242
|
+
repository = await runtime.repositoryContext.detectRepository(options.cwd);
|
|
227
243
|
}
|
|
228
244
|
catch {
|
|
229
245
|
return emitError(options, "not inside a git repository");
|
|
230
246
|
}
|
|
231
247
|
if (options.json) {
|
|
232
|
-
const rootWorktree = (await listWorktrees(repository.repoRoot)).find((worktree) => worktree.path === repository.repoRoot);
|
|
248
|
+
const rootWorktree = (await runtime.worktrees.listWorktrees(repository.repoRoot)).find((worktree) => worktree.path === repository.repoRoot);
|
|
233
249
|
options.stdout(`${JSON.stringify(createNavigationTarget(createNavigationRepository(repository.repoName, repository.repoRoot), repository.repoRoot, rootWorktree?.branch ?? null), null, 2)}\n`);
|
|
234
250
|
return 0;
|
|
235
251
|
}
|
|
@@ -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 {};
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { stdin, stdout } from "node:process";
|
|
3
3
|
import { spinner } from "@clack/prompts";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { middleEllipsize, sanitizeTerminalText, terminalWidth, } from "
|
|
8
|
-
import { formatRelativeAge, formatUpstreamState,
|
|
9
|
-
import {
|
|
4
|
+
import { loadWorktreeCatalog } from "../../application/worktree/catalog.js";
|
|
5
|
+
import { listDiscoverableWorktreeSources } from "../../application/worktree/sources.js";
|
|
6
|
+
import { mapWithConcurrency } from "../../domain/shared/concurrency.js";
|
|
7
|
+
import { middleEllipsize, sanitizeTerminalText, terminalWidth, } from "../../presentation/terminal/text.js";
|
|
8
|
+
import { formatRelativeAge, formatUpstreamState, } from "../../presentation/worktree/format.js";
|
|
9
|
+
import { defaultCliDependencies } from "../dependencies.js";
|
|
10
10
|
const MAX_HUB_REPOSITORY_CONCURRENCY = 4;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export async function buildHubData(cwd, dependencies = {}, runtime = defaultCliDependencies) {
|
|
12
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
13
|
+
const sourceDependencies = {
|
|
14
|
+
...runtime.repositoryContext,
|
|
15
|
+
...runtime.repositoryRegistry,
|
|
16
|
+
...runtime.worktrees,
|
|
17
|
+
};
|
|
15
18
|
const queryRepositoryPullRequests = dependencies.queryRepositoryPullRequests ??
|
|
16
|
-
|
|
19
|
+
runtime.pullRequests.listOpenPullRequestsForRepository;
|
|
17
20
|
const currentRepository = await detectRepository(cwd).catch(() => null);
|
|
18
|
-
const
|
|
19
|
-
const lastUsedByPath = new Map(history.map((entry) => [entry.path, entry.timestamp]));
|
|
20
|
-
const sources = await listDiscoverableWorktreeSources(cwd);
|
|
21
|
+
const sources = await listDiscoverableWorktreeSources(cwd, sourceDependencies);
|
|
21
22
|
const groups = new Map();
|
|
22
23
|
for (const source of sources) {
|
|
23
24
|
if (!source.repoRoot)
|
|
@@ -35,7 +36,6 @@ export async function buildHubData(cwd, dependencies = {}) {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
const repositories = await mapWithConcurrency([...groups.entries()], MAX_HUB_REPOSITORY_CONCURRENCY, async ([root, group]) => {
|
|
38
|
-
const infos = await readWorktreeInfos(group.sources.map((source) => source.worktree));
|
|
39
39
|
let pullRequests = [];
|
|
40
40
|
try {
|
|
41
41
|
pullRequests = await queryRepositoryPullRequests(root);
|
|
@@ -43,10 +43,14 @@ export async function buildHubData(cwd, dependencies = {}) {
|
|
|
43
43
|
catch {
|
|
44
44
|
// PR metadata is optional. The hub remains useful when a provider is unavailable.
|
|
45
45
|
}
|
|
46
|
-
const
|
|
46
|
+
const hydrated = await loadWorktreeCatalog(group.sources, "full", {
|
|
47
|
+
...runtime.worktreeCatalog,
|
|
48
|
+
queryRepositoryPullRequests: async () => pullRequests,
|
|
49
|
+
});
|
|
50
|
+
const worktrees = hydrated.map(({ info, lastUsedTimestamp, pullRequests }) => ({
|
|
47
51
|
...info,
|
|
48
|
-
lastUsedTimestamp
|
|
49
|
-
pullRequests
|
|
52
|
+
lastUsedTimestamp,
|
|
53
|
+
pullRequests,
|
|
50
54
|
}));
|
|
51
55
|
return {
|
|
52
56
|
current: currentRepository?.repoRoot === root,
|
|
@@ -73,7 +77,7 @@ export async function runHubCommand(options, dependencies = {}) {
|
|
|
73
77
|
loading?.start("Loading worktrees");
|
|
74
78
|
let data;
|
|
75
79
|
try {
|
|
76
|
-
data = await buildHubData(options.cwd, dependencies);
|
|
80
|
+
data = await buildHubData(options.cwd, dependencies, options.runtime);
|
|
77
81
|
}
|
|
78
82
|
finally {
|
|
79
83
|
loading?.stop();
|
|
@@ -283,17 +287,3 @@ function formatHumanText(value, maxLength) {
|
|
|
283
287
|
? sanitized
|
|
284
288
|
: `${sanitized.slice(0, Math.max(0, maxLength - 1))}…`;
|
|
285
289
|
}
|
|
286
|
-
async function mapWithConcurrency(items, limit, mapper) {
|
|
287
|
-
const results = new Array(items.length);
|
|
288
|
-
let nextIndex = 0;
|
|
289
|
-
async function readNext() {
|
|
290
|
-
for (;;) {
|
|
291
|
-
const index = nextIndex++;
|
|
292
|
-
if (index >= items.length)
|
|
293
|
-
return;
|
|
294
|
-
results[index] = await mapper(items[index]);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => readNext()));
|
|
298
|
-
return results;
|
|
299
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type SupportedShell } from "
|
|
1
|
+
import { type SupportedShell } from "../../presentation/shell/shell.js";
|
|
2
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
2
3
|
export type InstallSaveTarget = "local" | "global";
|
|
3
4
|
export type ShellIntegrationChoice = "existing" | "install" | "skip";
|
|
4
5
|
export interface SetupWizardResult {
|
|
@@ -29,6 +30,7 @@ export interface InitCommandOptions {
|
|
|
29
30
|
promptForOnboarding?: (context: InitOnboardingContext) => Promise<InitOnboardingResult | null>;
|
|
30
31
|
promptForSetup?: () => Promise<SetupWizardResult | null>;
|
|
31
32
|
shell?: string;
|
|
33
|
+
runtime?: CliRuntime<"integrations" | "configStore">;
|
|
32
34
|
stderr?: (chunk: string) => void;
|
|
33
35
|
stdout: (chunk: string) => void;
|
|
34
36
|
write?: boolean;
|
|
@@ -3,12 +3,11 @@ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
5
|
import { confirm, intro, isCancel, log, outro, select, text, } from "@clack/prompts";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { SHELL_INTEGRATION_END_MARKER as END_MARKER, executableExists, hasShellIntegration, ONBOARDING_SHELL_INTEGRATION_END_MARKER as ONBOARDING_END_MARKER, ONBOARDING_SHELL_INTEGRATION_START_MARKER as ONBOARDING_START_MARKER, resolveCompletionPath, resolveShellConfigPath, SHELL_INTEGRATION_START_MARKER as START_MARKER, } from "./shell-setup.js";
|
|
6
|
+
import { renderShellCompletion } from "../../presentation/shell/completion.js";
|
|
7
|
+
import { SHELL_INTEGRATION_END_MARKER as END_MARKER, executableExists, hasShellIntegration, ONBOARDING_SHELL_INTEGRATION_END_MARKER as ONBOARDING_END_MARKER, ONBOARDING_SHELL_INTEGRATION_START_MARKER as ONBOARDING_START_MARKER, resolveCompletionPath, resolveShellConfigPath, SHELL_INTEGRATION_START_MARKER as START_MARKER, } from "../../presentation/shell/setup.js";
|
|
8
|
+
import { resolveSupportedShell, } from "../../presentation/shell/shell.js";
|
|
9
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
10
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
12
11
|
const ZSH_COMPLETION_PATH_LINE = "fpath=(~/.zsh/completions $fpath)";
|
|
13
12
|
const SHELL_WRAPPED_COMMANDS = [
|
|
14
13
|
{
|
|
@@ -69,12 +68,13 @@ const SHELL_WRAPPED_COMMANDS = [
|
|
|
69
68
|
},
|
|
70
69
|
];
|
|
71
70
|
export async function runInitCommand(options) {
|
|
71
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
72
72
|
if (options.shell === undefined) {
|
|
73
|
-
return runOnboardingInitCommand(options);
|
|
73
|
+
return runOnboardingInitCommand(options, runtime);
|
|
74
74
|
}
|
|
75
|
-
return runLegacyInitCommand(options);
|
|
75
|
+
return runLegacyInitCommand(options, runtime);
|
|
76
76
|
}
|
|
77
|
-
async function runOnboardingInitCommand(options) {
|
|
77
|
+
async function runOnboardingInitCommand(options, runtime) {
|
|
78
78
|
if (options.json || isHeadless() || !canRunOnboarding(options)) {
|
|
79
79
|
return writeNonInteractiveInitError(options);
|
|
80
80
|
}
|
|
@@ -84,14 +84,14 @@ async function runOnboardingInitCommand(options) {
|
|
|
84
84
|
home,
|
|
85
85
|
};
|
|
86
86
|
if (options.promptForOnboarding === undefined) {
|
|
87
|
-
return runDefaultOnboarding(context);
|
|
87
|
+
return runDefaultOnboarding(context, runtime);
|
|
88
88
|
}
|
|
89
89
|
const result = await options.promptForOnboarding(context);
|
|
90
90
|
if (!result) {
|
|
91
91
|
options.stderr?.("Aborted.\n");
|
|
92
92
|
return 1;
|
|
93
93
|
}
|
|
94
|
-
await applyOnboardingResult(result, home);
|
|
94
|
+
await applyOnboardingResult(result, home, runtime);
|
|
95
95
|
return 0;
|
|
96
96
|
}
|
|
97
97
|
function canRunOnboarding(options) {
|
|
@@ -104,7 +104,7 @@ function writeNonInteractiveInitError(options) {
|
|
|
104
104
|
options.stderr?.(options.json ? `${JSON.stringify({ error })}\n` : `${error}\n`);
|
|
105
105
|
return 1;
|
|
106
106
|
}
|
|
107
|
-
async function runDefaultOnboarding(context) {
|
|
107
|
+
async function runDefaultOnboarding(context, runtime) {
|
|
108
108
|
intro("gji init");
|
|
109
109
|
const shell = await select({
|
|
110
110
|
message: "Which shell should gji configure?",
|
|
@@ -124,7 +124,7 @@ async function runDefaultOnboarding(context) {
|
|
|
124
124
|
return abortDefaultOnboarding();
|
|
125
125
|
const completionPath = resolveCompletionPath(shell, context.home);
|
|
126
126
|
const completionAlreadyInstalled = await fileExists(completionPath);
|
|
127
|
-
await applyShellIntegration(shell, shellIntegration, context.home);
|
|
127
|
+
await applyShellIntegration(shell, shellIntegration, context.home, runtime);
|
|
128
128
|
const installCompletion = await promptForCompletion(shell, completionPath);
|
|
129
129
|
if (installCompletion === null)
|
|
130
130
|
return abortDefaultOnboarding();
|
|
@@ -135,21 +135,21 @@ async function runDefaultOnboarding(context) {
|
|
|
135
135
|
if (shell === "zsh" && (completionAlreadyInstalled || installCompletion)) {
|
|
136
136
|
await ensureZshCompletionPath(rcPath);
|
|
137
137
|
}
|
|
138
|
-
const editor = await promptForEditor();
|
|
138
|
+
const editor = await promptForEditor(runtime.integrations.EDITORS);
|
|
139
139
|
if (editor === null)
|
|
140
140
|
return abortDefaultOnboarding();
|
|
141
141
|
if (editor) {
|
|
142
|
-
await updateGlobalConfigKey("editor", editor, context.home);
|
|
142
|
+
await runtime.configStore.updateGlobalConfigKey("editor", editor, context.home);
|
|
143
143
|
}
|
|
144
144
|
outro(`Setup complete. Restart your shell or run: source ${rcPath}\nVerify with: gji doctor`);
|
|
145
145
|
return 0;
|
|
146
146
|
}
|
|
147
|
-
async function applyShellIntegration(shell, choice, home) {
|
|
147
|
+
async function applyShellIntegration(shell, choice, home, runtime) {
|
|
148
148
|
if (choice === "install") {
|
|
149
149
|
await installOnboardingShellIntegration(shell, home);
|
|
150
150
|
}
|
|
151
151
|
if (choice !== "skip") {
|
|
152
|
-
await updateGlobalConfigKey("shellIntegration", true, home);
|
|
152
|
+
await runtime.configStore.updateGlobalConfigKey("shellIntegration", true, home);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
function abortDefaultOnboarding() {
|
|
@@ -187,8 +187,8 @@ async function promptForCompletion(shell, completionPath) {
|
|
|
187
187
|
return null;
|
|
188
188
|
return confirmed;
|
|
189
189
|
}
|
|
190
|
-
async function promptForEditor() {
|
|
191
|
-
const availableEditors = await findAvailableEditors();
|
|
190
|
+
async function promptForEditor(editors) {
|
|
191
|
+
const availableEditors = await findAvailableEditors(editors);
|
|
192
192
|
if (availableEditors.length === 0) {
|
|
193
193
|
log.info("No supported editor CLIs found on PATH; skipping editor setup.");
|
|
194
194
|
return undefined;
|
|
@@ -209,20 +209,20 @@ async function promptForEditor() {
|
|
|
209
209
|
return null;
|
|
210
210
|
return selected === skipValue ? undefined : selected;
|
|
211
211
|
}
|
|
212
|
-
async function findAvailableEditors() {
|
|
212
|
+
async function findAvailableEditors(editors) {
|
|
213
213
|
const availableEditors = [];
|
|
214
|
-
for (const editor of
|
|
214
|
+
for (const editor of editors) {
|
|
215
215
|
if (await executableExists(editor.cli)) {
|
|
216
216
|
availableEditors.push(editor);
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
return availableEditors;
|
|
220
220
|
}
|
|
221
|
-
async function applyOnboardingResult(result, home) {
|
|
221
|
+
async function applyOnboardingResult(result, home, runtime) {
|
|
222
222
|
const rcPath = resolveShellConfigPath(result.shell, home);
|
|
223
223
|
const completionPath = resolveCompletionPath(result.shell, home);
|
|
224
224
|
const completionAlreadyInstalled = await fileExists(completionPath);
|
|
225
|
-
await applyShellIntegration(result.shell, result.shellIntegration, home);
|
|
225
|
+
await applyShellIntegration(result.shell, result.shellIntegration, home, runtime);
|
|
226
226
|
if (result.installCompletion) {
|
|
227
227
|
await mkdir(dirname(completionPath), { recursive: true });
|
|
228
228
|
await writeFile(completionPath, renderShellCompletion(result.shell), "utf8");
|
|
@@ -232,7 +232,7 @@ async function applyOnboardingResult(result, home) {
|
|
|
232
232
|
await ensureZshCompletionPath(rcPath);
|
|
233
233
|
}
|
|
234
234
|
if (result.editor) {
|
|
235
|
-
await updateGlobalConfigKey("editor", result.editor, home);
|
|
235
|
+
await runtime.configStore.updateGlobalConfigKey("editor", result.editor, home);
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
async function installOnboardingShellIntegration(shell, home) {
|
|
@@ -290,7 +290,7 @@ async function fileExists(path) {
|
|
|
290
290
|
return false;
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
|
-
async function runLegacyInitCommand(options) {
|
|
293
|
+
async function runLegacyInitCommand(options, runtime) {
|
|
294
294
|
const shell = resolveSupportedShell(options.shell, process.env.SHELL);
|
|
295
295
|
const home = options.home ?? homedir();
|
|
296
296
|
if (!shell) {
|
|
@@ -309,7 +309,7 @@ async function runLegacyInitCommand(options) {
|
|
|
309
309
|
await writeFile(rcPath, next, "utf8");
|
|
310
310
|
options.stdout(`${rcPath}\n`);
|
|
311
311
|
// Run the setup wizard on the first-ever init (not on subsequent re-runs).
|
|
312
|
-
const { config: globalConfig } = await loadGlobalConfig(home);
|
|
312
|
+
const { config: globalConfig } = await runtime.configStore.loadGlobalConfig(home);
|
|
313
313
|
const alreadyConfigured = "shellIntegration" in globalConfig || "installSaveTarget" in globalConfig;
|
|
314
314
|
const hasCustomPrompt = options.promptForSetup !== undefined;
|
|
315
315
|
const canPrompt = !isHeadless() && (hasCustomPrompt || process.stdout.isTTY === true);
|
|
@@ -317,12 +317,12 @@ async function runLegacyInitCommand(options) {
|
|
|
317
317
|
const prompt = options.promptForSetup ?? defaultPromptForSetup;
|
|
318
318
|
const result = await prompt();
|
|
319
319
|
if (result) {
|
|
320
|
-
await updateGlobalConfigKey("installSaveTarget", result.installSaveTarget, home);
|
|
321
|
-
await saveWizardConfig(result, options.cwd, home);
|
|
320
|
+
await runtime.configStore.updateGlobalConfigKey("installSaveTarget", result.installSaveTarget, home);
|
|
321
|
+
await saveWizardConfig(result, options.cwd, home, runtime);
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
// Mark shell integration as installed so the first-run nudge is suppressed.
|
|
325
|
-
await updateGlobalConfigKey("shellIntegration", true, home);
|
|
325
|
+
await runtime.configStore.updateGlobalConfigKey("shellIntegration", true, home);
|
|
326
326
|
return 0;
|
|
327
327
|
}
|
|
328
328
|
export function renderShellIntegration(shell) {
|
|
@@ -361,7 +361,7 @@ export function upsertShellIntegration(existingConfig, script) {
|
|
|
361
361
|
}
|
|
362
362
|
return ensureTrailingNewline(`${prefix}\n\n${trimmedScript}`);
|
|
363
363
|
}
|
|
364
|
-
async function saveWizardConfig(result, cwd, home) {
|
|
364
|
+
async function saveWizardConfig(result, cwd, home, runtime) {
|
|
365
365
|
const values = {};
|
|
366
366
|
if (result.branchPrefix)
|
|
367
367
|
values.branchPrefix = result.branchPrefix;
|
|
@@ -379,12 +379,15 @@ async function saveWizardConfig(result, cwd, home) {
|
|
|
379
379
|
if (Object.keys(values).length === 0)
|
|
380
380
|
return;
|
|
381
381
|
if (result.installSaveTarget === "local") {
|
|
382
|
-
const loaded = await loadConfig(cwd);
|
|
383
|
-
await saveLocalConfig(cwd, {
|
|
382
|
+
const loaded = await runtime.configStore.loadConfig(cwd);
|
|
383
|
+
await runtime.configStore.saveLocalConfig(cwd, {
|
|
384
|
+
...loaded.config,
|
|
385
|
+
...values,
|
|
386
|
+
});
|
|
384
387
|
}
|
|
385
388
|
else {
|
|
386
|
-
const { config: existing } = await loadGlobalConfig(home);
|
|
387
|
-
await saveGlobalConfig({ ...existing, ...values }, home);
|
|
389
|
+
const { config: existing } = await runtime.configStore.loadGlobalConfig(home);
|
|
390
|
+
await runtime.configStore.saveGlobalConfig({ ...existing, ...values }, home);
|
|
388
391
|
}
|
|
389
392
|
}
|
|
390
393
|
async function readExistingConfig(path) {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { WorktreeInfo } from "../../application/worktree/read-models.js";
|
|
2
|
+
import type { WorktreeEntry } from "../../domain/worktree/types.js";
|
|
3
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
3
4
|
export interface LsCommandOptions {
|
|
4
5
|
compact?: boolean;
|
|
5
6
|
cwd: string;
|
|
6
7
|
json?: boolean;
|
|
8
|
+
runtime?: CliRuntime<"worktrees" | "worktreeInfo">;
|
|
7
9
|
stdout: (chunk: string) => void;
|
|
8
10
|
}
|
|
9
11
|
export declare function runLsCommand(options: LsCommandOptions): Promise<number>;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { comparePaths } from "
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { comparePaths } from "../../domain/shared/paths.js";
|
|
2
|
+
import { formatLastCommit, formatUpstreamState, } from "../../presentation/worktree/format.js";
|
|
3
|
+
import { defaultCliDependencies } from "../dependencies.js";
|
|
4
4
|
export async function runLsCommand(options) {
|
|
5
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
6
|
+
const { listWorktrees } = runtime.worktrees;
|
|
7
|
+
const { readWorktreeInfos } = runtime.worktreeInfo;
|
|
5
8
|
const worktrees = sortWorktrees(await listWorktrees(options.cwd));
|
|
6
9
|
if (options.compact) {
|
|
7
10
|
if (options.json) {
|
|
@@ -44,7 +47,7 @@ export function formatDetailedWorktreeTable(worktrees) {
|
|
|
44
47
|
"UPSTREAM".padEnd(upstreamWidth, " ") +
|
|
45
48
|
" " +
|
|
46
49
|
"LAST".padEnd(lastCommitWidth, " ") +
|
|
47
|
-
(hasTasks ?
|
|
50
|
+
(hasTasks ? ` ${"TASK".padEnd(taskWidth, " ")}` : "") +
|
|
48
51
|
" PATH",
|
|
49
52
|
];
|
|
50
53
|
for (const row of rows) {
|
|
@@ -68,7 +71,7 @@ export function formatWorktreeTable(worktrees) {
|
|
|
68
71
|
path: worktree.path,
|
|
69
72
|
}));
|
|
70
73
|
const branchWidth = Math.max("BRANCH".length, ...rows.map((row) => row.branch.length));
|
|
71
|
-
const lines = [
|
|
74
|
+
const lines = [` ${"BRANCH".padEnd(branchWidth, " ")} PATH`];
|
|
72
75
|
for (const row of rows) {
|
|
73
76
|
lines.push(`${row.isCurrent ? "*" : " "} ${row.branch.padEnd(branchWidth, " ")} ${row.path}`);
|
|
74
77
|
}
|