@solaqua/gji 0.14.0 → 0.16.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 (67) hide show
  1. package/README.md +63 -120
  2. package/dist/cli/commands/back.d.ts +1 -1
  3. package/dist/cli/commands/back.js +1 -0
  4. package/dist/cli/commands/clean.d.ts +2 -1
  5. package/dist/cli/commands/clean.js +66 -18
  6. package/dist/cli/commands/done.js +3 -2
  7. package/dist/cli/commands/go.js +18 -5
  8. package/dist/cli/commands/init.js +0 -14
  9. package/dist/cli/commands/new.d.ts +2 -1
  10. package/dist/cli/commands/new.js +71 -63
  11. package/dist/cli/commands/open.js +1 -1
  12. package/dist/cli/commands/pr-open.js +2 -2
  13. package/dist/cli/commands/pr.js +17 -6
  14. package/dist/cli/commands/run-hook.js +4 -0
  15. package/dist/cli/commands/undo.js +10 -74
  16. package/dist/cli/dependencies.d.ts +5 -1
  17. package/dist/cli/dependencies.js +7 -2
  18. package/dist/cli/program.js +6 -56
  19. package/dist/cli/runtime/headless.d.ts +3 -3
  20. package/dist/cli/runtime/headless.js +6 -4
  21. package/dist/domain/worktree/path-filter.d.ts +13 -0
  22. package/dist/domain/worktree/path-filter.js +72 -0
  23. package/dist/domain/worktree/policy.d.ts +1 -0
  24. package/dist/domain/worktree/policy.js +1 -1
  25. package/dist/gji-bundle.mjs +738 -969
  26. package/dist/infrastructure/filesystem/locked-file.d.ts +4 -0
  27. package/dist/infrastructure/filesystem/locked-file.js +53 -0
  28. package/dist/infrastructure/git/runner.d.ts +7 -0
  29. package/dist/infrastructure/git/runner.js +35 -1
  30. package/dist/infrastructure/persistence/history.js +12 -10
  31. package/dist/infrastructure/persistence/slots.js +24 -21
  32. package/dist/infrastructure/process/hooks.js +2 -0
  33. package/dist/infrastructure/repository/registry.js +11 -41
  34. package/dist/infrastructure/worktree/lifecycle.d.ts +25 -0
  35. package/dist/infrastructure/worktree/lifecycle.js +120 -19
  36. package/dist/ports/git.d.ts +1 -0
  37. package/dist/ports/hooks.d.ts +7 -0
  38. package/dist/presentation/shell/completion.js +14 -32
  39. package/dist/presentation/terminal/context-card.js +1 -1
  40. package/man/man1/gji-back.1 +1 -1
  41. package/man/man1/gji-clean.1 +4 -1
  42. package/man/man1/gji-completion.1 +1 -1
  43. package/man/man1/gji-config.1 +1 -1
  44. package/man/man1/gji-doctor.1 +1 -1
  45. package/man/man1/gji-done.1 +1 -1
  46. package/man/man1/gji-go.1 +1 -1
  47. package/man/man1/gji-history.1 +1 -1
  48. package/man/man1/gji-init.1 +1 -1
  49. package/man/man1/gji-ls.1 +1 -1
  50. package/man/man1/gji-new.1 +1 -1
  51. package/man/man1/gji-open.1 +1 -1
  52. package/man/man1/gji-pr.1 +1 -1
  53. package/man/man1/gji-root.1 +1 -1
  54. package/man/man1/gji-run-hook.1 +3 -3
  55. package/man/man1/gji-status.1 +1 -1
  56. package/man/man1/gji-sync-files.1 +1 -1
  57. package/man/man1/gji-sync.1 +1 -1
  58. package/man/man1/gji-task.1 +1 -1
  59. package/man/man1/gji-undo.1 +1 -1
  60. package/man/man1/gji.1 +2 -12
  61. package/package.json +3 -2
  62. package/dist/cli/commands/remove.d.ts +0 -21
  63. package/dist/cli/commands/remove.js +0 -189
  64. package/dist/cli/commands/warp.d.ts +0 -28
  65. package/dist/cli/commands/warp.js +0 -123
  66. package/man/man1/gji-remove.1 +0 -19
  67. package/man/man1/gji-warp.1 +0 -16
