@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.
Files changed (214) 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} +46 -44
  8. package/dist/application/worktree/read-models.d.ts +31 -0
  9. package/dist/application/worktree/sources.d.ts +7 -0
  10. package/dist/{worktree-sources.js → application/worktree/sources.js} +12 -24
  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} +4 -2
  14. package/dist/{clean.js → cli/commands/clean.js} +32 -24
  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} +6 -1
  23. package/dist/{go.js → cli/commands/go.js} +57 -41
  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} +23 -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} +20 -10
  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 +15 -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 +7100 -6265
  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} +1 -51
  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} +7 -11
  141. package/dist/{worktree-picker.js → presentation/worktree/picker.js} +36 -191
  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/context-card.d.ts +0 -1
  168. package/dist/format-bytes.d.ts +0 -1
  169. package/dist/format-bytes.js +0 -14
  170. package/dist/git.d.ts +0 -18
  171. package/dist/repo.d.ts +0 -20
  172. package/dist/repo.js +0 -161
  173. package/dist/worktree-bootstrap.d.ts +0 -24
  174. package/dist/worktree-info.d.ts +0 -37
  175. package/dist/worktree-info.js +0 -123
  176. package/dist/worktree-sources.d.ts +0 -4
  177. /package/dist/{worktree-source.js → application/worktree/read-models.js} +0 -0
  178. /package/dist/{completion.d.ts → cli/commands/completion.d.ts} +0 -0
  179. /package/dist/{headless.d.ts → cli/runtime/headless.d.ts} +0 -0
  180. /package/dist/{headless.js → cli/runtime/headless.js} +0 -0
  181. /package/dist/{paths.d.ts → domain/shared/paths.d.ts} +0 -0
  182. /package/dist/{paths.js → domain/shared/paths.js} +0 -0
  183. /package/dist/{file-sync.d.ts → infrastructure/filesystem/file-sync.d.ts} +0 -0
  184. /package/dist/{file-sync.js → infrastructure/filesystem/file-sync.js} +0 -0
  185. /package/dist/{fs-utils.d.ts → infrastructure/filesystem/fs-utils.d.ts} +0 -0
  186. /package/dist/{fs-utils.js → infrastructure/filesystem/fs-utils.js} +0 -0
  187. /package/dist/{safe-destination.d.ts → infrastructure/filesystem/safe-destination.d.ts} +0 -0
  188. /package/dist/{safe-destination.js → infrastructure/filesystem/safe-destination.js} +0 -0
  189. /package/dist/{browser.d.ts → infrastructure/integrations/browser.d.ts} +0 -0
  190. /package/dist/{browser.js → infrastructure/integrations/browser.js} +0 -0
  191. /package/dist/{editor.js → infrastructure/integrations/editor.js} +0 -0
  192. /package/dist/{pull-requests.js → infrastructure/integrations/pull-requests.js} +0 -0
  193. /package/dist/{history.js → infrastructure/persistence/history.js} +0 -0
  194. /package/dist/{slots.d.ts → infrastructure/persistence/slots.d.ts} +0 -0
  195. /package/dist/{slots.js → infrastructure/persistence/slots.js} +0 -0
  196. /package/dist/{task.d.ts → infrastructure/persistence/task.d.ts} +0 -0
  197. /package/dist/{command-runner.js → infrastructure/process/command-runner.js} +0 -0
  198. /package/dist/{hooks.js → infrastructure/process/hooks.js} +0 -0
  199. /package/dist/{bootstrap-output.js → presentation/bootstrap/output.js} +0 -0
  200. /package/dist/{conflict.d.ts → presentation/prompts/path-conflict.d.ts} +0 -0
  201. /package/dist/{conflict.js → presentation/prompts/path-conflict.js} +0 -0
  202. /package/dist/{shell-completion.d.ts → presentation/shell/completion.d.ts} +0 -0
  203. /package/dist/{shell-handoff.d.ts → presentation/shell/handoff.d.ts} +0 -0
  204. /package/dist/{shell-handoff.js → presentation/shell/handoff.js} +0 -0
  205. /package/dist/{shell-setup.d.ts → presentation/shell/setup.d.ts} +0 -0
  206. /package/dist/{shell-setup.js → presentation/shell/setup.js} +0 -0
  207. /package/dist/{shell.d.ts → presentation/shell/shell.d.ts} +0 -0
  208. /package/dist/{shell.js → presentation/shell/shell.js} +0 -0
  209. /package/dist/{navigation-output.d.ts → presentation/terminal/navigation.d.ts} +0 -0
  210. /package/dist/{navigation-output.js → presentation/terminal/navigation.js} +0 -0
  211. /package/dist/{terminal-text.d.ts → presentation/terminal/text.d.ts} +0 -0
  212. /package/dist/{terminal-text.js → presentation/terminal/text.js} +0 -0
  213. /package/dist/{worktree-prompts.d.ts → presentation/worktree/prompts.d.ts} +0 -0
  214. /package/dist/{worktree-prompts.js → presentation/worktree/prompts.js} +0 -0
