@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.
Files changed (216) hide show
  1. package/README.md +12 -0
  2. package/dist/application/worktree/catalog.d.ts +31 -0
  3. package/dist/application/worktree/catalog.js +88 -0
  4. package/dist/application/worktree/context-card.d.ts +14 -0
  5. package/dist/application/worktree/context-card.js +12 -0
  6. package/dist/{go-resolver.d.ts → application/worktree/go-resolution.d.ts} +12 -4
  7. package/dist/{go-resolver.js → application/worktree/go-resolution.js} +50 -46
  8. package/dist/application/worktree/read-models.d.ts +31 -0
  9. package/dist/application/worktree/sources.d.ts +16 -0
  10. package/dist/application/worktree/sources.js +86 -0
  11. package/dist/{back.d.ts → cli/commands/back.d.ts} +3 -1
  12. package/dist/{back.js → cli/commands/back.js} +8 -6
  13. package/dist/{clean.d.ts → cli/commands/clean.d.ts} +6 -3
  14. package/dist/cli/commands/clean.js +555 -0
  15. package/dist/{completion.js → cli/commands/completion.js} +2 -2
  16. package/dist/{config-command.d.ts → cli/commands/config-command.d.ts} +2 -0
  17. package/dist/{config-command.js → cli/commands/config-command.js} +7 -5
  18. package/dist/{doctor.d.ts → cli/commands/doctor.d.ts} +2 -0
  19. package/dist/{doctor.js → cli/commands/doctor.js} +23 -25
  20. package/dist/{done.d.ts → cli/commands/done.d.ts} +2 -0
  21. package/dist/{done.js → cli/commands/done.js} +26 -23
  22. package/dist/{go.d.ts → cli/commands/go.d.ts} +8 -2
  23. package/dist/{go.js → cli/commands/go.js} +68 -48
  24. package/dist/{history-command.d.ts → cli/commands/history-command.d.ts} +2 -0
  25. package/dist/{history-command.js → cli/commands/history-command.js} +3 -1
  26. package/dist/{hub.d.ts → cli/commands/hub.d.ts} +7 -3
  27. package/dist/{hub.js → cli/commands/hub.js} +26 -33
  28. package/dist/{init.d.ts → cli/commands/init.d.ts} +3 -1
  29. package/dist/{init.js → cli/commands/init.js} +37 -34
  30. package/dist/{ls.d.ts → cli/commands/ls.d.ts} +4 -2
  31. package/dist/{ls.js → cli/commands/ls.js} +8 -5
  32. package/dist/{new.d.ts → cli/commands/new.d.ts} +6 -2
  33. package/dist/{new.js → cli/commands/new.js} +237 -109
  34. package/dist/{open.d.ts → cli/commands/open.d.ts} +4 -2
  35. package/dist/{open.js → cli/commands/open.js} +19 -12
  36. package/dist/{pr-open.d.ts → cli/commands/pr-open.d.ts} +4 -2
  37. package/dist/{pr-open.js → cli/commands/pr-open.js} +20 -18
  38. package/dist/{pr.d.ts → cli/commands/pr.d.ts} +5 -3
  39. package/dist/{pr.js → cli/commands/pr.js} +34 -41
  40. package/dist/{remove.d.ts → cli/commands/remove.d.ts} +4 -2
  41. package/dist/{remove.js → cli/commands/remove.js} +23 -17
  42. package/dist/{root.d.ts → cli/commands/root.d.ts} +2 -0
  43. package/dist/{root.js → cli/commands/root.js} +4 -2
  44. package/dist/{run-hook.d.ts → cli/commands/run-hook.d.ts} +2 -0
  45. package/dist/{run-hook.js → cli/commands/run-hook.js} +7 -4
  46. package/dist/{status.d.ts → cli/commands/status.d.ts} +2 -0
  47. package/dist/{status.js → cli/commands/status.js} +10 -7
  48. package/dist/{sync-files-command.d.ts → cli/commands/sync-files-command.d.ts} +2 -0
  49. package/dist/{sync-files-command.js → cli/commands/sync-files-command.js} +11 -9
  50. package/dist/{sync.d.ts → cli/commands/sync.d.ts} +2 -0
  51. package/dist/{sync.js → cli/commands/sync.js} +7 -4
  52. package/dist/{task-command.d.ts → cli/commands/task-command.d.ts} +2 -0
  53. package/dist/{task-command.js → cli/commands/task-command.js} +4 -2
  54. package/dist/{undo.d.ts → cli/commands/undo.d.ts} +8 -6
  55. package/dist/{undo.js → cli/commands/undo.js} +44 -34
  56. package/dist/{warp.d.ts → cli/commands/warp.d.ts} +5 -2
  57. package/dist/{warp.js → cli/commands/warp.js} +25 -13
  58. package/dist/cli/dependencies.d.ts +114 -0
  59. package/dist/cli/dependencies.js +118 -0
  60. package/dist/{cli.d.ts → cli/program.d.ts} +4 -2
  61. package/dist/{cli.js → cli/program.js} +108 -124
  62. package/dist/domain/repository/context.d.ts +6 -0
  63. package/dist/domain/repository/context.js +1 -0
  64. package/dist/domain/repository/registry.d.ts +5 -0
  65. package/dist/domain/repository/registry.js +1 -0
  66. package/dist/domain/shared/concurrency.d.ts +1 -0
  67. package/dist/domain/shared/concurrency.js +20 -0
  68. package/dist/domain/worktree/matching.d.ts +4 -0
  69. package/dist/domain/worktree/matching.js +79 -0
  70. package/dist/domain/worktree/policy.d.ts +2 -0
  71. package/dist/domain/worktree/policy.js +58 -0
  72. package/dist/domain/worktree/pr-reference.d.ts +1 -0
  73. package/dist/domain/worktree/pr-reference.js +9 -0
  74. package/dist/{worktree-source.d.ts → domain/worktree/source.d.ts} +1 -1
  75. package/dist/domain/worktree/source.js +1 -0
  76. package/dist/domain/worktree/types.d.ts +5 -0
  77. package/dist/domain/worktree/types.js +1 -0
  78. package/dist/gji-bundle.mjs +8756 -7666
  79. package/dist/index.js +3 -3
  80. package/dist/{dependency-bootstrap.d.ts → infrastructure/bootstrap/dependency-bootstrap.d.ts} +3 -30
  81. package/dist/{dependency-bootstrap.js → infrastructure/bootstrap/dependency-bootstrap.js} +4 -4
  82. package/dist/infrastructure/git/health.d.ts +9 -0
  83. package/dist/infrastructure/git/health.js +44 -0
  84. package/dist/infrastructure/git/refs.d.ts +6 -0
  85. package/dist/{git.js → infrastructure/git/refs.js} +4 -62
  86. package/dist/infrastructure/git/runner.d.ts +2 -0
  87. package/dist/infrastructure/git/runner.js +16 -0
  88. package/dist/{editor.d.ts → infrastructure/integrations/editor.d.ts} +2 -6
  89. package/dist/{pull-requests.d.ts → infrastructure/integrations/pull-requests.d.ts} +3 -11
  90. package/dist/{config.d.ts → infrastructure/persistence/config.d.ts} +3 -17
  91. package/dist/{config.js → infrastructure/persistence/config.js} +2 -17
  92. package/dist/{history.d.ts → infrastructure/persistence/history.d.ts} +2 -5
  93. package/dist/{task.js → infrastructure/persistence/task.js} +1 -1
  94. package/dist/infrastructure/process/command-runner.d.ts +3 -0
  95. package/dist/{hooks.d.ts → infrastructure/process/hooks.d.ts} +2 -12
  96. package/dist/infrastructure/repository/adapters.d.ts +7 -0
  97. package/dist/infrastructure/repository/adapters.js +23 -0
  98. package/dist/infrastructure/repository/context.d.ts +3 -0
  99. package/dist/infrastructure/repository/context.js +20 -0
  100. package/dist/infrastructure/repository/refs.d.ts +3 -0
  101. package/dist/infrastructure/repository/refs.js +37 -0
  102. package/dist/{repo-registry.d.ts → infrastructure/repository/registry.d.ts} +1 -5
  103. package/dist/{repo-registry.js → infrastructure/repository/registry.js} +1 -1
  104. package/dist/infrastructure/repository/worktrees.d.ts +2 -0
  105. package/dist/infrastructure/repository/worktrees.js +48 -0
  106. package/dist/{uv-validation.d.ts → infrastructure/validation/uv.d.ts} +1 -1
  107. package/dist/{uv-validation.js → infrastructure/validation/uv.js} +1 -1
  108. package/dist/infrastructure/worktree/bootstrap.d.ts +3 -0
  109. package/dist/{worktree-bootstrap.js → infrastructure/worktree/bootstrap.js} +7 -10
  110. package/dist/infrastructure/worktree/info.d.ts +6 -0
  111. package/dist/infrastructure/worktree/info.js +61 -0
  112. package/dist/{worktree-management.d.ts → infrastructure/worktree/lifecycle.d.ts} +0 -6
  113. package/dist/{worktree-management.js → infrastructure/worktree/lifecycle.js} +1 -9
  114. package/dist/ports/bootstrap.d.ts +61 -0
  115. package/dist/ports/bootstrap.js +1 -0
  116. package/dist/ports/config.d.ts +21 -0
  117. package/dist/ports/config.js +17 -0
  118. package/dist/ports/editor.d.ts +6 -0
  119. package/dist/ports/editor.js +1 -0
  120. package/dist/ports/git.d.ts +8 -0
  121. package/dist/ports/git.js +1 -0
  122. package/dist/ports/history.d.ts +5 -0
  123. package/dist/ports/history.js +1 -0
  124. package/dist/ports/hooks.d.ts +12 -0
  125. package/dist/ports/hooks.js +1 -0
  126. package/dist/{command-runner.d.ts → ports/process.d.ts} +0 -1
  127. package/dist/ports/process.js +1 -0
  128. package/dist/ports/pull-requests.d.ts +11 -0
  129. package/dist/ports/pull-requests.js +1 -0
  130. package/dist/ports/repository.d.ts +17 -0
  131. package/dist/ports/repository.js +1 -0
  132. package/dist/{bootstrap-output.d.ts → presentation/bootstrap/output.d.ts} +1 -1
  133. package/dist/presentation/bootstrap/preview.d.ts +2 -0
  134. package/dist/{bootstrap-preview.js → presentation/bootstrap/preview.js} +0 -4
  135. package/dist/{shell-completion.js → presentation/shell/completion.js} +5 -4
  136. package/dist/presentation/terminal/context-card.d.ts +2 -0
  137. package/dist/{context-card.js → presentation/terminal/context-card.js} +3 -12
  138. package/dist/presentation/worktree/format.d.ts +4 -0
  139. package/dist/presentation/worktree/format.js +41 -0
  140. package/dist/{worktree-picker.d.ts → presentation/worktree/picker.d.ts} +16 -17
  141. package/dist/{worktree-picker.js → presentation/worktree/picker.js} +30 -184
  142. package/man/man1/gji-back.1 +1 -1
  143. package/man/man1/gji-clean.1 +1 -1
  144. package/man/man1/gji-completion.1 +1 -1
  145. package/man/man1/gji-config.1 +1 -1
  146. package/man/man1/gji-doctor.1 +1 -1
  147. package/man/man1/gji-done.1 +1 -1
  148. package/man/man1/gji-go.1 +1 -1
  149. package/man/man1/gji-history.1 +1 -1
  150. package/man/man1/gji-init.1 +1 -1
  151. package/man/man1/gji-ls.1 +1 -1
  152. package/man/man1/gji-new.1 +6 -3
  153. package/man/man1/gji-open.1 +1 -1
  154. package/man/man1/gji-pr.1 +1 -1
  155. package/man/man1/gji-remove.1 +1 -1
  156. package/man/man1/gji-root.1 +1 -1
  157. package/man/man1/gji-run-hook.1 +1 -1
  158. package/man/man1/gji-status.1 +1 -1
  159. package/man/man1/gji-sync-files.1 +1 -1
  160. package/man/man1/gji-sync.1 +1 -1
  161. package/man/man1/gji-task.1 +1 -1
  162. package/man/man1/gji-undo.1 +1 -1
  163. package/man/man1/gji-warp.1 +1 -1
  164. package/man/man1/gji.1 +2 -2
  165. package/package.json +1 -1
  166. package/dist/bootstrap-preview.d.ts +0 -9
  167. package/dist/clean.js +0 -313
  168. package/dist/context-card.d.ts +0 -1
  169. package/dist/format-bytes.d.ts +0 -1
  170. package/dist/format-bytes.js +0 -14
  171. package/dist/git.d.ts +0 -18
  172. package/dist/repo.d.ts +0 -20
  173. package/dist/repo.js +0 -161
  174. package/dist/worktree-bootstrap.d.ts +0 -24
  175. package/dist/worktree-info.d.ts +0 -37
  176. package/dist/worktree-info.js +0 -123
  177. package/dist/worktree-sources.d.ts +0 -4
  178. package/dist/worktree-sources.js +0 -77
  179. /package/dist/{worktree-source.js → application/worktree/read-models.js} +0 -0
  180. /package/dist/{completion.d.ts → cli/commands/completion.d.ts} +0 -0
  181. /package/dist/{headless.d.ts → cli/runtime/headless.d.ts} +0 -0
  182. /package/dist/{headless.js → cli/runtime/headless.js} +0 -0
  183. /package/dist/{paths.d.ts → domain/shared/paths.d.ts} +0 -0
  184. /package/dist/{paths.js → domain/shared/paths.js} +0 -0
  185. /package/dist/{file-sync.d.ts → infrastructure/filesystem/file-sync.d.ts} +0 -0
  186. /package/dist/{file-sync.js → infrastructure/filesystem/file-sync.js} +0 -0
  187. /package/dist/{fs-utils.d.ts → infrastructure/filesystem/fs-utils.d.ts} +0 -0
  188. /package/dist/{fs-utils.js → infrastructure/filesystem/fs-utils.js} +0 -0
  189. /package/dist/{safe-destination.d.ts → infrastructure/filesystem/safe-destination.d.ts} +0 -0
  190. /package/dist/{safe-destination.js → infrastructure/filesystem/safe-destination.js} +0 -0
  191. /package/dist/{browser.d.ts → infrastructure/integrations/browser.d.ts} +0 -0
  192. /package/dist/{browser.js → infrastructure/integrations/browser.js} +0 -0
  193. /package/dist/{editor.js → infrastructure/integrations/editor.js} +0 -0
  194. /package/dist/{pull-requests.js → infrastructure/integrations/pull-requests.js} +0 -0
  195. /package/dist/{history.js → infrastructure/persistence/history.js} +0 -0
  196. /package/dist/{slots.d.ts → infrastructure/persistence/slots.d.ts} +0 -0
  197. /package/dist/{slots.js → infrastructure/persistence/slots.js} +0 -0
  198. /package/dist/{task.d.ts → infrastructure/persistence/task.d.ts} +0 -0
  199. /package/dist/{command-runner.js → infrastructure/process/command-runner.js} +0 -0
  200. /package/dist/{hooks.js → infrastructure/process/hooks.js} +0 -0
  201. /package/dist/{bootstrap-output.js → presentation/bootstrap/output.js} +0 -0
  202. /package/dist/{conflict.d.ts → presentation/prompts/path-conflict.d.ts} +0 -0
  203. /package/dist/{conflict.js → presentation/prompts/path-conflict.js} +0 -0
  204. /package/dist/{shell-completion.d.ts → presentation/shell/completion.d.ts} +0 -0
  205. /package/dist/{shell-handoff.d.ts → presentation/shell/handoff.d.ts} +0 -0
  206. /package/dist/{shell-handoff.js → presentation/shell/handoff.js} +0 -0
  207. /package/dist/{shell-setup.d.ts → presentation/shell/setup.d.ts} +0 -0
  208. /package/dist/{shell-setup.js → presentation/shell/setup.js} +0 -0
  209. /package/dist/{shell.d.ts → presentation/shell/shell.d.ts} +0 -0
  210. /package/dist/{shell.js → presentation/shell/shell.js} +0 -0
  211. /package/dist/{navigation-output.d.ts → presentation/terminal/navigation.d.ts} +0 -0
  212. /package/dist/{navigation-output.js → presentation/terminal/navigation.js} +0 -0
  213. /package/dist/{terminal-text.d.ts → presentation/terminal/text.d.ts} +0 -0
  214. /package/dist/{terminal-text.js → presentation/terminal/text.js} +0 -0
  215. /package/dist/{worktree-prompts.d.ts → presentation/worktree/prompts.d.ts} +0 -0
  216. /package/dist/{worktree-prompts.js → presentation/worktree/prompts.js} +0 -0
