@junheep/gwt 0.2.1 → 0.3.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 (3) hide show
  1. package/README.md +24 -11
  2. package/bin/gwt.mjs +139 -40
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -82,7 +82,6 @@ Repositories without a remote use their canonical path.
82
82
  {
83
83
  "projects": {
84
84
  "github.com/owner/repository": {
85
- "worktreeDirectory": ".worktrees",
86
85
  "copyFiles": [
87
86
  "apps/server/.env",
88
87
  "apps/web/.env"
@@ -114,7 +113,6 @@ The project file contains the configuration fields directly:
114
113
  ```json
115
114
  {
116
115
  "base": "origin/main",
117
- "worktreeDirectory": ".worktrees",
118
116
  "copyFiles": [
119
117
  "apps/server/.env",
120
118
  "apps/web/.env"
@@ -133,12 +131,16 @@ The two files are not merged. Run `gwt config show` to see whether user and
133
131
  repository configuration is available, the location of each existing config
134
132
  file, the active source, and its resolved value.
135
133
 
136
- All fields are optional. Without either config, worktrees are created beneath
137
- `.worktrees`, use the primary worktree's current commit as their base, and run
138
- no setup actions.
134
+ All fields are optional. Without either config, worktrees are created outside
135
+ the repository beneath `~/.gwt/worktrees`. Set `GWT_HOME` to an absolute path
136
+ to use a different gwt home directory. A repository normally uses a directory
137
+ named after it. If another repository already uses that name, gwt adds a short
138
+ hash derived from the canonical path. Worktrees use the primary worktree's
139
+ current commit as their base and run no setup actions.
139
140
 
140
141
  - `base`: Git revision used when `--base` is omitted.
141
- - `worktreeDirectory`: Repository-relative directory for managed worktrees.
142
+ - `worktreeDirectory`: Optional repository-relative directory for managed
143
+ worktrees. Setting it opts out of the external default.
142
144
  - `copyFiles`: Ignored local files copied from the primary worktree without
143
145
  overwriting an existing destination.
144
146
  - `ports`: Environment variable names assigned stable ports in the range
@@ -146,8 +148,17 @@ no setup actions.
146
148
  - `postCreate`: Executable run after files and ports are prepared.
147
149
  - `preRemove`: Executable run before removal.
148
150
 
149
- The worktree directory is added to `.git/info/exclude`; tracked project files
150
- are not modified.
151
+ An explicitly configured repository-relative worktree directory is added to
152
+ `.git/info/exclude`; tracked project files are not modified.
153
+
154
+ For example, an existing configuration can retain the previous in-repository
155
+ layout explicitly:
156
+
157
+ ```json
158
+ {
159
+ "worktreeDirectory": ".worktrees"
160
+ }
161
+ ```
151
162
 
152
163
  ## Hooks
153
164
 
@@ -192,8 +203,8 @@ Approval is invalidated when `.gwt.json` or either hook changes.
192
203
  gwt new [branch] [--base <ref>] [--no-hooks]
193
204
  gwt setup [id|branch|path] [--no-hooks]
194
205
  gwt list
195
- gwt switch [id|branch|path]
196
- gwt info [id|branch|path]
206
+ gwt switch [primary|id|branch|path]
207
+ gwt info [primary|id|branch|path]
197
208
  gwt remove [id|branch|path] [--keep-branch|--discard] [--yes] [--no-hooks]
198
209
  gwt trust [--revoke]
199
210
  gwt config create [--project]
@@ -216,7 +227,9 @@ Setup failures retain the worktree and record the failure. Retry with
216
227
  Run `gwt switch` without a target to open the interactive picker. Use the
217
228
  arrow keys, `j`/`k`, or Ctrl-n/Ctrl-p to move; press `/` to filter by branch,
218
229
  ID, or path. Enter switches to the selected worktree. Escape leaves filter
219
- mode or cancels the picker.
230
+ mode or cancels the picker. `primary` is a reserved ID for the repository's
231
+ primary worktree, so `gwt switch primary` returns to it from any linked
232
+ worktree.
220
233
 
221
234
  `gwt remove` refuses dirty worktrees and first tries to delete the branch with
222
235
  `git branch -d`. If Git rejects safe deletion, an interactive terminal asks
package/bin/gwt.mjs CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  } from "node:fs"
20
20
  import { createServer } from "node:net"
21
21
  import { homedir } from "node:os"
22
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"
22
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path"
23
23
  import { emitKeypressEvents } from "node:readline"
24
24
  import { createInterface } from "node:readline/promises"
25
25
 
@@ -28,7 +28,7 @@ const PORT_MIN = 20_000
28
28
  const PORT_MAX = 39_999
29
29
  const PICKER_ESCAPE_CODE_TIMEOUT_MS = 50
30
30
  const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/
31
- const DEFAULT_CONFIG = { worktreeDirectory: ".worktrees", copyFiles: [], ports: [] }
31
+ const DEFAULT_CONFIG = { copyFiles: [], ports: [] }
32
32
  const SKILL_DIRECTORIES = { claude: ".claude", codex: ".agents" }
33
33
  const SKILL_USAGE = `Usage: gwt skill install <${Object.keys(SKILL_DIRECTORIES).join("|")}> [--project] [--dry-run] [--yes]`
34
34
 
@@ -136,9 +136,12 @@ function validateConfig(parsed, label) {
136
136
  throw new CliError("base must be a non-empty string")
137
137
  }
138
138
 
139
- const worktreeDirectory = validateRelativePath(parsed.worktreeDirectory ?? ".worktrees", "worktreeDirectory")
140
- if (worktreeDirectory.split(/[\\/]+/).some((part) => !/^[A-Za-z0-9._-]+$/.test(part))) {
141
- throw new CliError("worktreeDirectory can only contain letters, digits, '.', '_', '-', and path separators")
139
+ let worktreeDirectory
140
+ if (parsed.worktreeDirectory !== undefined) {
141
+ worktreeDirectory = validateRelativePath(parsed.worktreeDirectory, "worktreeDirectory")
142
+ if (worktreeDirectory.split(/[\\/]+/).some((part) => !/^[A-Za-z0-9._-]+$/.test(part))) {
143
+ throw new CliError("worktreeDirectory can only contain letters, digits, '.', '_', '-', and path separators")
144
+ }
142
145
  }
143
146
  if (!Array.isArray(parsed.copyFiles ?? [])) throw new CliError("copyFiles must be an array")
144
147
  const copyFiles = (parsed.copyFiles ?? []).map((path, index) => validateRelativePath(path, `copyFiles[${index}]`))
@@ -156,18 +159,61 @@ function validateConfig(parsed, label) {
156
159
  if (parsed[hook] !== undefined) validateRelativePath(parsed[hook], hook)
157
160
  }
158
161
 
159
- return {
162
+ const config = {
160
163
  ...parsed,
161
- worktreeDirectory,
162
164
  copyFiles,
163
165
  ports,
164
166
  }
167
+ if (worktreeDirectory !== undefined) config.worktreeDirectory = worktreeDirectory
168
+ return config
165
169
  }
166
170
 
167
171
  function configHome() {
168
172
  return process.env.XDG_CONFIG_HOME || join(homedir(), ".config")
169
173
  }
170
174
 
175
+ function gwtHome() {
176
+ const path = process.env.GWT_HOME || join(homedir(), ".gwt")
177
+ if (!isAbsolute(path)) throw new CliError("GWT_HOME must be an absolute path")
178
+ return path
179
+ }
180
+
181
+ function repositoryUsesDirectory(repository, directory) {
182
+ const resolvedDirectory = pathExists(directory) ? canonical(directory) : resolve(directory)
183
+ return repository.worktrees.some((worktree) => {
184
+ if (!pathExists(worktree.path)) return false
185
+ const worktreePath = canonical(worktree.path)
186
+ return worktreePath !== repository.primaryPath && dirname(worktreePath) === resolvedDirectory
187
+ })
188
+ }
189
+
190
+ function directoryIsEmpty(path) {
191
+ try {
192
+ return statSync(path).isDirectory() && readdirSync(path).length === 0
193
+ } catch {
194
+ return false
195
+ }
196
+ }
197
+
198
+ function defaultWorktreeDirectory(repository) {
199
+ const root = join(gwtHome(), "worktrees")
200
+ const name = basename(repository.primaryPath)
201
+ const namedDirectory = join(root, name)
202
+ const digest = createHash("sha256").update(repository.primaryPath).digest("hex").slice(0, 8)
203
+ const disambiguatedDirectory = join(root, `${name}-${digest}`)
204
+
205
+ if (repositoryUsesDirectory(repository, disambiguatedDirectory)) return disambiguatedDirectory
206
+ if (repositoryUsesDirectory(repository, namedDirectory)) return namedDirectory
207
+ if (!pathExists(namedDirectory) || directoryIsEmpty(namedDirectory)) return namedDirectory
208
+ return disambiguatedDirectory
209
+ }
210
+
211
+ function resolveWorktreeDirectory(repository, config) {
212
+ return config.worktreeDirectory
213
+ ? resolve(repository.primaryPath, config.worktreeDirectory)
214
+ : defaultWorktreeDirectory(repository)
215
+ }
216
+
171
217
  function userConfigPath() {
172
218
  return join(configHome(), "gwt", "config.json")
173
219
  }
@@ -311,6 +357,8 @@ function resolveWorktree(repository, selector, options = {}) {
311
357
  return current
312
358
  }
313
359
 
360
+ if (selector === "primary") return repository.primary
361
+
314
362
  const metadata = loadMetadata(repository)
315
363
  const idMatch = metadata.find((item) => item.id === selector)
316
364
  if (idMatch) {
@@ -330,9 +378,10 @@ function resolveWorktree(repository, selector, options = {}) {
330
378
  }
331
379
 
332
380
  function generateId(repository, config) {
381
+ const directory = resolveWorktreeDirectory(repository, config)
333
382
  for (let attempt = 0; attempt < 100; attempt += 1) {
334
383
  const id = randomBytes(4).toString("hex")
335
- const target = join(repository.primaryPath, config.worktreeDirectory, id)
384
+ const target = join(directory, id)
336
385
  if (!existsSync(metadataPath(repository, id)) && !pathExists(target)) return id
337
386
  }
338
387
  throw new CliError("Could not generate a unique worktree ID")
@@ -632,24 +681,27 @@ async function commandNew(args) {
632
681
  const base = requestedBase
633
682
  ? gitOutput(["rev-parse", "--verify", `${requestedBase}^{commit}`], repository.primaryPath)
634
683
  : gitOutput(["rev-parse", "HEAD"], repository.primaryPath)
635
- const target = join(repository.primaryPath, configDocument.value.worktreeDirectory, id)
636
- ensureLocalExclude(repository, configDocument.value.worktreeDirectory)
684
+ const target = join(resolveWorktreeDirectory(repository, configDocument.value), id)
685
+ if (configDocument.value.worktreeDirectory) {
686
+ ensureLocalExclude(repository, configDocument.value.worktreeDirectory)
687
+ }
637
688
 
638
689
  git(["worktree", "add", "-b", branch, target, base], repository.primaryPath, { stdio: "inherit" })
690
+ const targetPath = canonical(target)
639
691
  const refreshed = discoverRepository(repository.primaryPath)
640
- const worktree = refreshed.worktrees.find((item) => resolve(item.path) === resolve(target))
692
+ const worktree = refreshed.worktrees.find((item) => resolve(item.path) === targetPath)
641
693
 
642
694
  try {
643
695
  const metadata = await setupWorktree(refreshed, configDocument, worktree, {
644
696
  id,
645
697
  noHooks: options["no-hooks"],
646
698
  })
647
- console.log(`Worktree ${metadata.id} is ready at ${target}`)
699
+ console.log(`Worktree ${metadata.id} is ready at ${targetPath}`)
648
700
  console.log(`Branch: ${branch}`)
649
701
  for (const [name, port] of Object.entries(metadata.ports)) console.log(`${name}: ${port}`)
650
- writeCdDirective(target)
702
+ writeCdDirective(targetPath)
651
703
  } catch (error) {
652
- console.error(`Setup failed; worktree retained at ${target}`)
704
+ console.error(`Setup failed; worktree retained at ${targetPath}`)
653
705
  console.error(`Retry: gwt setup ${id}`)
654
706
  console.error(`Remove: gwt remove ${id}`)
655
707
  throw error
@@ -711,22 +763,19 @@ async function chooseWorktree(repository) {
711
763
 
712
764
  const terminalWidth = Math.max(48, process.stdout.columns ?? 100)
713
765
  const idWidth = 8
714
- const setupWidth = Math.max(5, ...filtered.map((choice) => choice.setup.length))
715
- const longestBranch = Math.max(12, ...filtered.map((choice) => choice.branch.length))
766
+ const setupWidth = Math.max(5, ...filtered.map((choice) => displayWidth(choice.setup)))
767
+ const longestBranch = Math.max(12, ...filtered.map((choice) => displayWidth(choice.branch)))
716
768
  const flexibleWidth = terminalWidth - idWidth - setupWidth - 10
717
769
  const branchWidth = Math.min(32, longestBranch, Math.max(8, flexibleWidth - 8))
718
770
  const pathWidth = Math.max(8, flexibleWidth - branchWidth)
719
- const fit = (value, width) => value.length > width
720
- ? `${value.slice(0, Math.max(0, width - 1))}…`
721
- : value.padEnd(width)
722
771
  const visibleCount = Math.max(3, (process.stdout.rows ?? 24) - 5)
723
772
  const start = Math.max(0, Math.min(selected - Math.floor(visibleCount / 2), filtered.length - visibleCount))
724
773
  const visible = filtered.slice(start, start + visibleCount)
725
774
  const escapeAction = filtering ? "clear" : "cancel"
726
775
  const lines = [
727
- `${colors.dim}${fit(`Switch worktree Esc ${escapeAction} · Enter select · ↑↓/jk/C-n/C-p · / filter`, terminalWidth)}${colors.reset}`,
728
- ...(filtering ? [fit(`Filter: /${query}`, terminalWidth)] : []),
729
- `${colors.dim} ${fit("BRANCH", branchWidth)} ${fit("ID", idWidth)} ${fit("SETUP", setupWidth)} ${fit("PATH", pathWidth)}${colors.reset}`,
776
+ `${colors.dim}${fitDisplay(`Switch worktree Esc ${escapeAction} · Enter select · ↑↓/jk/C-n/C-p · / filter`, terminalWidth)}${colors.reset}`,
777
+ ...(filtering ? [fitDisplay(`Filter: /${query}`, terminalWidth)] : []),
778
+ `${colors.dim} ${fitDisplay("BRANCH", branchWidth)} ${fitDisplay("ID", idWidth)} ${fitDisplay("SETUP", setupWidth)} ${fitDisplay("PATH", pathWidth)}${colors.reset}`,
730
779
  ]
731
780
 
732
781
  if (visible.length === 0) {
@@ -736,7 +785,7 @@ async function chooseWorktree(repository) {
736
785
  const index = start + visibleIndex
737
786
  const selection = index === selected ? `${colors.cyan}>${colors.reset}` : " "
738
787
  const currentMarker = choice.current ? `${colors.yellow}*${colors.reset}` : " "
739
- lines.push(`${selection} ${currentMarker} ${fit(choice.branch, branchWidth)} ${fit(choice.id, idWidth)} ${fit(choice.setup, setupWidth)} ${fit(choice.path, pathWidth)}`)
788
+ lines.push(`${selection} ${currentMarker} ${fitDisplay(choice.branch, branchWidth)} ${fitDisplay(choice.id, idWidth)} ${fitDisplay(choice.setup, setupWidth)} ${fitDisplay(choice.path, pathWidth)}`)
740
789
  })
741
790
  }
742
791
 
@@ -809,7 +858,7 @@ async function chooseWorktree(repository) {
809
858
  }
810
859
 
811
860
  async function commandSwitch(args) {
812
- if (args.length > 1) throw new CliError("Usage: gwt switch [id|branch|path]")
861
+ if (args.length > 1) throw new CliError("Usage: gwt switch [primary|id|branch|path]")
813
862
  const repository = discoverRepository()
814
863
  const worktree = args[0] ? resolveWorktree(repository, args[0]) : await chooseWorktree(repository)
815
864
  writeCdDirective(canonical(worktree.path))
@@ -854,6 +903,38 @@ function padDisplay(value, width) {
854
903
  return `${value}${" ".repeat(Math.max(0, width - displayWidth(value)))}`
855
904
  }
856
905
 
906
+ function fitDisplay(value, width) {
907
+ const normalized = value.normalize("NFC")
908
+ if (displayWidth(normalized) <= width) return padDisplay(normalized, width)
909
+
910
+ const contentWidth = width - displayWidth("…")
911
+ let fitted = ""
912
+ let fittedWidth = 0
913
+ for (const character of normalized) {
914
+ const characterWidth = displayWidth(character)
915
+ if (fittedWidth + characterWidth > contentWidth) break
916
+ fitted += character
917
+ fittedWidth += characterWidth
918
+ }
919
+ return padDisplay(`${fitted}…`, width)
920
+ }
921
+
922
+ function truncateDisplayTail(value, width) {
923
+ const normalized = value.normalize("NFC")
924
+ if (displayWidth(normalized) <= width) return normalized
925
+
926
+ const contentWidth = width - displayWidth("…")
927
+ let fitted = ""
928
+ let fittedWidth = 0
929
+ for (const character of [...normalized].reverse()) {
930
+ const characterWidth = displayWidth(character)
931
+ if (fittedWidth + characterWidth > contentWidth) break
932
+ fitted = `${character}${fitted}`
933
+ fittedWidth += characterWidth
934
+ }
935
+ return `…${fitted}`
936
+ }
937
+
857
938
  function commandList(args) {
858
939
  if (args.length > 0) throw new CliError("Usage: gwt list")
859
940
  const repository = discoverRepository()
@@ -864,15 +945,25 @@ function commandList(args) {
864
945
  id: Math.max(2, ...rows.map((row) => displayWidth(row.id))),
865
946
  setup: Math.max(5, ...rows.map((row) => displayWidth(row.setup))),
866
947
  }
867
- console.log(`${colors.dim} ${padDisplay("BRANCH", widths.branch)} ${padDisplay("ID", widths.id)} ${padDisplay("SETUP", widths.setup)} PATH${colors.reset}`)
948
+ let pathWidth = null
949
+ if (process.stdout.isTTY) {
950
+ const minimumWidth = 8 + 6 + widths.id + widths.setup + 4
951
+ const terminalWidth = Math.max(minimumWidth, process.stdout.columns || 100)
952
+ const flexibleWidth = terminalWidth - widths.id - widths.setup - 8
953
+ widths.branch = Math.min(32, widths.branch, Math.max(6, flexibleWidth - 12))
954
+ pathWidth = flexibleWidth - widths.branch
955
+ }
956
+
957
+ console.log(`${colors.dim} ${fitDisplay("BRANCH", widths.branch)} ${fitDisplay("ID", widths.id)} ${fitDisplay("SETUP", widths.setup)} PATH${colors.reset}`)
868
958
  for (const row of rows) {
869
959
  const currentMarker = row.current ? `${colors.yellow}*${colors.reset}` : " "
870
- console.log(`${currentMarker} ${padDisplay(row.branch, widths.branch)} ${padDisplay(row.id, widths.id)} ${padDisplay(row.setup, widths.setup)} ${row.path}`)
960
+ const path = pathWidth === null ? row.path : truncateDisplayTail(row.path, pathWidth)
961
+ console.log(`${currentMarker} ${fitDisplay(row.branch, widths.branch)} ${fitDisplay(row.id, widths.id)} ${fitDisplay(row.setup, widths.setup)} ${path}`)
871
962
  }
872
963
  }
873
964
 
874
965
  function commandInfo(args) {
875
- if (args.length > 1) throw new CliError("Usage: gwt info [id|branch|path]")
966
+ if (args.length > 1) throw new CliError("Usage: gwt info [primary|id|branch|path]")
876
967
  const repository = discoverRepository()
877
968
  const worktree = resolveWorktree(repository, args[0])
878
969
  const metadata = metadataForWorktree(repository, worktree)
@@ -985,7 +1076,7 @@ function commandTrust(args) {
985
1076
  }
986
1077
 
987
1078
  function configScaffold() {
988
- return { worktreeDirectory: ".worktrees", copyFiles: [], ports: [] }
1079
+ return { copyFiles: [], ports: [] }
989
1080
  }
990
1081
 
991
1082
  function commandConfigCreate(args) {
@@ -1040,6 +1131,7 @@ function commandConfigShow(args) {
1040
1131
  console.log(`Repository config: ${repositoryConfigured ? "configured" : "not created"}`)
1041
1132
  if (repositoryConfigured) console.log(` File: ${projectPath}`)
1042
1133
  console.log(`Active config: ${activeLabel}`)
1134
+ console.log(`Worktree directory: ${resolveWorktreeDirectory(repository, active.value)}`)
1043
1135
  console.log(JSON.stringify(active.value, null, 2))
1044
1136
  }
1045
1137
 
@@ -1293,7 +1385,7 @@ function commandComplete(args) {
1293
1385
 
1294
1386
  if (args[0] === "worktrees") {
1295
1387
  const repository = discoverRepository()
1296
- const values = []
1388
+ const values = ["primary"]
1297
1389
  for (const worktree of repository.worktrees) {
1298
1390
  const metadata = metadataForWorktree(repository, worktree)
1299
1391
  if (metadata?.id) values.push(metadata.id)
@@ -1372,10 +1464,13 @@ Options:
1372
1464
 
1373
1465
  Behavior:
1374
1466
  The worktree receives an immutable 8-character ID. gwt creates it below the
1375
- configured worktreeDirectory, copies configured local files, assigns stable
1467
+ resolved worktree directory, copies configured local files, assigns stable
1376
1468
  ports, and runs postCreate. A setup failure keeps the worktree so setup can
1377
1469
  be retried. With shell integration installed, the current shell moves into
1378
- the new worktree after setup succeeds.
1470
+ the new worktree after setup succeeds. Without worktreeDirectory, the default
1471
+ is $GWT_HOME/worktrees or ~/.gwt/worktrees when GWT_HOME is not set. A short
1472
+ repository hash is added to the directory name only when needed to avoid a
1473
+ name collision.
1379
1474
 
1380
1475
  Examples:
1381
1476
  gwt new feature/auth
@@ -1418,36 +1513,40 @@ Example:
1418
1513
  switch: `Switch the current shell to another worktree.
1419
1514
 
1420
1515
  Usage:
1421
- gwt switch [id|branch|path]
1516
+ gwt switch [primary|id|branch|path]
1422
1517
 
1423
1518
  Arguments:
1424
- id|branch|path Worktree to switch to. Opens the picker when omitted.
1519
+ selector Worktree to switch to. Opens the picker when omitted.
1425
1520
 
1426
1521
  Options:
1427
1522
  -h, --help Show help for this command.
1428
1523
 
1429
1524
  Behavior:
1430
- The picker supports arrow keys, j/k, Ctrl-n/Ctrl-p, and '/' filtering. Shell
1431
- integration must be installed for gwt to change the parent shell's directory;
1432
- otherwise the selected path is only printed.
1525
+ 'primary' is the reserved ID for the primary worktree. Other worktrees can be
1526
+ selected by ID, exact branch name, or path. The picker supports arrow keys,
1527
+ j/k, Ctrl-n/Ctrl-p, and '/' filtering. Shell integration must be installed for
1528
+ gwt to change the parent shell's directory; otherwise the path is only printed.
1433
1529
 
1434
1530
  Examples:
1435
1531
  gwt switch
1532
+ gwt switch primary
1436
1533
  gwt switch feature/auth
1437
1534
  gwt switch a1b2c3d4`,
1438
1535
  info: `Show a worktree's identity, Git state, setup status, and assigned ports.
1439
1536
 
1440
1537
  Usage:
1441
- gwt info [id|branch|path]
1538
+ gwt info [primary|id|branch|path]
1442
1539
 
1443
1540
  Arguments:
1444
- id|branch|path Worktree to inspect. Defaults to the current worktree.
1541
+ selector 'primary', an ID, an exact branch name, or a path. Defaults
1542
+ to the current worktree.
1445
1543
 
1446
1544
  Options:
1447
1545
  -h, --help Show help for this command.
1448
1546
 
1449
1547
  Examples:
1450
1548
  gwt info
1549
+ gwt info primary
1451
1550
  gwt info feature/auth`,
1452
1551
  remove: `Safely remove a linked worktree and, by default, its branch.
1453
1552
 
@@ -1540,8 +1639,8 @@ Options:
1540
1639
  -h, --help Show help for this command.
1541
1640
 
1542
1641
  The output distinguishes a missing user config file from an existing file that
1543
- does not configure the current project. Repository configuration takes
1544
- precedence over user configuration.
1642
+ does not configure the current project. It also shows the resolved worktree
1643
+ directory. Repository configuration takes precedence over user configuration.
1545
1644
 
1546
1645
  Example:
1547
1646
  gwt config show`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junheep/gwt",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Lightweight native Git worktree workflows",
5
5
  "license": "MIT",
6
6
  "author": "Junhee Park",