@@ -0,0 +1,61 @@
1
+ import { mapWithConcurrency } from "../../domain/shared/concurrency.js";
2
+ import { readWorktreeHealth } from "../git/health.js";
3
+ import { readBranchLastCommitTimestamp } from "../git/refs.js";
4
+ import { getWorktreeSlot } from "../persistence/slots.js";
5
+ import { readTask } from "../persistence/task.js";
6
+ const MAX_WORKTREE_INFO_READ_CONCURRENCY = 8;
7
+ export async function readWorktreeInfos(worktrees) {
8
+ return mapWithConcurrency(worktrees, MAX_WORKTREE_INFO_READ_CONCURRENCY, readWorktreeInfo);
9
+ }
10
+ export async function readWorktreeInfo(worktree) {
11
+ const [healthResult, lastCommitResult, slotResult, taskResult] = await Promise.allSettled([
12
+ readWorktreeHealth(worktree.path),
13
+ worktree.branch === null
14
+ ? null
15
+ : readBranchLastCommitTimestamp(worktree.path, worktree.branch),
16
+ getWorktreeSlot(worktree.path),
17
+ readTask(worktree.path),
18
+ ]);
19
+ const health = healthResult.status === "fulfilled" ? healthResult.value : null;
20
+ const lastCommitTimestamp = lastCommitResult.status === "fulfilled" ? lastCommitResult.value : null;
21
+ const slot = slotResult.status === "fulfilled" ? slotResult.value : null;
22
+ const task = taskResult.status === "fulfilled" ? (taskResult.value?.task ?? null) : null;
23
+ return {
24
+ ...worktree,
25
+ lastCommitTimestamp,
26
+ slot,
27
+ status: health?.status ?? "unknown",
28
+ task,
29
+ upstream: buildUpstreamState(worktree.branch, health),
30
+ };
31
+ }
32
+ function buildUpstreamState(branch, health) {
33
+ if (branch === null) {
34
+ return { kind: "detached" };
35
+ }
36
+ if (health === null) {
37
+ return { kind: "unknown" };
38
+ }
39
+ if (!health.hasUpstream) {
40
+ return { kind: "no-upstream" };
41
+ }
42
+ if (health.upstreamGone) {
43
+ return { kind: "stale" };
44
+ }
45
+ return {
46
+ ahead: health.ahead,
47
+ behind: health.behind,
48
+ kind: "tracked",
49
+ };
50
+ }
51
+ export function serializeWorktreeInfo(info) {
52
+ return {
53
+ branch: info.branch,
54
+ lastCommitTimestamp: info.lastCommitTimestamp,
55
+ path: info.path,
56
+ slot: info.slot,
57
+ status: info.status,
58
+ task: info.task,
59
+ upstream: info.upstream,
60
+ };
61
+ }
@@ -1,9 +1,3 @@
1
- import { type RepositoryContext, type WorktreeEntry } from "./repo.js";
2
- export interface LinkedWorktreeContext {
3
- linkedWorktrees: WorktreeEntry[];
4
- repository: RepositoryContext;
5
- }
6
- export declare function loadLinkedWorktrees(cwd: string): Promise<LinkedWorktreeContext>;
7
1
  export declare function removeWorktree(repoRoot: string, worktreePath: string): Promise<void>;
