@solaqua/gji 0.10.0 → 0.12.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 (122) hide show
  1. package/README.md +83 -426
  2. package/dist/back.d.ts +3 -0
  3. package/dist/back.js +19 -7
  4. package/dist/bootstrap-output.d.ts +3 -0
  5. package/dist/bootstrap-output.js +27 -0
  6. package/dist/bootstrap-preview.d.ts +10 -0
  7. package/dist/bootstrap-preview.js +21 -0
  8. package/dist/clean.js +27 -3
  9. package/dist/cli.d.ts +4 -0
  10. package/dist/cli.js +140 -22
  11. package/dist/clone-failure-store.d.ts +18 -0
  12. package/dist/clone-failure-store.js +230 -0
  13. package/dist/command-runner.d.ts +6 -0
  14. package/dist/command-runner.js +38 -0
  15. package/dist/completion.js +7 -2
  16. package/dist/config-command.d.ts +1 -0
  17. package/dist/config-command.js +7 -1
  18. package/dist/config.d.ts +26 -1
  19. package/dist/config.js +110 -6
  20. package/dist/context-card.d.ts +1 -0
  21. package/dist/context-card.js +25 -0
  22. package/dist/dependency-bootstrap-prompt.d.ts +24 -0
  23. package/dist/dependency-bootstrap-prompt.js +103 -0
  24. package/dist/dependency-bootstrap.d.ts +129 -0
  25. package/dist/dependency-bootstrap.js +566 -0
  26. package/dist/dir-clone.d.ts +33 -0
  27. package/dist/dir-clone.js +505 -0
  28. package/dist/doctor.js +21 -0
  29. package/dist/done.d.ts +10 -0
  30. package/dist/done.js +210 -0
  31. package/dist/file-sync.js +71 -22
  32. package/dist/format-bytes.d.ts +1 -0
  33. package/dist/format-bytes.js +14 -0
  34. package/dist/fs-utils.d.ts +4 -0
  35. package/dist/fs-utils.js +26 -0
  36. package/dist/git.d.ts +5 -0
  37. package/dist/git.js +50 -0
  38. package/dist/gji-bundle.mjs +6189 -1313
  39. package/dist/go-resolver.d.ts +35 -0
  40. package/dist/go-resolver.js +185 -0
  41. package/dist/go.d.ts +6 -2
  42. package/dist/go.js +270 -62
  43. package/dist/hooks.d.ts +2 -1
  44. package/dist/hooks.js +20 -8
  45. package/dist/hub.d.ts +31 -0
  46. package/dist/hub.js +287 -0
  47. package/dist/init.js +30 -22
  48. package/dist/install-prompt.d.ts +1 -0
  49. package/dist/install-prompt.js +3 -25
  50. package/dist/ls.js +9 -1
  51. package/dist/navigation-output.d.ts +11 -0
  52. package/dist/navigation-output.js +6 -0
  53. package/dist/new.d.ts +14 -2
  54. package/dist/new.js +422 -38
  55. package/dist/pr-open.js +2 -10
  56. package/dist/pr.d.ts +6 -2
  57. package/dist/pr.js +135 -30
  58. package/dist/pull-requests.d.ts +1 -0
  59. package/dist/pull-requests.js +32 -3
  60. package/dist/remove.js +22 -0
  61. package/dist/repo.d.ts +4 -0
  62. package/dist/repo.js +61 -6
  63. package/dist/run-hook.js +2 -0
  64. package/dist/safe-destination.d.ts +15 -0
  65. package/dist/safe-destination.js +166 -0
  66. package/dist/shell-completion.js +52 -13
  67. package/dist/slots.d.ts +8 -0
  68. package/dist/slots.js +61 -0
  69. package/dist/status.d.ts +2 -0
  70. package/dist/status.js +9 -1
  71. package/dist/sync-directories.d.ts +33 -0
  72. package/dist/sync-directories.js +97 -0
  73. package/dist/sync-plan.d.ts +16 -0
  74. package/dist/sync-plan.js +128 -0
  75. package/dist/sync.js +7 -13
  76. package/dist/task-command.d.ts +9 -0
  77. package/dist/task-command.js +29 -0
  78. package/dist/task.d.ts +9 -0
  79. package/dist/task.js +37 -0
  80. package/dist/terminal-text.d.ts +4 -0
  81. package/dist/terminal-text.js +72 -0
  82. package/dist/undo.d.ts +36 -0
  83. package/dist/undo.js +392 -0
  84. package/dist/warp.d.ts +3 -1
  85. package/dist/warp.js +32 -125
  86. package/dist/worktree-bootstrap.d.ts +35 -0
  87. package/dist/worktree-bootstrap.js +96 -0
  88. package/dist/worktree-info.d.ts +6 -1
  89. package/dist/worktree-info.js +14 -4
  90. package/dist/worktree-management.d.ts +1 -0
  91. package/dist/worktree-management.js +23 -4
  92. package/dist/worktree-picker.d.ts +24 -11
  93. package/dist/worktree-picker.js +176 -95
  94. package/dist/worktree-prompts.js +1 -1
  95. package/dist/worktree-source.d.ts +6 -0
  96. package/dist/worktree-source.js +1 -0
  97. package/dist/worktree-sources.d.ts +4 -0
  98. package/dist/worktree-sources.js +77 -0
  99. package/man/man1/gji-back.1 +1 -1
  100. package/man/man1/gji-clean.1 +1 -1
  101. package/man/man1/gji-completion.1 +1 -1
  102. package/man/man1/gji-config.1 +1 -1
  103. package/man/man1/gji-doctor.1 +1 -1
  104. package/man/man1/gji-done.1 +19 -0
  105. package/man/man1/gji-go.1 +12 -3
  106. package/man/man1/gji-history.1 +1 -1
  107. package/man/man1/gji-init.1 +1 -1
  108. package/man/man1/gji-ls.1 +1 -1
  109. package/man/man1/gji-new.1 +21 -3
  110. package/man/man1/gji-open.1 +1 -1
  111. package/man/man1/gji-pr.1 +7 -1
  112. package/man/man1/gji-remove.1 +3 -3
  113. package/man/man1/gji-root.1 +1 -1
  114. package/man/man1/gji-run-hook.1 +1 -1
  115. package/man/man1/gji-status.1 +1 -1
  116. package/man/man1/gji-sync-files.1 +1 -1
  117. package/man/man1/gji-sync.1 +1 -1
  118. package/man/man1/gji-task.1 +16 -0
  119. package/man/man1/gji-undo.1 +16 -0
  120. package/man/man1/gji-warp.1 +3 -6
  121. package/man/man1/gji.1 +17 -5
  122. package/package.json +3 -1
