@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
@@ -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 { CONFIG_FILE_NAME, GLOBAL_CONFIG_FILE_PATH, KNOWN_CONFIG_KEYS, KNOWN_GLOBAL_CONFIG_KEYS, } from "./config.js";
9
- import { EDITORS } from "./editor.js";
10
- import { isHeadless } from "./headless.js";
11
- import { detectRepository } from "./repo.js";
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 repository = await detectRepositoryOrSkip(cwd);
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 = EDITORS.some(({ cli }) => cli === editor);
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 { loadEffectiveConfig, resolveConfigString } from "./config.js";
4
- import { isBranchMergedInto, readWorktreeHealth, resolveRemoteDefaultBranch, runGit, } from "./git.js";
5
- import { isHeadless } from "./headless.js";
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 { type QueryWorktreePullRequests, type WorktreePromptEntry, type WorktreePromptScope } from "./worktree-picker.js";
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
- registeredSources = await listRegisteredWorktreeSources(options.cwd, () => {
53
- skippedRegisteredRepos++;
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
- entries: await buildWorktreePromptEntries(promptSources, {
79
- metadata: currentRepositoryScope ? "full" : "fast",
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
- queryPullRequests: dependencies.queryPullRequests,
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 renderContextCard(path);
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, { scope });
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 { type PullRequestInfo } from "./pull-requests.js";
2
- import { type WorktreeInfo } from "./worktree-info.js";
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>): Promise<HubData>;
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 {};