8
2
  export declare function forceRemoveWorktree(repoRoot: string, worktreePath: string): Promise<void>;
9
3
  export declare function deleteBranch(repoRoot: string, branch: string): Promise<void>;
@@ -1,18 +1,10 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { rm } from "node:fs/promises";
3
3
  import { promisify } from "node:util";
4
- import { detectRepository, listWorktrees, } from "./repo.js";
4
+ import { listWorktrees } from "../repository/worktrees.js";
5
5
  const execFileAsync = promisify(execFile);
6
6
  // Force English output so error message string matching is locale-independent.
7
7
  const GIT_ENV = { ...process.env, LC_ALL: "C" };
8
- export async function loadLinkedWorktrees(cwd) {
9
- const repository = await detectRepository(cwd);
10
- const linkedWorktrees = (await listWorktrees(cwd)).filter((worktree) => worktree.path !== repository.repoRoot);
11
- return {
12
- linkedWorktrees,
13
- repository,
14
- };
15
- }
16
8
  export async function removeWorktree(repoRoot, worktreePath) {
17
9
  await execFileAsync("git", ["worktree", "remove", worktreePath], {
18
10
  cwd: repoRoot,
@@ -0,0 +1,61 @@
1
+ import type { EffectiveGjiConfig } from "./config.js";
2
+ import type { CommandRunner } from "./process.js";
3
+ export type BootstrapKind = "dependency" | "build-cache" | "sync-file";
4
+ export type BootstrapState = "installed" | "failed";
5
+ export type DependencyBootstrapMode = "off" | "install";
6
+ export interface BootstrapEvent {
7
+ adapter: string;
8
+ kind: BootstrapKind;
9
+ reason?: string;
10
+ state: BootstrapState;
11
+ target: string;
12
+ message: string;
13
+ }
14
+ export interface DependencyBootstrapReporter {
15
+ dependency(event: BootstrapEvent): void;
16
+ }
17
+ export interface DependencyBootstrapReport {
18
+ mode: DependencyBootstrapMode;
19
+ ready: boolean;
20
+ events: readonly BootstrapEvent[];
21
+ }
22
+ export interface DependencyBootstrapPreview {
23
+ mode: DependencyBootstrapMode;
24
+ targets: readonly {
25
+ adapter: string;
26
+ kind: BootstrapKind;
27
+ target: string;
28
+ command: string;
29
+ }[];
30
+ }
31
+ export interface DependencyBootstrapPreviewContext {
32
+ repoRoot: string;
33
+ currentRoot?: string;
34
+ worktreePath: string;
35
+ cargoBuildCommand?: string;
36
+ }
37
+ export interface DependencyBootstrapPort {
38
+ resolveMode(dependencyBootstrap: "off" | undefined, noInstall?: boolean): DependencyBootstrapMode;
39
+ preview(mode: DependencyBootstrapMode, context: DependencyBootstrapPreviewContext): Promise<DependencyBootstrapPreview>;
40
+ }
41
+ export interface WorktreeBootstrapOptions {
42
+ branch: string;
43
+ config: EffectiveGjiConfig;
44
+ currentRoot?: string;
45
+ dependencyDetectionRoot?: string;
46
+ dependencyMode: DependencyBootstrapMode;
47
+ runCommand?: CommandRunner;
48
+ commandStdout?: (chunk: string) => void;
49
+ commandStderr?: (chunk: string) => void;
50
+ repoRoot: string;
51
+ reporter: DependencyBootstrapReporter;
52
+ worktreePath: string;
53
+ }
54
+ export interface WorktreeBootstrapResult {
55
+ dependencyBootstrap: DependencyBootstrapReport;
56
+ ready: boolean;
57
+ syncFileFailures: readonly BootstrapEvent[];
58
+ }
59
+ export interface WorktreeBootstrapPort {
60
+ bootstrapWorktree(options: WorktreeBootstrapOptions): Promise<WorktreeBootstrapResult>;
61
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ export type GjiConfig = Record<string, unknown>;
2
+ export type DependencyBootstrapSetting = "off";
3
+ export declare const KNOWN_CONFIG_KEYS: ReadonlySet<string>;
4
+ export declare const KNOWN_GLOBAL_CONFIG_KEYS: ReadonlySet<string>;
5
+ export interface EffectiveGjiConfig extends GjiConfig {
6
+ branchPrefix?: string;
7
+ dependencyBuildCommand?: string;
8
+ dependencyBootstrap?: DependencyBootstrapSetting;
9
+ editor?: string;
10
+ hooks?: Record<string, unknown>;
11
+ installSaveTarget?: string;
12
+ shellIntegration?: string;
13
+ syncFiles?: readonly string[];
14
+ syncDefaultBranch?: string;
15
+ syncRemote?: string;
16
+ worktreePath?: string;
17
+ }
18
+ export interface ConfigPort {
19
+ loadEffectiveConfig(root: string, home?: string, onWarning?: (message: string) => void): Promise<EffectiveGjiConfig>;
20
+ resolveConfigString(config: GjiConfig, key: string): string | undefined;
21
+ }
@@ -0,0 +1,17 @@
1
+ export const KNOWN_CONFIG_KEYS = new Set([
2
+ "branchPrefix",
3
+ "dependencyBuildCommand",
4
+ "dependencyBootstrap",
5
+ "editor",
6
+ "hooks",
7
+ "installSaveTarget",
8
+ "shellIntegration",
9
+ "syncDefaultBranch",
10
+ "syncFiles",
11
+ "syncRemote",
12
+ "worktreePath",
13
+ ]);
14
+ export const KNOWN_GLOBAL_CONFIG_KEYS = new Set([
15
+ ...KNOWN_CONFIG_KEYS,
16
+ "repos",
17
+ ]);
@@ -0,0 +1,6 @@
1
+ export interface EditorDefinition {
2
+ cli: string;
3
+ name: string;
4
+ newWindowFlag?: string;
5
+ supportsWorkspace: boolean;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export interface RemoteBase {
2
+ branch: string;
3
+ ref: string;
4
+ }
5
+ export interface GitCommandPort {
6
+ runGit(cwd: string, args: string[]): Promise<string>;
7
+ runGitRaw(cwd: string, args: string[]): Promise<string>;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export interface HistoryEntry {
2
+ branch: string | null;
3
+ path: string;
4
+ timestamp: number;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ export type GjiHookCommand = string | string[];
2
+ export interface GjiHooks {
3
+ "after-create"?: GjiHookCommand;
4
+ "after-enter"?: GjiHookCommand;
5
+ "before-remove"?: GjiHookCommand;
6
+ }
7
+ export interface HookContext {
8
+ branch?: string;
9
+ path: string;
10
+ repo: string;
11
+ slot?: number | null;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -3,4 +3,3 @@ export interface CommandRunnerOptions {
3
3
  env?: NodeJS.ProcessEnv;
4
4
  shell?: boolean;
5
5
  }
6
- export declare const runCommand: CommandRunner;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ export interface PullRequestInfo {
2
+ number: number;
3
+ sourceBranch: string;
4
+ title?: string;
5
+ url: string;
6
+ }
7
+ export interface PullRequestPort {
8
+ findOpenPullRequest(repoRoot: string, number: number): Promise<PullRequestInfo | null>;
9
+ listOpenPullRequests(repoRoot: string, sourceBranch: string): Promise<PullRequestInfo[]>;
10
+ listOpenPullRequestsForRepository(repoRoot: string): Promise<PullRequestInfo[]>;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { RepositoryContext } from "../domain/repository/context.js";
2
+ import type { RepoRegistryEntry } from "../domain/repository/registry.js";
3
+ import type { WorktreeEntry } from "../domain/worktree/types.js";
4
+ export interface RepositoryContextPort {
5
+ detectRepository(cwd: string): Promise<RepositoryContext>;
6
+ }
7
+ export interface WorktreePort {
8
+ listWorktrees(cwd: string): Promise<WorktreeEntry[]>;
9
+ }
10
+ export interface RepositoryRefPort {
11
+ getRepositoryRemoteUrl(repoRoot: string, remote: string): Promise<string | null>;
12
+ hasLocalBranch(repoRoot: string, branch: string): Promise<boolean>;
13
+ hasRemoteBranch(repoRoot: string, remote: string, branch: string): Promise<boolean>;
14
+ }
15
+ export interface RepositoryRegistryPort {
16
+ loadRegistry(): Promise<RepoRegistryEntry[]>;
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import type { DependencyBootstrapReporter } from "./dependency-bootstrap.js";
1
+ import type { DependencyBootstrapReporter } from "../../ports/bootstrap.js";
2
2
  export declare function createBootstrapReporter(write: (chunk: string) => void, json: boolean): DependencyBootstrapReporter & {
3
3
  write: (chunk: string) => void;
4
4
  };
@@ -0,0 +1,2 @@
1
+ import type { DependencyBootstrapPreview } from "../../ports/bootstrap.js";
2
+ export declare function formatDependencyBootstrapPreview(preview: DependencyBootstrapPreview | undefined): string;
@@ -1,7 +1,3 @@
1
- import { prepareDependencyBootstrap, previewDependencyBootstrap, } from "./dependency-bootstrap.js";
2
- export async function createDependencyBootstrapPreview(mode, context) {
3
- return previewDependencyBootstrap(await prepareDependencyBootstrap(mode, context));
4
- }
5
1
  export function formatDependencyBootstrapPreview(preview) {
6
2
  if (!preview)
7
3
  return "";
@@ -1,8 +1,8 @@
1
- import { KNOWN_GLOBAL_CONFIG_KEYS } from "./config.js";
1
+ import { KNOWN_GLOBAL_CONFIG_KEYS } from "../../ports/config.js";
2
2
  const TOP_LEVEL_COMMANDS = [
3
3
  {
4
4
  name: "new",
5
- description: "create a new branch or detached linked worktree",
5
+ description: "create a branch with an optional linked worktree",
6
6
  },
7
7
  {
8
8
  name: "done",
@@ -149,7 +149,7 @@ _gji_completion() {
149
149
 
150
150
  case "$command_name" in
151
151
  new)
152
- COMPREPLY=( $(compgen -W "--detached --from-current --no-fetch --take --copy --task --force --open --editor --dry-run --no-install --json --help" -- "$cur") )
152
+ COMPREPLY=( $(compgen -W "--branch-only --detached --from-current --no-fetch --take --copy --task --force --open --editor --dry-run --no-install --json --help" -- "$cur") )
153
153
  ;;
154
154
  done)
155
155
  COMPREPLY=( $(compgen -W "--force --keep-branch --json --help" -- "$cur") )
@@ -387,6 +387,7 @@ complete -c gji -n '__fish_seen_subcommand_from undo' -l json -d 'emit JSON on s
387
387
  ${commandLines}
388
388
 
389
389
  complete -c gji -n '__fish_seen_subcommand_from new' -l detached -d 'create a detached worktree without a branch'
390
+ complete -c gji -n '__fish_seen_subcommand_from new' -l branch-only -d 'update the default branch and create the new branch in the current worktree'
390
391
  complete -c gji -n '__fish_seen_subcommand_from new' -l from-current -d 'base the new branch on the current worktree instead of the main worktree'
391
392
  complete -c gji -n '__fish_seen_subcommand_from new' -l take -d 'move current uncommitted changes into the new worktree'
392
393
  complete -c gji -n '__fish_seen_subcommand_from new' -l copy -d 'copy current uncommitted changes instead of moving them (requires --take)'
@@ -553,7 +554,7 @@ fi
553
554
 
554
555
  case "\${words[2]}" in
555
556
  new)
556
- _arguments '--detached[create a detached worktree without a branch]' '--from-current[base the new branch on the current worktree instead of the main worktree]' '--no-fetch[skip refreshing the remote default branch before creating the new branch]' '--take[move current uncommitted changes into the new worktree]' '--copy[copy current uncommitted changes instead of moving them (requires --take)]' '--task[record the purpose of the new worktree]:description:' '--force[remove and recreate the worktree if the target path already exists]' '--open[open the new worktree in an editor after creation]' '--editor[editor CLI to use with --open (code, cursor, zed, …)]:editor:' '--dry-run[show what would be created without executing any git commands or writing files]' '--no-install[skip automatic dependency setup in the new worktree]' '--json[emit JSON on success or error instead of human-readable output]' '2:branch: '
557
+ _arguments '--branch-only[update the default branch and create the new branch in the current worktree]' '--detached[create a detached worktree without a branch]' '--from-current[base the new branch on the current worktree instead of the main worktree]' '--no-fetch[skip refreshing the remote default branch before creating the new branch]' '--take[move current uncommitted changes into the new worktree]' '--copy[copy current uncommitted changes instead of moving them (requires --take)]' '--task[record the purpose of the new worktree]:description:' '--force[remove and recreate the worktree if the target path already exists]' '--open[open the new worktree in an editor after creation]' '--editor[editor CLI to use with --open (code, cursor, zed, …)]:editor:' '--dry-run[show what would be created without executing any git commands or writing files]' '--no-install[skip automatic dependency setup in the new worktree]' '--json[emit JSON on success or error instead of human-readable output]' '2:branch: '
557
558
  ;;
558
559
  done)
559
560
  _arguments '--force[remove dirty or unmerged worktrees without prompting]' '--keep-branch[remove the worktree but preserve its branch]' '--json[emit JSON on success or error instead of human-readable output]' '2:branch: '
@@ -0,0 +1,2 @@
1
+ import type { ContextCardModel } from "../../application/worktree/context-card.js";
2
+ export declare function renderContextCard(model: ContextCardModel): string;
@@ -1,16 +1,7 @@
1
- import { listWorktrees } from "./repo.js";
2
- import { readTask } from "./task.js";
3
- import { readWorktreeInfo } from "./worktree-info.js";
4
- export async function renderContextCard(worktreePath) {
5
- const worktree = (await listWorktrees(worktreePath)).find((entry) => entry.path === worktreePath);
6
- if (!worktree)
7
- return null;
8
- const info = await readWorktreeInfo(worktree);
9
- const task = await readTask(worktreePath);
10
- if (!task)
11
- return null;
1
+ export function renderContextCard(model) {
2
+ const { info, task } = model;
12
3
  const rows = [`┌ ${info.branch ?? "(detached)"}`];
13
- rows.push(`│ task ${task.task}`);
4
+ rows.push(`│ task ${task}`);
14
5
  rows.push(`│ state ${formatState(info)}`);
15
6
  if (info.lastCommitTimestamp !== null)
16
7
  rows.push(`│ last ${formatLastCommit(info)}`);
@@ -0,0 +1,4 @@
1
+ import type { UpstreamState } from "../../application/worktree/read-models.js";
2
+ export declare function formatUpstreamState(upstream: UpstreamState): string;
3
+ export declare function formatLastCommit(timestampSeconds: number | null): string;
4
+ export declare function formatRelativeAge(timestampSeconds: number, nowSeconds?: number): string;
@@ -0,0 +1,41 @@
1
+ export function formatUpstreamState(upstream) {
2
+ if (upstream.kind === "detached")
3
+ return "n/a";
4
+ if (upstream.kind === "no-upstream")
5
+ return "no-upstream";
6
+ if (upstream.kind === "stale")
7
+ return "gone";
8
+ if (upstream.kind === "unknown")
9
+ return "unknown";
10
+ return formatAheadBehind(upstream.ahead, upstream.behind);
11
+ }
12
+ function formatAheadBehind(ahead, behind) {
13
+ if (ahead === 0 && behind === 0)
14
+ return "up to date";
15
+ if (ahead === 0)
16
+ return `behind ${behind}`;
17
+ if (behind === 0)
18
+ return `ahead ${ahead}`;
19
+ return `ahead ${ahead}, behind ${behind}`;
20
+ }
21
+ export function formatLastCommit(timestampSeconds) {
22
+ return timestampSeconds === null
23
+ ? "n/a"
24
+ : formatRelativeAge(timestampSeconds);
25
+ }
26
+ export function formatRelativeAge(timestampSeconds, nowSeconds = Math.floor(Date.now() / 1000)) {
27
+ const ageSeconds = Math.max(0, nowSeconds - timestampSeconds);
28
+ const units = [
29
+ { label: "y", seconds: 365 * 24 * 60 * 60 },
30
+ { label: "mo", seconds: 30 * 24 * 60 * 60 },
31
+ { label: "d", seconds: 24 * 60 * 60 },
32
+ { label: "h", seconds: 60 * 60 },
33
+ { label: "m", seconds: 60 },
34
+ ];
35
+ for (const unit of units) {
36
+ const value = Math.floor(ageSeconds / unit.seconds);
37
+ if (value > 0)
38
+ return `${value}${unit.label} ago`;
39
+ }
40
+ return "just now";
41
+ }
@@ -1,7 +1,8 @@
1
1
  import type { Readable, Writable } from "node:stream";
2
- import { type PullRequestInfo } from "./pull-requests.js";
3
- import type { WorktreeEntry } from "./repo.js";
4
- import type { WorktreeSource } from "./worktree-source.js";
2
+ import { type WorktreeCatalogDependencies, type WorktreeMetadataMode } from "../../application/worktree/catalog.js";
3
+ import type { WorktreeSource } from "../../domain/worktree/source.js";
4
+ import type { WorktreeEntry } from "../../domain/worktree/types.js";
5
+ import type { PullRequestInfo, PullRequestPort } from "../../ports/pull-requests.js";
5
6
  export type WorktreePromptSource = WorktreeSource;
6
7
  export interface WorktreePromptEntry extends WorktreeEntry {
7
8
  group: "recent" | "other";
@@ -11,14 +12,12 @@ export interface WorktreePromptEntry extends WorktreeEntry {
11
12
  repoName: string;
12
13
  task?: string | null;
13
14
  }
14
- export type QueryWorktreePullRequests = (repoRoot: string, sourceBranch: string) => Promise<PullRequestInfo[]>;
15
- export type QueryRepositoryPullRequests = (repoRoot: string) => Promise<PullRequestInfo[]>;
15
+ export type QueryWorktreePullRequests = PullRequestPort["listOpenPullRequests"];
16
+ export type QueryRepositoryPullRequests = PullRequestPort["listOpenPullRequestsForRepository"];
16
17
  export interface BuildWorktreePromptEntriesDependencies {
17
18
  metadata?: WorktreeMetadataMode;
18
- queryPullRequests?: QueryWorktreePullRequests;
19
- queryRepositoryPullRequests?: QueryRepositoryPullRequests;
19
+ catalog?: WorktreeCatalogDependencies;
20
20
  }
21
- export type WorktreeMetadataMode = "full" | "fast";
22
21
  export interface WorktreePickerIO {
23
22
  input?: Readable & {
24
23
  isTTY?: boolean;
@@ -40,9 +39,6 @@ export interface WorktreePromptScopeResult {
40
39
  toggleLabel: string;
41
40
  }
42
41
  export declare function buildWorktreePromptEntries(sources: WorktreeSource[], dependencies?: BuildWorktreePromptEntriesDependencies): Promise<WorktreePromptEntry[]>;
43
- export declare function resolveWorktreeQuery(sources: WorktreeSource[], query: string): WorktreeSource | null;
44
- export declare function resolveWorktreeQueryMatches(sources: WorktreeSource[], query: string): WorktreeSource[];
45
- export declare function resolveExactWorktreeQueryMatches(sources: WorktreeSource[], query: string): WorktreeSource[];
46
42
  export declare function promptForSingleWorktree(message: string, worktrees: WorktreePromptEntry[], io?: WorktreePickerIO & {
47
43
  scope?: WorktreePromptScope;
48
44
  }): Promise<string | null>;