package/README.md CHANGED
@@ -1,487 +1,144 @@
1
- # gji — Git worktrees without the hassle
1
+ # gji — Switch Git contexts without stashing
2
2
 
3
- > Jump between tasks instantly. No stash. No branch juggling. No mess.
3
+ > Start, review, and switch between Git tasks without disturbing the work already open.
4
4
 
5
- `gji` wraps Git worktrees into a fast, ergonomic CLI. Each branch gets its own directory, its own `node_modules`, and its own terminal — so switching context is a single command instead of a ritual.
5
+ `gji` makes Git worktrees feel like normal shell navigation. Each task gets its
6
+ own directory, dependencies, editor context, and terminal — so moving to a
7
+ pull request or experiment does not require stashing, checking out, or
8
+ reinstalling.
6
9
 
7
- That matters even more in AI-assisted workflows, where one repository often has several active tasks in parallel: your main feature, a PR review, a scratch experiment, or an agent-driven refactor. `gji` keeps each one isolated and easy to enter.
10
+ ## Start your next task
8
11
 
9
- ```sh
10
- gji new feature/payment-refactor # new branch + worktree, cd in
11
- gji pr 1234 # review PR in isolation, cd in
12
- gji pr open # open the PR for the current worktree
13
- gji go main # jump back, shell changes directory
14
- gji remove feature/payment-refactor
15
- ```
16
-
17
- ## Before / After
18
-
19
- <table>
20
- <tr>
21
- <td width="50%" valign="top">
22
- <strong>Before</strong><br />
23
- <img src=".github/assets/readme-before.gif" alt="Traditional branch review flow with git stash, branch switching, reinstalling dependencies, and a merge conflict on stash pop." />
24
- </td>
25
- <td width="50%" valign="top">
26
- <strong>After</strong><br />
27
- <img src=".github/assets/readme-after.gif" alt="gji creating an isolated pull request worktree from the terminal in a few commands." />
28
- </td>
29
- </tr>
30
- </table>
31
-
32
- Maintainer note: `pnpm generate:readme-demos` currently expects macOS, `zsh`, Google Chrome, `asciinema`, and `ffmpeg`.
33
-
34
- ---
35
-
36
- **If `gji` has saved you from a `git stash` spiral, a ⭐ on [GitHub](https://github.com/sjquant/gji) means a lot — it helps other developers find this tool.**
37
-
38
- ---
39
-
40
- ## The problem
41
-
42
- You are deep in a feature branch. A colleague asks for a quick review. You:
43
-
44
- 1. stash your changes
45
- 2. checkout their branch
46
- 3. wait for `npm install` to finish
47
- 4. review
48
- 5. checkout back
49
- 6. pop your stash
50
- 7. realize something is broken
51
-
52
- **Or you use `gji`, run `gji pr 1234`, and let the fresh worktree boot itself.**
53
-
54
- ## Why it matters more now
55
-
56
- AI increases the amount of parallel work around a codebase.
57
-
58
- It is increasingly normal to have:
59
-
60
- 1. your own branch open
61
- 2. another branch for review
62
- 3. a scratch space for testing an AI-generated change
63
- 4. a separate worktree for validating a risky migration or refactor
64
-
65
- That makes Git worktrees more important, because a single shared checkout becomes the bottleneck. `gji` turns worktrees into a daily workflow instead of a Git power-user feature.
66
-
67
- ## Install
12
+ Run `gji new` and let the interactive flow guide you into a fresh worktree:
68
13
 
69
14
  ```sh
70
- npm install -g @solaqua/gji
15
+ gji new
71
16
  ```
72
17
 
73
- Then run the guided setup in an interactive terminal:
18
+ Choose a task name and setup options, then continue working in the new
19
+ directory. For repeatable workflows, you can still provide a branch directly:
74
20
 
75
21
  ```sh
76
- gji init
77
- # restart your shell, or source the rc file shown by the wizard
78
- gji doctor
22
+ gji new feature/payment-refactor
79
23
  ```
80
24
 
81
- The wizard installs shell integration, completions, and an available editor. In a non-interactive environment, use the existing explicit shell command instead:
25
+ ## The problem
82
26
 
83
- ```sh
84
- gji init zsh --write
27
+ You are halfway through a feature when someone asks you to review a pull
28
+ request. Without worktrees, a quick review often becomes:
29
+
30
+ ```text
31
+ stash → checkout → reinstall → review → checkout back → stash pop
85
32
  ```
86
33
 
87
- ## Quick start
34
+ With `gji`, the review gets its own workspace and your current task stays
35
+ untouched:
88
36
 
89
37
  ```sh
90
- # start a new task
91
- gji new feature/dark-mode
92
-
93
- # start a task and open it straight in your editor
94
- gji new feature/dark-mode --open --editor cursor
95
-
96
- # review a pull request
97
38
  gji pr 1234
98
- gji pr open # open the PR for the current worktree
99
- gji pr open --select # choose a PR from any linked worktree
100
- gji pr open feature/auth-refactor # open the PR for a branch
101
- gji pr open '#1234' # open an open PR directly
102
-
103
- # see what's open
104
- gji status
105
-
106
- # jump between worktrees
107
- gji go feature/dark-mode
108
- gji go main
109
-
110
- # open the current worktree in an editor
111
- gji open
112
- gji open --select # choose another worktree interactively
113
- gji open feature/dark-mode --editor code
114
-
115
- # clean up when done
116
- gji remove feature/dark-mode
117
- ```
118
-
119
- Worktrees land at a deterministic path so your editor bookmarks and scripts always know where to look:
120
-
121
- ```
122
- ../worktrees/<repo>/<branch>
123
39
  ```
124
40
 
125
- Set `worktreePath` in your config to use a different base (e.g. `"~/worktrees"` → `~/worktrees/<branch>`).
41
+ <table>
42
+ <tr>
43
+ <td width="50%" valign="top">
44
+ <strong>Before</strong><br />
45
+ <img src=".github/assets/readme-before.gif" alt="Traditional branch switching with stash and reinstall steps" />
46
+ </td>
47
+ <td width="50%" valign="top">
48
+ <strong>After</strong><br />
49
+ <img src=".github/assets/readme-after.gif" alt="Creating an isolated pull request worktree with gji" />
50
+ </td>
51
+ </tr>
52
+ </table>
126
53
 
127
- ## Daily workflow
54
+ ## Remember four commands
128
55
 
129
56
  ```sh
130
- gji new feature/auth-refactor # new branch + worktree
131
- gji new feature/auth-tests --from-current # branch from the current worktree
132
- gji new --detached # scratch space, auto-named
133
-
134
- gji pr 1234 # checkout PR locally
135
- gji pr https://github.com/org/repo/pull/1234 # or paste the URL
136
-
137
- gji go feature/auth-refactor # jump to a worktree
138
- gji root # jump to repo root
139
-
140
- gji status # health overview + ahead/behind counts
141
- gji ls # list with status/upstream/last commit
142
- gji ls --compact # branch/path only
143
-
144
- gji sync # rebase current worktree onto default branch
145
- gji sync --all # rebase every worktree
146
-
147
- gji clean # interactive bulk cleanup
148
- gji clean --stale # only target safe stale cleanup candidates
149
- gji remove feature/auth-refactor # remove one worktree and its branch
150
-
151
- gji trigger-hook afterCreate # re-run setup in the current worktree
57
+ gji new # interactively start a task
58
+ gji pr 1234 # open a pull request in isolation
59
+ gji go main # jump to another worktree
60
+ gji done feature/payment-refactor # finish and clean up a task
152
61
  ```
153
62
 
154
- ## Comparison
155
-
156
- `gji` sits between raw Git primitives and larger Git or repository tools:
157
-
158
- - **vs raw `git worktree`**: same underlying capability, but with branch-first commands, shell handoff, PR checkout, hooks, sync, and cleanup built into the workflow
159
- - **vs `lazygit`**: `lazygit` is a broad Git UI; `gji` is narrower and faster for opening, jumping between, and removing isolated branch directories
160
- - **vs `ghq`**: `ghq` organizes where repositories live; `gji` organizes which branch, PR, or worktree you should be in once you are inside one
161
-
162
- Use `gji` when your bottleneck is repeated context switching between features, reviews, and maintenance work without disturbing what is already open.
163
-
164
- It is especially useful when those contexts are happening in parallel across both human and AI-assisted work.
165
-
166
- See the full comparison in [website/docs/comparison.mdx](./website/docs/comparison.mdx).
167
-
168
- ## Shell setup
63
+ With shell integration enabled, each command moves your shell into the
64
+ selected worktree automatically.
169
65
 
170
- Without shell integration `gji` prints paths and exits — which is fine for scripts but means it cannot `cd` you into a new worktree. Install the integration, completions, and an editor once with:
66
+ ## Install
171
67
 
172
68
  ```sh
69
+ npm install -g @solaqua/gji
173
70
  gji init
174
- gji doctor
175
- ```
176
-
177
- `gji init` is interactive. For dotfiles or CI, the explicit shell commands remain available and preserve their existing output:
178
-
179
- ```sh
180
- # zsh
181
- echo 'eval "$(gji init zsh)"' >> ~/.zshrc
182
-
183
- # bash
184
- echo 'eval "$(gji init bash)"' >> ~/.bashrc
185
-
186
- # fish
187
- gji init fish --write
188
- ```
189
-
190
- Install completions separately so your shell rc stays small:
191
-
192
- ```sh
193
- # zsh
194
- mkdir -p ~/.zsh/completions
195
- gji completion zsh > ~/.zsh/completions/_gji
196
- # add this before running compinit in ~/.zshrc
197
- fpath=(~/.zsh/completions $fpath)
198
-
199
- # bash
200
- mkdir -p ~/.local/share/bash-completion/completions
201
- gji completion bash > ~/.local/share/bash-completion/completions/gji
202
-
203
- # fish
204
- mkdir -p ~/.config/fish/completions
205
- gji completion fish > ~/.config/fish/completions/gji.fish
206
- ```
207
-
208
- After a reinstall or upgrade, refresh both the wrapper and the completion file:
209
-
210
- ```sh
211
- # zsh
212
- eval "$(gji init zsh)"
213
- gji completion zsh > ~/.zsh/completions/_gji
214
- # if zsh is already running, refresh completion discovery too
215
- autoload -Uz compinit && compinit
216
-
217
- # fish
218
- gji init fish --write
219
- gji completion fish > ~/.config/fish/completions/gji.fish
220
- source ~/.config/fish/config.fish
221
- ```
222
-
223
- For scripts that need the raw path, use `--print`:
224
-
225
- ```sh
226
- path=$(gji go --print feature/dark-mode)
227
- path=$(gji root --print)
228
- ```
229
-
230
- ## Commands
231
-
232
- | Command | Description |
233
- |---|---|
234
- | `gji new [branch] [--from-current] [--detached] [--open] [--editor <cli>] [--json]` | create branch + worktree, optionally based on the current worktree |
235
- | `gji pr <ref> [--json]` | fetch PR ref, create worktree, cd in |
236
- | `gji pr open [branch|#N] [--select]` | open the current worktree PR, or choose a linked worktree with `--select` |
237
- | `gji open [branch] [--select] [--editor <cli>] [--save] [--workspace]` | open the current or selected worktree in an editor |
238
- | `gji go [branch] [--print]` | jump to a worktree |
239
- | `gji root [--print]` | jump to the main repo root |
240
- | `gji status [--json]` | repo overview, worktree health, ahead/behind |
241
- | `gji ls [--compact] [--json]` | list active worktrees |
242
- | `gji sync [--all]` | fetch and rebase worktrees onto default branch |
243
- | `gji sync-files [list\|add\|remove] [paths...]` | manage local files copied into new worktrees |
244
- | `gji clean [--stale] [--force] [--json]` | interactively prune linked worktrees |
245
- | `gji remove [branch] [--force] [--json]` | remove a worktree and its branch |
246
- | `gji trigger-hook <hook>` | run a hook in the current worktree |
247
- | `gji config [get\|set\|unset] [key] [value]` | manage global defaults |
248
- | `gji init [shell]` | interactively set up onboarding, or print/install a shell wrapper |
249
- | `gji doctor [--json] [--fix] [--yes]` | check installation and configuration health; optionally remove stale repository entries |
250
- | `gji completion [shell]` | print shell completion definitions |
251
-
252
- ## Configuration
253
-
254
- No setup required. Optional config lives in:
255
-
256
- - `~/.config/gji/config.json` — global defaults
257
- - `.gji.json` — repo-local overrides (takes precedence)
258
-
259
- ### Available keys
260
-
261
- | Key | Description |
262
- |---|---|
263
- | `branchPrefix` | prefix added to new branch names (e.g. `"feature/"`) |
264
- | `editor` | default editor CLI for `gji open` and `gji new --open` (e.g. `"cursor"`, `"code"`, `"zed"`); set automatically with `gji open --save` |
265
- | `worktreePath` | base directory for new worktrees (absolute or `~/…`); overrides the default `../worktrees/<repo>/` layout |
266
- | `syncRemote` | remote for `gji sync` (default: `origin`) |
267
- | `syncDefaultBranch` | branch to rebase onto (default: remote `HEAD`) |
268
- | `syncFiles` | files to copy from main worktree into each new worktree; use global per-repo config for private files |
269
- | `skipInstallPrompt` | `true` to disable the auto-install prompt permanently |
270
- | `installSaveTarget` | `"local"` or `"global"` — where **Always**/**Never** choices are persisted (default: `"local"`); set during `gji init <shell> --write` |
271
- | `hooks` | lifecycle scripts (see [Hooks](#hooks)) |
272
- | `repos` | per-repo overrides inside the global config (see below) |
273
-
274
- ```json
275
- {
276
- "branchPrefix": "feature/",
277
- "syncRemote": "upstream",
278
- "syncDefaultBranch": "main",
279
- "syncFiles": [".env.example", ".nvmrc"]
280
- }
281
71
  ```
282
72
 
283
- ### Syncing local files
284
-
285
- Use `syncFiles` for private, gitignored, or machine-local files that every new worktree needs, such as `.env.local` or `.npmrc`. `gji new` copies these files from the main worktree before install hooks run, skips missing source files, and does not overwrite files that already exist in the target worktree.
286
-
287
- For private files, prefer the `sync-files` command. It writes to your global per-repo config so secret filenames do not need to be committed to `.gji.json`:
73
+ `gji init` interactively installs shell integration, completions, and an
74
+ available editor. Restart your shell, or source the rc file shown by the
75
+ wizard, then run:
288
76
 
289
77
  ```sh
290
- gji sync-files add .env.local .npmrc
291
- gji sync-files list
292
- gji sync-files remove .npmrc
293
- ```
294
-
295
- This stores:
296
-
297
- ```json
298
- {
299
- "repos": {
300
- "/home/me/code/my-repo": {
301
- "syncFiles": [".env.local"]
302
- }
303
- }
304
- }
305
- ```
306
-
307
- ### Per-repo overrides in global config
308
-
309
- If you work across many repositories, you can scope config to a specific repo inside `~/.config/gji/config.json` without adding a `.gji.json` to that repo:
310
-
311
- ```json
312
- {
313
- "branchPrefix": "feature/",
314
- "repos": {
315
- "/home/me/code/my-repo": {
316
- "branchPrefix": "fix/",
317
- "hooks": {
318
- "afterCreate": "npm install"
319
- }
320
- }
321
- }
322
- }
78
+ gji doctor
323
79
  ```
324
80
 
325
- Precedence (lowest highest): **global defaults per-repo global local `.gji.json`**. Hooks from all three layers are merged per key — different keys all apply, same key the higher-precedence layer wins.
81
+ Without shell integration, commands still print paths for scripts and other
82
+ non-interactive use. See the [installation guide](https://gji.solaqua.dev/docs/installation)
83
+ for explicit shell setup.
326
84
 
327
- ### Config commands
85
+ ## A typical day
328
86
 
329
87
  ```sh
330
- gji config get
331
- gji config get branchPrefix
332
- gji config set branchPrefix feature/
333
- gji config unset branchPrefix
334
- ```
335
-
336
- ## Hooks
337
-
338
- Run scripts automatically at key lifecycle moments:
339
-
340
- ```json
341
- {
342
- "hooks": {
343
- "afterCreate": ["pnpm", "install"],
344
- "afterEnter": ["printf", "switched to %s\n", "{{branch}}"],
345
- "beforeRemove": "pnpm run cleanup"
346
- }
347
- }
348
- ```
88
+ # Start feature work
89
+ gji new
349
90
 
350
- | Hook | When it runs |
351
- |---|---|
352
- | `afterCreate` | after `gji new` or `gji pr` creates a worktree |
353
- | `afterEnter` | after `gji go` switches to a worktree |
354
- | `beforeRemove` | before `gji remove` deletes a worktree |
91
+ # Check what is active
92
+ gji
355
93
 
356
- Hooks receive `{{branch}}`, `{{path}}`, `{{repo}}` as template variables and `GJI_BRANCH`, `GJI_PATH`, `GJI_REPO` as environment variables. A failing hook emits a warning but never aborts the command.
94
+ # Review a PR without changing the current task
95
+ gji pr 1234
357
96
 
358
- Prefer argv-array hooks for simple commands:
97
+ # Move between tasks
98
+ gji go feature/payment-refactor
99
+ gji back
359
100
 
360
- ```json
361
- {
362
- "hooks": {
363
- "afterCreate": ["pnpm", "install"],
364
- "afterEnter": ["printf", "switched to %s at %s\n", "{{branch}}", "{{path}}"]
365
- }
366
- }
101
+ # Keep worktrees current and remove finished work
102
+ gji sync
103
+ gji done feature/payment-refactor
367
104
  ```
368
105
 
369
- Array hooks run without a shell and pass each array item as exactly one argument. Use string hooks only when you need shell features like `&&`, pipes, redirects, shell functions, or `nvm use`.
370
-
371
- Template values are interpolated before the shell parses string hooks, so avoid putting `{{branch}}`, `{{path}}`, or `{{repo}}` directly into shell strings. For shell-string hooks, the safer pattern is to use the environment variables and double-quote each expansion:
106
+ Worktrees use a predictable location by default:
372
107
 
373
- ```json
374
- {
375
- "hooks": {
376
- "afterCreate": "pnpm install && printf 'ready: %s\n' \"$GJI_PATH\""
377
- }
378
- }
108
+ ```text
109
+ ../worktrees/<repo>/<branch>
379
110
  ```
380
111
 
381
- Avoid unquoted template values in shell strings, such as `echo {{branch}}` or `cd {{path}}`.
112
+ That keeps editor bookmarks, scripts, and terminal navigation stable.
382
113
 
383
- Hooks from all three config layers merge per key — different keys from different layers both apply, same key the higher-precedence layer wins:
114
+ ## More capabilities
384
115
 
385
- ```jsonc
386
- // ~/.config/gji/config.json
387
- { "hooks": { "afterCreate": "nvm use", "afterEnter": "echo hi" } }
116
+ Use the documentation when you need to go beyond the core workflow:
388
117
 
389
- // per-repo entry in ~/.config/gji/config.json
390
- { "repos": { "/my/repo": { "hooks": { "afterCreate": "npm install" } } } }
118
+ - [Daily workflow](https://gji.solaqua.dev/docs/daily-workflow) navigation, PRs, sync, and cleanup
119
+ - [Commands](https://gji.solaqua.dev/docs/commands) complete command reference
120
+ - [Shell integration](https://gji.solaqua.dev/docs/shell-integration) — zsh, bash, and fish setup
121
+ - [Configuration](https://gji.solaqua.dev/docs/configuration) — paths, defaults, and local files
122
+ - [Hooks](https://gji.solaqua.dev/docs/hooks) — automate setup and cleanup
123
+ - [Sync and cleanup](https://gji.solaqua.dev/docs/sync-and-cleanup) — maintain active worktrees
124
+ - [Comparison](https://gji.solaqua.dev/docs/comparison) — `gji` vs `git worktree`, `lazygit`, and `ghq`
125
+ - [FAQ and troubleshooting](https://gji.solaqua.dev/docs/faq) — common questions and fixes
391
126
 
392
- // .gji.json
393
- { "hooks": { "beforeRemove": "echo bye" } }
394
-
395
- // effective
396
- { "hooks": { "afterCreate": "npm install", "afterEnter": "echo hi", "beforeRemove": "echo bye" } }
397
- ```
398
-
399
- ### Triggering hooks manually
400
-
401
- Run any hook in the current worktree on demand:
127
+ `gji` also supports JSON output for scripts and AI-assisted workflows:
402
128
 
403
129
  ```sh
404
- gji trigger-hook afterCreate # re-run the setup script
405
- gji trigger-hook afterEnter # re-run the enter script
406
- gji trigger-hook beforeRemove # dry-run the cleanup script
407
- ```
408
-
409
- This is useful after cloning on a new machine, recovering a broken worktree, or letting an AI agent bootstrap an already-existing worktree without needing interactive prompts.
410
-
411
- ## Install prompt
412
-
413
- When `gji new` or `gji pr` creates a worktree, `gji` detects the project's package manager from its lockfile and offers to run the install command:
414
-
415
- ```
416
- Run `pnpm install` in the new worktree?
417
- › Yes run once
418
- No skip this time
419
- Always save as afterCreate hook
420
- Never disable this prompt for this repo
421
- ```
422
-
423
- **Always** saves `hooks.afterCreate`; **Never** writes `skipInstallPrompt: true`. Where they are saved depends on `installSaveTarget` (see [Available keys](#available-keys)) — defaults to `.gji.json`.
424
-
425
- ## JSON output
426
-
427
- Every mutating command supports `--json` for scripting and AI agent use. Success goes to stdout, errors go to stderr with exit code 1.
428
-
429
- ```sh
430
- # create
130
+ gji --json
431
131
  gji new --json feature/dark-mode
432
- # { "branch": "feature/dark-mode", "path": "/…/worktrees/repo/feature/dark-mode" }
433
-
434
- # fetch PR
435
- gji pr --json 1234
436
- # → { "branch": "pr/1234", "path": "/…/worktrees/repo/pr/1234" }
437
-
438
- # detailed list
439
- gji ls --json
440
- # → [{ "branch": "...", "status": "clean", "upstream": { "kind": "tracked", ... }, ... }]
441
-
442
- # remove
443
- gji remove --json --force feature/dark-mode
444
- # → { "branch": "feature/dark-mode", "path": "/…", "deleted": true }
445
-
446
- # bulk clean
447
- gji clean --json --force
448
- # → { "removed": [{ "branch": "...", "path": "..." }, …] }
449
-
450
- # stale-only clean
451
- gji clean --stale --json --force
452
- # → { "removed": [{ "branch": "...", "path": "..." }, …] }
453
-
454
- # error shape (any command)
455
- # stderr → { "error": "branch argument is required" }
132
+ gji go --json feature/dark-mode
456
133
  ```
457
134
 
458
- `gji clean --stale` limits cleanup to clean branch worktrees whose upstream is gone and whose branch is already merged into the configured or remote default branch.
459
-
460
- `--json` suppresses all interactive prompts. `--force` is required for `remove` and `clean` in JSON mode. `branch` is `null` for detached worktrees.
461
-
462
- `gji ls --json` and `gji status --json` also produce structured output — see `gji status --json | jq` for the full schema.
135
+ Read the [full documentation](https://gji.solaqua.dev) for configuration,
136
+ dependency bootstrap, hooks, automation, and the complete CLI reference.
463
137
 
464
- ## Non-interactive / CI mode
465
-
466
- ```sh
467
- GJI_NO_TUI=1 gji new feature/ci-branch
468
- GJI_NO_TUI=1 gji remove --force feature/ci-branch
469
- GJI_NO_TUI=1 gji clean --force
470
- ```
471
-
472
- `GJI_NO_TUI=1` disables all prompts. Commands that need confirmation require their non-interactive approval flag (`--force` for cleanup commands, `--yes` for `doctor --fix`). `--json` implies the same behaviour.
473
-
474
- `gji pr open --select` requires an interactive terminal; plain `gji pr open` opens the PR for the current worktree without prompting.
475
- If the current worktree has no open PR or has multiple open PRs in headless mode, pass `gji pr open <branch|#N>` explicitly.
476
-
477
- Update notifications are also suppressed automatically in non-interactive and `--json` runs. Users can opt out explicitly with `NO_UPDATE_NOTIFIER=1` or `--no-update-notifier`.
478
-
479
- ## Notes
138
+ ---
480
139
 
481
- - Works from either the main repo root or inside any linked worktree
482
- - The current worktree is never offered as a `gji clean` candidate
483
- - `gji pr` fetches from `origin` using the first matching forge ref namespace: GitHub `refs/pull/<number>/head`, GitLab `refs/merge-requests/<number>/head`, then Bitbucket `refs/pull-requests/<number>/from`
484
- - `gji pr open` reads open PRs from the `origin` forge (GitHub, GitLab, or Bitbucket), preferring an installed authenticated provider CLI and falling back to its public API
140
+ If `gji` has saved you from a `git stash` spiral, a on
141
+ [GitHub](https://github.com/sjquant/gji) helps other developers find it.
485
142
 
486
143
  ## License
487
144
 
package/dist/back.d.ts CHANGED
@@ -3,7 +3,10 @@ export declare const BACK_OUTPUT_FILE_ENV = "GJI_BACK_OUTPUT_FILE";
3
3
  export interface BackCommandOptions {
4
4
  cwd: string;
5
5
  home?: string;
6
+ json?: boolean;
6
7
  n?: number;
8
+ commandName?: string;
9
+ outputEnv?: string;
7
10
  print?: boolean;
8
11
  stderr: (chunk: string) => void;
9
12
  stdout: (chunk: string) => void;
package/dist/back.js CHANGED
@@ -3,6 +3,7 @@ import { basename } from "node:path";
3
3
  import { loadEffectiveConfig } from "./config.js";
4
4
  import { appendHistory, loadHistory } from "./history.js";
5
5
  import { extractHooks, runHook } from "./hooks.js";
6
+ import { createNavigationRepository, createNavigationTarget, } from "./navigation-output.js";
6
7
  import { detectRepository } from "./repo.js";
7
8
  import { writeShellOutput } from "./shell-handoff.js";
8
9
  export const BACK_OUTPUT_FILE_ENV = "GJI_BACK_OUTPUT_FILE";
@@ -31,10 +32,23 @@ export async function runBackCommand(options) {
31
32
  }
32
33
  }
33
34
  if (!target) {
34
- options.stderr("gji back: no previous worktree in history\n");
35
- options.stderr("Hint: Use 'gji go', 'gji new', or 'gji pr' to navigate between worktrees\n");
35
+ const commandName = options.commandName ?? "gji back";
36
+ if (options.json) {
37
+ options.stderr(`${JSON.stringify({ error: "no previous worktree in history" }, null, 2)}\n`);
38
+ }
39
+ else {
40
+ options.stderr(`${commandName}: no previous worktree in history\n`);
41
+ }
42
+ if (!options.json) {
43
+ options.stderr("Hint: Use 'gji go', 'gji new', or 'gji pr' to navigate between worktrees\n");
44
+ }
36
45
  return 1;
37
46
  }
47
+ if (options.json) {
48
+ const repository = await detectRepository(target.path);
49
+ options.stdout(`${JSON.stringify(createNavigationTarget(createNavigationRepository(repository.repoName, repository.repoRoot), target.path, target.branch), null, 2)}\n`);
50
+ return 0;
51
+ }
38
52
  try {
39
53
  const repository = await detectRepository(target.path);
40
54
  const config = await loadEffectiveConfig(repository.repoRoot, options.home, options.stderr);
@@ -49,21 +63,19 @@ export async function runBackCommand(options) {
49
63
  // Not in a git repo or hooks unavailable — proceed without hook
50
64
  }
51
65
  await appendHistory(target.path, target.branch, options.home);
52
- await writeShellOutput(BACK_OUTPUT_FILE_ENV, target.path, options.stdout);
66
+ await writeShellOutput(options.outputEnv ?? BACK_OUTPUT_FILE_ENV, target.path, options.stdout);
53
67
  return 0;
54
68
  }
55
69
  export function formatHistoryList(history, cwd) {
56
70
  const branchWidth = Math.max("BRANCH".length, ...history.map((e) => (e.branch ?? "(detached)").length));
57
- const lines = [
58
- " " + "BRANCH".padEnd(branchWidth) + " WHEN PATH",
59
- ];
71
+ const lines = [` ${"BRANCH".padEnd(branchWidth)} WHEN PATH`];
60
72
  for (const entry of history) {
61
73
  const isCurrent = entry.path === cwd;
62
74
  const branch = (entry.branch ?? "(detached)").padEnd(branchWidth);
63
75
  const when = formatAge(entry.timestamp).padEnd(10);
64
76
  lines.push(`${isCurrent ? "*" : " "} ${branch} ${when} ${entry.path}`);
65
77
  }
66
- return lines.join("\n") + "\n";
78
+ return `${lines.join("\n")}\n`;
67
79
  }
68
80
  export function formatAge(timestamp) {
69
81
  const seconds = Math.floor((Date.now() - timestamp) / 1000);
@@ -0,0 +1,3 @@
1
+ import type { DependencyBootstrapReporter } from "./dependency-bootstrap.js";
2
+ import type { SyncDirectoryReporter } from "./sync-directories.js";
3
+ export declare function createBootstrapReporter(write: (chunk: string) => void, json: boolean, measureCloneSize?: boolean): SyncDirectoryReporter & DependencyBootstrapReporter;
@@ -0,0 +1,27 @@
1
+ import { formatBytes } from "./format-bytes.js";
2
+ export function createBootstrapReporter(write, json, measureCloneSize = false) {
3
+ return {
4
+ emitCachedFailureWarnings: !json,
5
+ measureCloneSize: measureCloneSize && !json,
6
+ write,
7
+ cloned: (directory) => {
8
+ if (json)
9
+ return;
10
+ write(`⚡ cloned ${directory.dir} (${formatBytes(directory.bytes)} → ${formatDuration(directory.ms)})\n`);
11
+ },
12
+ skipped: (directory) => {
13
+ if (json)
14
+ return;
15
+ write(`gji: skipped ${directory.dir} — ${directory.reason}\n`);
16
+ },
17
+ dependency: (event) => {
18
+ if (json)
19
+ return;
20
+ const target = event.target ? ` ${event.target}` : "";
21
+ write(`gji: ${event.state}${target} — ${event.message}\n`);
22
+ },
23
+ };
24
+ }
25
+ function formatDuration(ms) {
26
+ return `${(ms / 1000).toFixed(1)}s`;
27
+ }