@@ -1,23 +1,27 @@
1
1
  import { basename } from "node:path";
2
2
  import { stdin, stdout } from "node:process";
3
3
  import { spinner } from "@clack/prompts";
4
- import { loadHistory } from "./history.js";
5
- import { createPullRequestQuery, } from "./pull-requests.js";
6
- import { detectRepository } from "./repo.js";
7
- import { middleEllipsize, sanitizeTerminalText, terminalWidth, } from "./terminal-text.js";
8
- import { formatRelativeAge, formatUpstreamState, readWorktreeInfos, } from "./worktree-info.js";
9
- import { listDiscoverableWorktreeSources } from "./worktree-sources.js";
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
- const defaultDependencies = {
12
- queryRepositoryPullRequests: createPullRequestQuery().listOpenPullRequestsForRepository,
13
- };
14
- export async function buildHubData(cwd, dependencies = {}) {
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
- defaultDependencies.queryRepositoryPullRequests;
19
+ runtime.pullRequests.listOpenPullRequestsForRepository;
17
20
  const currentRepository = await detectRepository(cwd).catch(() => null);
18
- const history = await loadHistory();
19
- const lastUsedByPath = new Map(history.map((entry) => [entry.path, entry.timestamp]));
20
- const sources = await listDiscoverableWorktreeSources(cwd);
21
+ const { sources } = await listDiscoverableWorktreeSources({
22
+ cwd,
23
+ repositoryPort: sourceDependencies,
24
+ });
21
25
  const groups = new Map();
22
26
  for (const source of sources) {
23
27
  if (!source.repoRoot)
@@ -35,7 +39,6 @@ export async function buildHubData(cwd, dependencies = {}) {
35
39
  }
36
40
  }
37
41
  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
42
  let pullRequests = [];
40
43
  try {
41
44
  pullRequests = await queryRepositoryPullRequests(root);
@@ -43,10 +46,14 @@ export async function buildHubData(cwd, dependencies = {}) {
43
46
  catch {
44
47
  // PR metadata is optional. The hub remains useful when a provider is unavailable.
45
48
  }
46
- const worktrees = infos.map((info) => ({
49
+ const hydrated = await loadWorktreeCatalog(group.sources, "full", {
50
+ ...runtime.worktreeCatalog,
51
+ queryRepositoryPullRequests: async () => pullRequests,
52
+ });
53
+ const worktrees = hydrated.map(({ info, lastUsedTimestamp, pullRequests }) => ({
47
54
  ...info,
48
- lastUsedTimestamp: lastUsedByPath.get(info.path) ?? null,
49
- pullRequests: pullRequests.filter((pullRequest) => pullRequest.sourceBranch === info.branch),
55
+ lastUsedTimestamp,
56
+ pullRequests,
50
57
  }));
51
58
  return {
52
59
  current: currentRepository?.repoRoot === root,
@@ -73,7 +80,7 @@ export async function runHubCommand(options, dependencies = {}) {
73
80
  loading?.start("Loading worktrees");
74
81
  let data;
75
82
  try {
76
- data = await buildHubData(options.cwd, dependencies);
83
+ data = await buildHubData(options.cwd, dependencies, options.runtime);
77
84
  }
78
85
  finally {
79
86
  loading?.stop();
@@ -283,17 +290,3 @@ function formatHumanText(value, maxLength) {
283
290
  ? sanitized
284
291
  : `${sanitized.slice(0, Math.max(0, maxLength - 1))}…`;
285
292
  }
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 "./shell.js";
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 { loadConfig, loadGlobalConfig, saveGlobalConfig, saveLocalConfig, updateGlobalConfigKey, } from "./config.js";
7
- import { EDITORS } from "./editor.js";
8
- import { isHeadless } from "./headless.js";
9
- import { resolveSupportedShell } from "./shell.js";
10
- import { renderShellCompletion } from "./shell-completion.js";
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 EDITORS) {
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, { ...loaded.config, ...values });
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 { type WorktreeEntry } from "./repo.js";
2
- import { type WorktreeInfo } from "./worktree-info.js";
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 "./paths.js";
2
- import { listWorktrees } from "./repo.js";
3
- import { formatLastCommit, formatUpstreamState, readWorktreeInfos, } from "./worktree-info.js";
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 ? " " + "TASK".padEnd(taskWidth, " ") : "") +
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 = [" " + "BRANCH".padEnd(branchWidth, " ") + " PATH"];
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
  }
@@ -1,9 +1,11 @@
1
- import { type PathConflictChoice } from "./conflict.js";
2
- import { type BootstrapCommandRunner } from "./dependency-bootstrap.js";
1
+ import type { CommandRunner as BootstrapCommandRunner } from "../../ports/process.js";
2
+ import { type PathConflictChoice } from "../../presentation/prompts/path-conflict.js";
3
+ import { type CliRuntime } from "../dependencies.js";
3
4
  export type { PathConflictChoice };
4
5
  export type NewWorktreeMode = "create" | "checkout" | "track";
5
6
  export interface NewCommandOptions {
6
7
  branch?: string;
8
+ branchOnly?: boolean;
7
9
  copy?: boolean;
8
10
  cwd: string;
9
11
  detached?: boolean;
@@ -22,7 +24,9 @@ export interface NewCommandOptions {
22
24
  stderr: (chunk: string) => void;
23
25
  stdout: (chunk: string) => void;
24
26
  task?: string;
27
+ runtime?: NewRuntime;
25
28
  }
29
+ type NewRuntime = CliRuntime<"bootstrap" | "config" | "git" | "history" | "integrations" | "repositoryContext" | "tasks">;
26
30
  export interface NewCommandDependencies {
27
31
  createBranchPlaceholder: () => string;
28
32
  promptForBranch: (placeholder: string) => Promise<string | null>;