@@ -1,4 +1,4 @@
1
- .TH GJI\-BACK 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-BACK 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-back \- navigate to the previously visited worktree, optionally N steps back
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-CLEAN 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-CLEAN 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-clean \- interactively prune linked worktrees
4
4
  .SH SYNOPSIS
@@ -13,6 +13,9 @@ bypass prompts, force\-remove dirty worktrees, and force\-delete unmerged branch
13
13
  .B \-\-stale
14
14
  only target clean worktrees whose upstream is gone and branch is merged into the default branch
15
15
  .TP
16
+ .B \-\-path <pattern>
17
+ only target worktrees under a directory or matching a path glob (repeatable)
18
+ .TP
16
19
  .B \-\-dry\-run
17
20
  show what would be deleted without removing anything
18
21
  .TP
@@ -1,4 +1,4 @@
1
- .TH GJI\-COMPLETION 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-COMPLETION 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-completion \- print shell completion definitions
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-CONFIG 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-CONFIG 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-config \- manage global config defaults
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-DOCTOR 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-DOCTOR 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-doctor \- check gji installation and configuration health
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-DONE 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-DONE 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-done \- finish the current linked worktree and return safely
4
4
  .SH SYNOPSIS
package/man/man1/gji-go.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI\-GO 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-GO 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-go \- resolve and jump to a worktree path
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-HISTORY 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-HISTORY 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-history \- show navigation history
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-INIT 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-INIT 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-init \- set up shell integration interactively or print a shell wrapper
4
4
  .SH SYNOPSIS
package/man/man1/gji-ls.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI\-LS 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-LS 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-ls \- list active worktrees
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-NEW 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-NEW 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-new \- create a branch with an optional linked worktree
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-OPEN 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-OPEN 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-open \- open the worktree in an editor
4
4
  .SH SYNOPSIS
package/man/man1/gji-pr.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI\-PR 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-PR 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-pr \- fetch a pull request by number, #number, or URL into a linked worktree
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-ROOT 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-ROOT 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-root \- print the main repository root path
4
4
  .SH SYNOPSIS
@@ -1,9 +1,9 @@
1
- .TH GJI\-RUN\-HOOK 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-RUN\-HOOK 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
- gji\-run\-hook \- run a named hook (after\-create, after\-enter, before\-remove) in the current worktree
3
+ gji\-run\-hook \- run a named hook (after\-branch\-create, after\-create, after\-enter, after\-switch, before\-remove) in the current worktree
4
4
  .SH SYNOPSIS
5
5
  .B gji run\-hook [options] <hook>
6
6
  .SH DESCRIPTION
7
- run a named hook (after\-create, after\-enter, before\-remove) in the current worktree
7
+ run a named hook (after\-branch\-create, after\-create, after\-enter, after\-switch, before\-remove) in the current worktree
8
8
  .SH "SEE ALSO"
9
9
  .BR gji (1)
@@ -1,4 +1,4 @@
1
- .TH GJI\-STATUS 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-STATUS 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-status \- summarize repository and worktree health
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-SYNC\-FILES 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-SYNC\-FILES 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-sync\-files \- manage local files copied into new worktrees
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-SYNC 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-SYNC 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-sync \- fetch and update one or all worktrees
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-TASK 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-TASK 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-task \- show or update the current worktree task
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-UNDO 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI\-UNDO 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji\-undo \- restore the most recent destructive worktree operation
4
4
  .SH SYNOPSIS
package/man/man1/gji.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI 1 "August 2026" "gji 0.14.0" "User Commands"
1
+ .TH GJI 1 "October 2026" "gji 0.16.0" "User Commands"
2
2
  .SH NAME
3
3
  gji \- Context switching without the mess.
4
4
  .SH SYNOPSIS
@@ -67,19 +67,11 @@ list active worktrees
67
67
  .B clean [options]
68
68
  interactively prune linked worktrees
