@solaqua/gji 0.8.0 → 0.8.1

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.
package/dist/clean.js CHANGED
@@ -3,7 +3,7 @@ import { loadEffectiveConfig } from "./config.js";
3
3
  import { isBranchMergedInto, readWorktreeHealth, resolveRemoteDefaultBranch, runGit, } from "./git.js";
4
4
  import { isHeadless } from "./headless.js";
5
5
  import { formatLastCommit, formatUpstreamState, readWorktreeInfos, serializeWorktreeInfo, } from "./worktree-info.js";
6
- import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeDirtyError, loadLinkedWorktrees, removeWorktree, } from "./worktree-management.js";
6
+ import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeDeletionError, isWorktreeForceRemovalError, loadLinkedWorktrees, removeWorktree, } from "./worktree-management.js";
7
7
  import { buildWorktreePromptEntries, promptForMultipleWorktrees, } from "./worktree-picker.js";
8
8
  import { defaultConfirmForceDeleteBranch, defaultConfirmForceRemoveWorktree, } from "./worktree-prompts.js";
9
9
  export function createCleanCommand(dependencies = {}) {
@@ -77,8 +77,8 @@ export function createCleanCommand(dependencies = {}) {
77
77
  }
78
78
  return 0;
79
79
  }
80
- const removedPaths = [];
81
80
  const removedWorktrees = [];