69
69
  .TP
70
- .B remove [options] [branch]
71
- deprecated: use gji done for one worktree or gji clean for bulk cleanup
72
- .br
73
- Alias: \fBrm\fR
74
- .TP
75
70
  .B run\-hook [options] <hook>
76
- run a named hook (after\-create, after\-enter, before\-remove) in the current worktree
71
+ run a named hook (after\-branch\-create, after\-create, after\-enter, after\-switch, before\-remove) in the current worktree
77
72
  .br
78
73
  Alias: \fBtrigger\-hook\fR
79
74
  .TP
80
- .B warp [options] [branch]
81
- deprecated: use gji go (press Tab for all known repos)
82
- .TP
83
75
  .B config [options] [command]
84
76
  manage global config defaults
85
77
  .SH OPTIONS
@@ -105,7 +97,5 @@ output the version number
105
97
  .BR gji\-sync\-files (1),
106
98
  .BR gji\-ls (1),
107
99
  .BR gji\-clean (1),
108
- .BR gji\-remove (1),
109
100
  .BR gji\-run\-hook (1),
110
- .BR gji\-warp (1),
111
101
  .BR gji\-config (1)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@solaqua/gji",
3
- "version": "0.14.0",
4
- "description": "Git worktree CLI for fast context switching.",
3
+ "version": "0.16.0",
4
+ "description": "Git worktree CLI for switching tasks without disturbing your work.",
5
5
  "license": "MIT",
6
6
  "author": "sjquant",
7
7
  "type": "module",