81
+ const failures = [];
82
82
  for (const worktree of selectedWorktrees) {
83
83
  if (options.stale &&
84
84
  !(await isStaleCleanupCandidate(repository.repoRoot, worktree, staleBaseRef))) {
@@ -89,31 +89,39 @@ export function createCleanCommand(dependencies = {}) {
89
89
  await removeWorktree(repository.repoRoot, worktree.path);
90
90
  }
91
91
  catch (error) {
92
- if (!isWorktreeDirtyError(error)) {
93
- throw error;
92
+ if (!isWorktreeForceRemovalError(error)) {
93
+ failures.push({
94
+ branch: worktree.branch,
95
+ message: toMessage(error),
96
+ path: worktree.path,
97
+ });
98
+ continue;
94
99
  }
95
- if (options.stale) {
100
+ if (options.stale && !isWorktreeDeletionError(error)) {
96
101
  options.stderr(`Skipped ${worktree.path}: no longer a safe stale cleanup candidate\n`);
97
102
  continue;
98
103
  }
99
104
  if (!options.force &&
100
105
  !(await confirmForceRemoveWorktree(worktree.path))) {
101
- reportRemovedPaths(removedPaths, options.stderr);
102
- options.stderr("Aborted\n");
103
- return 1;
106
+ failures.push({
107
+ branch: worktree.branch,
108
+ message: "force removal declined",
109
+ path: worktree.path,
110
+ });
111
+ continue;
104
112
  }
105
113
  try {
106
114
  await forceRemoveWorktree(repository.repoRoot, worktree.path);
107
115
  }
108
116
  catch (forceError) {
109
- if (!options.json) {
110
- reportRemovedPaths(removedPaths, options.stderr);
111
- }
112
- emitError(options, `Failed to remove worktree at ${worktree.path}: ${toMessage(forceError)}`);
113
- return 1;
117
+ failures.push({
118
+ branch: worktree.branch,
119
+ message: toMessage(forceError),
120
+ path: worktree.path,
121
+ });
122
+ continue;
114
123
  }
115
124
  }
116
- removedPaths.push(worktree.path);
117
125
  removedWorktrees.push(worktree);
118
126
  if (worktree.branch) {
119
127
  try {
@@ -145,12 +153,16 @@ export function createCleanCommand(dependencies = {}) {
145
153
  ? { branch: worktree.branch, path: worktree.path }
146
154
  : serializeWorktreeInfo(info);
147
155
  });
148
- options.stdout(`${JSON.stringify({ removed }, null, 2)}\n`);
156
+ const payload = failures.length === 0 ? { removed } : { removed, failed: failures };
157
+ options.stdout(`${JSON.stringify(payload, null, 2)}\n`);
158
+ }
159
+ else if (failures.length > 0) {
160
+ reportCleanFailures(failures, options.stderr);
149
161
  }
150
162
  else {
151
163
  options.stdout(`${repository.repoRoot}\n`);
152
164
  }
153
- return 0;
165
+ return failures.length === 0 ? 0 : 1;
154
166
  };
155
167
  }
156
168
  export const runCleanCommand = createCleanCommand();
@@ -216,9 +228,12 @@ function resolveSelectedWorktrees(worktrees, selections) {
216
228
  }
217
229
  return selectedWorktrees;
218
230
  }
219
- function reportRemovedPaths(paths, stderr) {
220
- if (paths.length > 0) {
221
- stderr(`Already removed: ${paths.join(", ")}\n`);
231
+ function reportCleanFailures(failures, stderr) {
232
+ const noun = failures.length === 1 ? "worktree" : "worktrees";
233
+ stderr(`Failed to clean ${failures.length} ${noun}:\n`);
234
+ for (const failure of failures) {
235
+ const branch = failure.branch === null ? "detached" : failure.branch;
236
+ stderr(`- ${failure.path} (${branch}): ${failure.message}\n`);
222
237
  }
223
238
  }
224
239
  function formatCleanInfo(info) {
@@ -14127,6 +14127,7 @@ function formatRelativeAge(timestampSeconds) {
14127
14127
 
14128
14128
  // src/worktree-management.ts
14129
14129
  import { execFile as execFile2 } from "node:child_process";
14130
+ import { rm } from "node:fs/promises";
14130
14131
  import { promisify as promisify3 } from "node:util";
14131
14132
  var execFileAsync2 = promisify3(execFile2);
14132
14133
  var GIT_ENV = { ...process.env, LC_ALL: "C" };
@@ -14147,10 +14148,24 @@ async function removeWorktree(repoRoot, worktreePath) {
14147
14148
  });
14148
14149
  }
14149
14150
  async function forceRemoveWorktree(repoRoot, worktreePath) {
14150
- await execFileAsync2("git", ["worktree", "remove", "--force", worktreePath], {
14151
- cwd: repoRoot,
14152
- env: GIT_ENV
14153
- });
14151
+ try {
14152
+ await execFileAsync2(
14153
+ "git",
14154
+ ["worktree", "remove", "--force", worktreePath],
14155
+ {
14156
+ cwd: repoRoot,
14157
+ env: GIT_ENV
14158
+ }
14159
+ );
14160
+ } catch (error) {
14161
+ const worktreeIsStillRegistered = (await listWorktrees(repoRoot)).some(
14162
+ (worktree) => worktree.path === worktreePath
14163
+ );
14164
+ if (worktreeIsStillRegistered) {
14165
+ throw error;
14166
+ }
14167
+ await rm(worktreePath, { force: true, recursive: true });
14168
+ }
14154
14169
  }
14155
14170
  async function deleteBranch(repoRoot, branch) {
14156
14171
  await execFileAsync2("git", ["branch", "-d", branch], {
@@ -14164,8 +14179,14 @@ async function forceDeleteBranch(repoRoot, branch) {
14164
14179
  env: GIT_ENV
14165
14180
  });
14166
14181
  }
14167
- function isWorktreeDirtyError(error) {
14168
- return hasStderr(error) && error.stderr.includes("contains modified or untracked files");
14182
+ function isWorktreeForceRemovalError(error) {
14183
+ if (!hasStderr(error)) {
14184
+ return false;
14185
+ }
14186
+ return error.stderr.includes("contains modified or untracked files") || isWorktreeDeletionError(error);
14187
+ }
14188
+ function isWorktreeDeletionError(error) {
14189
+ return hasStderr(error) && error.stderr.includes("failed to delete");
14169
14190
  }
14170
14191
  function isBranchUnmergedError(error) {
14171
14192
  return hasStderr(error) && error.stderr.includes("is not fully merged");
@@ -15156,8 +15177,8 @@ function createCleanCommand(dependencies = {}) {
15156
15177
  }
15157
15178
  return 0;
15158
15179
  }
15159
- const removedPaths = [];
15160
15180
  const removedWorktrees = [];
15181
+ const failures = [];
15161
15182
  for (const worktree of selectedWorktrees) {
15162
15183
  if (options.stale && !await isStaleCleanupCandidate(
15163
15184
  repository.repoRoot,
@@ -15173,10 +15194,15 @@ function createCleanCommand(dependencies = {}) {
15173
15194
  try {
15174
15195
  await removeWorktree(repository.repoRoot, worktree.path);
15175
15196
  } catch (error) {
15176
- if (!isWorktreeDirtyError(error)) {
15177
- throw error;
15197
+ if (!isWorktreeForceRemovalError(error)) {
15198
+ failures.push({
15199
+ branch: worktree.branch,
15200
+ message: toMessage(error),
15201
+ path: worktree.path
15202
+ });
15203
+ continue;
15178
15204
  }
15179
- if (options.stale) {
15205
+ if (options.stale && !isWorktreeDeletionError(error)) {
15180
15206
  options.stderr(
15181
15207
  `Skipped ${worktree.path}: no longer a safe stale cleanup candidate
15182
15208
  `
@@ -15184,24 +15210,24 @@ function createCleanCommand(dependencies = {}) {
15184
15210
  continue;
15185
15211
  }
15186
15212
  if (!options.force && !await confirmForceRemoveWorktree(worktree.path)) {
15187
- reportRemovedPaths(removedPaths, options.stderr);
15188
- options.stderr("Aborted\n");
15189
- return 1;
15213
+ failures.push({
15214
+ branch: worktree.branch,
15215
+ message: "force removal declined",
15216
+ path: worktree.path
15217
+ });
15218
+ continue;
15190
15219
  }
15191
15220
  try {
15192
15221
  await forceRemoveWorktree(repository.repoRoot, worktree.path);
15193
15222
  } catch (forceError) {
15194
- if (!options.json) {
15195
- reportRemovedPaths(removedPaths, options.stderr);
15196
- }
15197
- emitError(
15198
- options,
15199
- `Failed to remove worktree at ${worktree.path}: ${toMessage(forceError)}`
15200
- );
15201
- return 1;
15223
+ failures.push({
15224
+ branch: worktree.branch,
15225
+ message: toMessage(forceError),
15226
+ path: worktree.path
15227
+ });
15228
+ continue;
15202
15229
  }
15203
15230
  }
15204
- removedPaths.push(worktree.path);
15205
15231
  removedWorktrees.push(worktree);
15206
15232
  if (worktree.branch) {
15207
15233
  try {
@@ -15233,13 +15259,16 @@ function createCleanCommand(dependencies = {}) {
15233
15259
  const info = selectedInfoByPath.get(worktree.path);
15234
15260
  return info === void 0 ? { branch: worktree.branch, path: worktree.path } : serializeWorktreeInfo(info);
15235
15261
  });
15236
- options.stdout(`${JSON.stringify({ removed }, null, 2)}
15262
+ const payload = failures.length === 0 ? { removed } : { removed, failed: failures };
15263
+ options.stdout(`${JSON.stringify(payload, null, 2)}
15237
15264
  `);
15265
+ } else if (failures.length > 0) {
15266
+ reportCleanFailures(failures, options.stderr);
15238
15267
  } else {
15239
15268
  options.stdout(`${repository.repoRoot}
15240
15269
  `);
15241
15270
  }
15242
- return 0;
15271
+ return failures.length === 0 ? 0 : 1;
15243
15272
  };
15244
15273
  }
15245
15274
  var runCleanCommand = createCleanCommand();
@@ -15316,9 +15345,13 @@ function resolveSelectedWorktrees(worktrees, selections) {
15316
15345
  }
15317
15346
  return selectedWorktrees;
15318
15347
  }
15319
- function reportRemovedPaths(paths, stderr) {
15320
- if (paths.length > 0) {
15321
- stderr(`Already removed: ${paths.join(", ")}
15348
+ function reportCleanFailures(failures, stderr) {
15349
+ const noun = failures.length === 1 ? "worktree" : "worktrees";
15350
+ stderr(`Failed to clean ${failures.length} ${noun}:
15351
+ `);
15352
+ for (const failure of failures) {
15353
+ const branch = failure.branch === null ? "detached" : failure.branch;
15354
+ stderr(`- ${failure.path} (${branch}): ${failure.message}
15322
15355
  `);
15323
15356
  }
15324
15357
  }
@@ -17918,7 +17951,7 @@ function createRemoveCommand(dependencies = {}) {
17918
17951
  try {
17919
17952
  await removeWorktree(repository.repoRoot, worktree.path);
17920
17953
  } catch (error) {
17921
- if (!isWorktreeDirtyError(error)) {
17954
+ if (!isWorktreeForceRemovalError(error)) {
17922
17955
  throw error;
17923
17956
  }
17924
17957
  if (!options.force && !await confirmForceRemoveWorktree(worktree.path)) {
package/dist/remove.js CHANGED
@@ -4,7 +4,7 @@ import { loadEffectiveConfig } from "./config.js";
4
4
  import { isHeadless } from "./headless.js";
5
5
  import { extractHooks, runHook } from "./hooks.js";
6
6
  import { writeShellOutput } from "./shell-handoff.js";
7
- import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeDirtyError, loadLinkedWorktrees, removeWorktree, } from "./worktree-management.js";
7
+ import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeForceRemovalError, loadLinkedWorktrees, removeWorktree, } from "./worktree-management.js";
8
8
  import { buildWorktreePromptEntries, promptForSingleWorktree, } from "./worktree-picker.js";
9
9
  import { defaultConfirmForceDeleteBranch, defaultConfirmForceRemoveWorktree, } from "./worktree-prompts.js";
10
10
  const REMOVE_OUTPUT_FILE_ENV = "GJI_REMOVE_OUTPUT_FILE";
@@ -83,7 +83,7 @@ export function createRemoveCommand(dependencies = {}) {
83
83
  await removeWorktree(repository.repoRoot, worktree.path);
84
84
  }
85
85
  catch (error) {
86
- if (!isWorktreeDirtyError(error)) {
86
+ if (!isWorktreeForceRemovalError(error)) {
87
87
  throw error;
88
88
  }
89
89
  if (!options.force &&
@@ -8,5 +8,6 @@ export declare function removeWorktree(repoRoot: string, worktreePath: string):
8
8
  export declare function forceRemoveWorktree(repoRoot: string, worktreePath: string): Promise<void>;
9
9
  export declare function deleteBranch(repoRoot: string, branch: string): Promise<void>;
10
10
  export declare function forceDeleteBranch(repoRoot: string, branch: string): Promise<void>;
11
- export declare function isWorktreeDirtyError(error: unknown): boolean;
11
+ export declare function isWorktreeForceRemovalError(error: unknown): boolean;
12
+ export declare function isWorktreeDeletionError(error: unknown): boolean;
12
13
  export declare function isBranchUnmergedError(error: unknown): boolean;
@@ -1,4 +1,5 @@
1
1
  import { execFile } from "node:child_process";
2
+ import { rm } from "node:fs/promises";
2
3
  import { promisify } from "node:util";
3
4
  import { detectRepository, listWorktrees, } from "./repo.js";
4
5
  const execFileAsync = promisify(execFile);
@@ -19,10 +20,19 @@ export async function removeWorktree(repoRoot, worktreePath) {
19
20
  });
20
21
  }
21
22
  export async function forceRemoveWorktree(repoRoot, worktreePath) {
22
- await execFileAsync("git", ["worktree", "remove", "--force", worktreePath], {
23
- cwd: repoRoot,
24
- env: GIT_ENV,
25
- });
23
+ try {
24
+ await execFileAsync("git", ["worktree", "remove", "--force", worktreePath], {
25
+ cwd: repoRoot,
26
+ env: GIT_ENV,
27
+ });
28
+ }
29
+ catch (error) {
30
+ const worktreeIsStillRegistered = (await listWorktrees(repoRoot)).some((worktree) => worktree.path === worktreePath);
31
+ if (worktreeIsStillRegistered) {
32
+ throw error;
33
+ }
34
+ await rm(worktreePath, { force: true, recursive: true });
35
+ }
26
36
  }
27
37
  export async function deleteBranch(repoRoot, branch) {
28
38
  await execFileAsync("git", ["branch", "-d", branch], {
@@ -36,9 +46,15 @@ export async function forceDeleteBranch(repoRoot, branch) {
36
46
  env: GIT_ENV,
37
47
  });
38
48
  }
39
- export function isWorktreeDirtyError(error) {
40
- return (hasStderr(error) &&
41
- error.stderr.includes("contains modified or untracked files"));
49
+ export function isWorktreeForceRemovalError(error) {
50
+ if (!hasStderr(error)) {
51
+ return false;
52
+ }
53
+ return (error.stderr.includes("contains modified or untracked files") ||
54
+ isWorktreeDeletionError(error));
55
+ }
56
+ export function isWorktreeDeletionError(error) {
57
+ return hasStderr(error) && error.stderr.includes("failed to delete");
42
58
  }
43
59
  export function isBranchUnmergedError(error) {
44
60
  return hasStderr(error) && error.stderr.includes("is not fully merged");
@@ -1,4 +1,4 @@
1
- .TH GJI\-BACK 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-BACK 1 "July 2026" "gji 0.8.1" "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 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-CLEAN 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-clean \- interactively prune linked worktrees
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-COMPLETION 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-COMPLETION 1 "July 2026" "gji 0.8.1" "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 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-CONFIG 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-config \- manage global config defaults
4
4
  .SH SYNOPSIS
package/man/man1/gji-go.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI\-GO 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-GO 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-go \- print or select a worktree path
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-HISTORY 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-HISTORY 1 "July 2026" "gji 0.8.1" "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 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-INIT 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-init \- print or install shell integration
4
4
  .SH SYNOPSIS
package/man/man1/gji-ls.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI\-LS 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-LS 1 "July 2026" "gji 0.8.1" "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 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-NEW 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-new \- create a new branch or detached linked worktree
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-OPEN 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-OPEN 1 "July 2026" "gji 0.8.1" "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 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-PR 1 "July 2026" "gji 0.8.1" "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\-REMOVE 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-REMOVE 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-remove \- remove a linked worktree and delete its branch when present
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-ROOT 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-ROOT 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-root \- print the main repository root path
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-RUN\-HOOK 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-RUN\-HOOK 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-run\-hook \- run a named hook (after\-create, after\-enter, before\-remove) in the current worktree
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH GJI\-STATUS 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-STATUS 1 "July 2026" "gji 0.8.1" "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 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-SYNC\-FILES 1 "July 2026" "gji 0.8.1" "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 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-SYNC 1 "July 2026" "gji 0.8.1" "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\-WARP 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI\-WARP 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji\-warp \- jump to any worktree across all known repos
4
4
  .SH SYNOPSIS
package/man/man1/gji.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH GJI 1 "July 2026" "gji 0.8.0" "User Commands"
1
+ .TH GJI 1 "July 2026" "gji 0.8.1" "User Commands"
2
2
  .SH NAME
3
3
  gji \- Context switching without the mess.
4
4
  .SH SYNOPSIS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solaqua/gji",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Git worktree CLI for fast context switching.",
5
5
  "license": "MIT",
6
6
  "author": "sjquant",