@@ -65,6 +65,7 @@
65
65
  "devDependencies": {
66
66
  "@biomejs/biome": "^2.4.15",
67
67
  "@j178/prek": "^0.3.13",
68
+ "@resvg/resvg-js": "^2.6.2",
68
69
  "@types/node": "^24.6.0",
69
70
  "@vitest/coverage-v8": "3.2.4",
70
71
  "esbuild": "^0.27.0",
@@ -1,21 +0,0 @@
1
- import type { WorktreeEntry } from "../../domain/worktree/types.js";
2
- import { type WorktreePromptEntry } from "../../presentation/worktree/picker.js";
3
- import { type CliRuntime } from "../dependencies.js";
4
- export interface RemoveCommandOptions {
5
- branch?: string;
6
- cwd: string;
7
- dryRun?: boolean;
8
- force?: boolean;
9
- json?: boolean;
10
- runtime?: CliRuntime<"git" | "config" | "configStore" | "slots" | "hooks" | "repositoryContext" | "repositoryRegistry" | "worktrees" | "worktreeLifecycle" | "worktreeCatalog">;
11
- stderr: (chunk: string) => void;
12
- stdout: (chunk: string) => void;
13
- }
14
- export interface RemoveCommandDependencies {
15
- confirmForceDeleteBranch: (branch: string) => Promise<boolean>;
16
- confirmForceRemoveWorktree: (worktreePath: string) => Promise<boolean>;
17
- confirmRemoval: (worktree: WorktreeEntry) => Promise<boolean>;
18
- promptForWorktree: (worktrees: WorktreePromptEntry[]) => Promise<string | null>;
19
- }
20
- export declare function createRemoveCommand(dependencies?: Partial<RemoveCommandDependencies>): (options: RemoveCommandOptions) => Promise<number>;
21
- export declare const runRemoveCommand: (options: RemoveCommandOptions) => Promise<number>;
@@ -1,189 +0,0 @@
1
- import { basename } from "node:path";
2
- import { confirm, isCancel } from "@clack/prompts";
3
- import { loadLinkedWorktrees } from "../../application/worktree/catalog.js";
4
- import { writeShellOutput } from "../../presentation/shell/handoff.js";
5
- import { buildWorktreePromptEntries, promptForSingleWorktree, } from "../../presentation/worktree/picker.js";
6
- import { defaultConfirmForceDeleteBranch, defaultConfirmForceRemoveWorktree, } from "../../presentation/worktree/prompts.js";
7
- import { defaultCliDependencies } from "../dependencies.js";
8
- import { isHeadless } from "../runtime/headless.js";
9
- import { finalizeUndoOperation, recordUndoOperation } from "./undo.js";
10
- const REMOVE_OUTPUT_FILE_ENV = "GJI_REMOVE_OUTPUT_FILE";
11
- export function createRemoveCommand(dependencies = {}) {
12
- const promptForWorktree = dependencies.promptForWorktree ?? defaultPromptForWorktree;
13
- const confirmRemoval = dependencies.confirmRemoval ?? defaultConfirmRemoval;
14
- const confirmForceRemoveWorktree = dependencies.confirmForceRemoveWorktree ??
15
- defaultConfirmForceRemoveWorktree;
16
- const confirmForceDeleteBranch = dependencies.confirmForceDeleteBranch ?? defaultConfirmForceDeleteBranch;
17
- return async function runRemoveCommand(options) {
18
- const runtime = options.runtime ?? defaultCliDependencies;
19
- const { loadEffectiveConfig } = runtime.config;
20
- const { releaseWorktreeSlot } = runtime.slots;
21
- const { extractHooks, runHook } = runtime.hooks;
22
- const sourceDependencies = {
23
- ...runtime.repositoryContext,
24
- ...runtime.repositoryRegistry,
25
- ...runtime.worktrees,
26
- };
27
- const { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeForceRemovalError, removeWorktree, } = runtime.worktreeLifecycle;
28
- const { linkedWorktrees, repository } = await loadLinkedWorktrees(options.cwd, sourceDependencies);
29
- if (linkedWorktrees.length === 0) {
30
- emitError(options, "No linked worktrees to finish");
31
- return 1;
32
- }
33
- if (!options.branch && (options.json || isHeadless())) {
34
- const message = "branch argument is required";
35
- if (options.json) {
36
- emitError(options, message);
37
- }
38
- else {
39
- options.stderr(`gji remove: ${message} in non-interactive mode (GJI_NO_TUI=1)\n`);
40
- }
41
- return 1;
42
- }
43
- const selection = options.branch ??
44
- (await promptForWorktree(await buildWorktreePromptEntries(linkedWorktrees.map((worktree) => ({
45
- repoName: repository.repoName,
46
- worktree,
47
- })), { catalog: runtime.worktreeCatalog })));
48
- if (!selection) {
49
- options.stderr("Aborted\n");
50
- return 1;
51
- }
52
- const worktree = linkedWorktrees.find((entry) => entry.branch === selection || entry.path === selection);
53
- if (!worktree) {
54
- emitError(options, `No linked worktree found for branch: ${selection}`);
55
- return 1;
56
- }
57
- if (!options.dryRun && !options.force && (options.json || isHeadless())) {
58
- const message = "--force is required";
59
- if (options.json) {
60
- emitError(options, message);
61
- }
62
- else {
63
- options.stderr(`gji remove: ${message} in non-interactive mode (GJI_NO_TUI=1)\n`);
64
- }
65
- return 1;
66
- }
67
- if (!options.dryRun &&
68
- !options.force &&
69
- !(await confirmRemoval(worktree))) {
70
- options.stderr("Aborted\n");
71
- return 1;
72
- }
73
- if (options.dryRun) {
74
- if (options.json) {
75
- options.stdout(`${JSON.stringify({ branch: worktree.branch, path: worktree.path, dryRun: true }, null, 2)}\n`);
76
- }
77
- else {
78
- const desc = worktree.branch
79
- ? `branch: ${worktree.branch}`
80
- : "detached";
81
- options.stdout(`Would remove worktree at ${worktree.path} (${desc})\n`);
82
- }
83
- return 0;
84
- }
85
- const config = await loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
86
- const hooks = extractHooks(config);
87
- let journal;
88
- try {
89
- journal = await recordUndoOperation("remove", repository.repoRoot, [worktree], undefined, runtime);
90
- }
91
- catch (error) {
92
- emitError(options, `could not write undo journal; no worktree was removed: ${toMessage(error)}`);
93
- return 1;
94
- }
95
- if (!journal) {
96
- emitError(options, "could not capture undo state; no worktree was removed");
97
- return 1;
98
- }
99
- await runHook(hooks["before-remove"], worktree.path, {
100
- branch: worktree.branch ?? undefined,
101
- path: worktree.path,
102
- repo: basename(repository.repoRoot),
103
- }, options.stderr);
104
- try {
105
- await removeWorktree(repository.repoRoot, worktree.path);
106
- }
107
- catch (error) {
108
- if (!isWorktreeForceRemovalError(error)) {
109
- await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
110
- throw error;
111
- }
112
- if (!options.force &&
113
- !(await confirmForceRemoveWorktree(worktree.path))) {
114
- await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
115
- options.stderr("Aborted\n");
116
- return 1;
117
- }
118
- try {
119
- await forceRemoveWorktree(repository.repoRoot, worktree.path);
120
- }
121
- catch (forceError) {
122
- await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
123
- emitError(options, `Failed to remove worktree at ${worktree.path}: ${toMessage(forceError)}`);
124
- return 1;
125
- }
126
- }
127
- await finalizeUndoOperation(journal, [worktree], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
128
- await releaseWorktreeSlot(worktree.path);
129
- if (worktree.branch) {
130
- try {
131
- await deleteBranch(repository.repoRoot, worktree.branch);
132
- }
133
- catch (error) {
134
- if (!isBranchUnmergedError(error)) {
135
- throw error;
136
- }
137
- if (options.force ||
138
- (await confirmForceDeleteBranch(worktree.branch))) {
139
- try {
140
- await forceDeleteBranch(repository.repoRoot, worktree.branch);
141
- }
142
- catch (forceError) {
143
- options.stderr(`Failed to delete branch ${worktree.branch}: ${toMessage(forceError)}\n`);
144
- }
145
- }
146
- else {
147
- options.stderr(`Branch ${worktree.branch} was not deleted (has unmerged commits)\n`);
148
- }
149
- }
150
- }
151
- if (options.json) {
152
- options.stdout(`${JSON.stringify({ branch: worktree.branch, path: worktree.path, deleted: true }, null, 2)}\n`);
153
- }
154
- else {
155
- options.stderr("undo: gji undo\n");
156
- await writeOutput(repository.repoRoot, options.stdout);
157
- }
158
- return 0;
159
- };
160
- }
161
- export const runRemoveCommand = createRemoveCommand();
162
- async function defaultPromptForWorktree(worktrees) {
163
- return promptForSingleWorktree("Choose a worktree to finish", worktrees);
164
- }
165
- async function defaultConfirmRemoval(worktree) {
166
- const choice = await confirm({
167
- message: worktree.branch
168
- ? `Remove worktree and delete branch ${worktree.branch}?`
169
- : `Remove detached worktree ${worktree.path}?`,
170
- active: "Yes",
171
- inactive: "No",
172
- initialValue: true,
173
- });
174
- return !isCancel(choice) && choice;
175
- }
176
- async function writeOutput(repoRoot, stdout) {
177
- await writeShellOutput(REMOVE_OUTPUT_FILE_ENV, repoRoot, stdout);
178
- }
179
- function emitError(options, message) {
180
- if (options.json) {
181
- options.stderr(`${JSON.stringify({ error: message }, null, 2)}\n`);
182
- }
183
- else {
184
- options.stderr(`${message}\n`);
185
- }
186
- }
187
- function toMessage(error) {
188
- return error instanceof Error ? error.message : String(error);
189
- }
@@ -1,28 +0,0 @@
1
- import { type NavigationRepository } from "../../presentation/terminal/navigation.js";
2
- import { type QueryWorktreePullRequests } from "../../presentation/worktree/picker.js";
3
- import { type CliRuntime } from "../dependencies.js";
4
- export interface WarpCommandOptions {
5
- branch?: string;
6
- cwd: string;
7
- json?: boolean;
8
- queryPullRequests?: QueryWorktreePullRequests;
9
- runtime?: CliRuntime<"history" | "repositoryContext" | "repositoryRegistry" | "worktrees" | "worktreeCatalog">;
10
- stderr: (chunk: string) => void;
11
- stdout: (chunk: string) => void;
12
- }
13
- export declare function runWarpCommand(options: WarpCommandOptions): Promise<number>;
14
- export interface WarpTarget {
15
- branch: string | null;
16
- path: string;
17
- repository: NavigationRepository;
18
- }
19
- export declare function resolveWarpTarget(options: {
20
- branch?: string;
21
- commandName?: string;
22
- cwd: string;
23
- excludeRepoRoot?: string;
24
- json?: boolean;
25
- queryPullRequests?: QueryWorktreePullRequests;
26
- runtime?: CliRuntime<"history" | "repositoryContext" | "repositoryRegistry" | "worktrees" | "worktreeCatalog">;
27
- stderr: (chunk: string) => void;
28
- }): Promise<WarpTarget | null>;
@@ -1,123 +0,0 @@
1
- import { listRegisteredWorktreeSources } from "../../application/worktree/sources.js";
2
- import { resolveWorktreeQuery } from "../../domain/worktree/matching.js";
3
- import { writeShellOutput } from "../../presentation/shell/handoff.js";
4
- import { createNavigationRepository, createNavigationTarget, } from "../../presentation/terminal/navigation.js";
5
- import { buildWorktreePromptEntries, promptForSingleWorktree, } from "../../presentation/worktree/picker.js";
6
- import { defaultCliDependencies, withPullRequestQueries, } from "../dependencies.js";
7
- import { isHeadless } from "../runtime/headless.js";
8
- const WARP_OUTPUT_FILE_ENV = "GJI_WARP_OUTPUT_FILE";
9
- export async function runWarpCommand(options) {
10
- return runWarpNavigate(options);
11
- }
12
- async function runWarpNavigate(options) {
13
- const runtime = options.runtime ?? defaultCliDependencies;
14
- const { recordWorktreeUsage } = runtime.history;
15
- if ((isHeadless() || options.json) && !options.branch) {
16
- const message = "branch argument is required";
17
- if (options.json) {
18
- options.stderr(`${JSON.stringify({ error: message }, null, 2)}\n`);
19
- }
20
- else {
21
- options.stderr("gji warp: branch argument is required in non-interactive mode (GJI_NO_TUI=1)\n");
22
- }
23
- return 1;
24
- }
25
- const target = await resolveWarpTarget({
26
- ...options,
27
- commandName: "gji warp",
28
- json: options.json,
29
- queryPullRequests: options.queryPullRequests,
30
- runtime,
31
- });
32
- if (!target)
33
- return 1;
34
- if (options.json) {
35
- // json callers use the output programmatically; skip history and shell handoff.
36
- options.stdout(`${JSON.stringify(createNavigationTarget(target.repository, target.path, target.branch), null, 2)}\n`);
37
- return 0;
38
- }
39
- await recordWorktreeUsage(target.path, target.branch);
40
- await writeShellOutput(WARP_OUTPUT_FILE_ENV, target.path, options.stdout);
41
- return 0;
42
- }
43
- export async function resolveWarpTarget(options) {
44
- const cmd = options.commandName ?? "gji";
45
- const runtime = options.runtime ?? defaultCliDependencies;
46
- const { detectRepository } = runtime.repositoryContext;
47
- const { loadRegistry } = runtime.repositoryRegistry;
48
- const sourceDependencies = {
49
- ...runtime.repositoryContext,
50
- ...runtime.repositoryRegistry,
51
- ...runtime.worktrees,
52
- };
53
- const emitError = (message, hint) => {
54
- if (options.json) {
55
- options.stderr(`${JSON.stringify({ error: message }, null, 2)}\n`);
56
- }
57
- else {
58
- options.stderr(`${cmd}: ${message}\n`);
59
- if (hint)
60
- options.stderr(hint);
61
- }
62
- };
63
- const registry = await loadRegistry();
64
- if (registry.length === 0) {
65
- emitError("not in a git repository and no repos registered yet.", "Use any gji command inside a repository to register it.\n");
66
- return null;
67
- }
68
- const registered = await listRegisteredWorktreeSources({
69
- cwd: options.cwd,
70
- repositoryPort: sourceDependencies,
71
- });
72
- const skippedRegisteredRepos = registered.skipped.length;
73
- const allItems = registered.sources.filter((item) => item.repoRoot !== options.excludeRepoRoot);
74
- if (allItems.length === 0) {
75
- emitError("no accessible worktrees found in any registered repo.", skippedRegisteredRepos > 0
76
- ? "Hint: Run 'gji doctor' to inspect stale repository entries.\n"
77
- : undefined);
78
- return null;
79
- }
80
- const promptSources = allItems.map((item) => ({
81
- repoRoot: item.repoRoot,
82
- repoName: item.repoName,
83
- worktree: item.worktree,
84
- }));
85
- if (options.branch) {
86
- const match = resolveWorktreeQuery(promptSources, options.branch);
87
- if (!match) {
88
- emitError(`no worktree found matching: ${options.branch}`, skippedRegisteredRepos > 0
89
- ? "Hint: Run 'gji doctor' to inspect stale repository entries.\n"
90
- : undefined);
91
- return null;
92
- }
93
- return {
94
- branch: match.worktree.branch,
95
- path: match.worktree.path,
96
- repository: createNavigationRepository(match.repoName, match.repoRoot ??
97
- (await detectRepository(match.worktree.path)).repoRoot),
98
- };
99
- }
100
- const promptEntries = await buildWorktreePromptEntries(promptSources, {
101
- metadata: "fast",
102
- catalog: withPullRequestQueries(runtime, options.queryPullRequests),
103
- });
104
- const path = await promptForWarpTarget(promptEntries);
105
- if (!path) {
106
- options.stderr("Aborted\n");
107
- return null;
108
- }
109
- const chosenSource = promptSources.find((item) => item.worktree.path === path);
110
- if (chosenSource === undefined) {
111
- emitError("selected worktree is no longer registered");
112
- return null;
113
- }
114
- return {
115
- branch: chosenSource.worktree.branch,
116
- path,
117
- repository: createNavigationRepository(chosenSource.repoName, chosenSource.repoRoot ??
118
- (await detectRepository(chosenSource.worktree.path)).repoRoot),
119
- };
120
- }
121
- async function promptForWarpTarget(items) {
122
- return promptForSingleWorktree("Warp to a worktree", items);
123
- }
@@ -1,19 +0,0 @@
1
- .TH GJI\-REMOVE 1 "August 2026" "gji 0.14.0" "User Commands"
2
- .SH NAME
3
- gji\-remove \- deprecated: use gji done for one worktree or gji clean for bulk cleanup
4
- .SH SYNOPSIS
5
- .B gji remove [\fIoptions\fR] [options] [branch]
6
- .SH DESCRIPTION
7
- deprecated: use gji done for one worktree or gji clean for bulk cleanup
8
- .SH OPTIONS
9
- .TP
10
- .B \-f, \-\-force
11
- bypass prompts, force\-remove a dirty worktree, and force\-delete an unmerged branch
12
- .TP
13
- .B \-\-dry\-run
14
- show what would be deleted without removing anything
15
- .TP
16
- .B \-\-json
17
- emit JSON on success or error instead of human\-readable output
18
- .SH "SEE ALSO"
19
- .BR gji (1)
@@ -1,16 +0,0 @@
1
- .TH GJI\-WARP 1 "August 2026" "gji 0.14.0" "User Commands"
2
- .SH NAME
3
- gji\-warp \- deprecated: use gji go (press Tab for all known repos)
4
- .SH SYNOPSIS
5
- .B gji warp [\fIoptions\fR] [options] [branch]
6
- .SH DESCRIPTION
7
- deprecated: use gji go (press Tab for all known repos)
8
- .SH OPTIONS
9
- .TP
10
- .B \-\-print
11
- print the resolved worktree path without changing directory
12
- .TP
13
- .B \-\-json
14
- emit JSON on success or error instead of human\-readable output
15
- .SH "SEE ALSO"
16
- .BR gji (1)