vde-worktree 0.0.18 → 0.0.20

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/README.ja.md CHANGED
@@ -142,6 +142,7 @@ vw list --full-path
142
142
 
143
143
  - Git の porcelain 情報から worktree 一覧を取得
144
144
  - branch/path/dirty/lock/merged/PR/upstream を表示
145
+ - JSON メタデータには non-base branch ごとに `pr.status` と `pr.url` を含む
145
146
  - テーブル表示では長い `path` は端末幅に合わせて `…` で省略
146
147
  - `--full-path` でテーブル表示の path 省略を無効化
147
148
  - `--no-gh` 指定時は PR 状態判定をスキップ(`pr.status` は `unknown`、`merged.byPR` は `null`)
@@ -428,6 +429,7 @@ vw completion zsh --install
428
429
  - `merged.byPR`: GitHub PR merged 判定(`gh`)
429
430
  - `merged.overall`: 最終判定
430
431
  - `pr.status`: PR 状態(`none` / `open` / `merged` / `closed_unmerged` / `unknown`)
432
+ - `pr.url`: branch の最新 PR URL(取得不可時は `null`)
431
433
 
432
434
  `overall` ポリシー:
433
435
 
@@ -445,7 +447,7 @@ vw completion zsh --install
445
447
  - `gh` 未導入
446
448
  - `gh auth` 未設定
447
449
  - API 失敗
448
- - `git config vde-worktree.enableGh false`
450
+ - `config.yml` の `github.enabled: false`
449
451
  - `--no-gh` を指定して実行
450
452
 
451
453
  ## JSON 契約
@@ -466,15 +468,39 @@ vw completion zsh --install
466
468
  - `message`
467
469
  - `details`
468
470
 
469
- ## 設定キー(git config)
470
-
471
- - `vde-worktree.baseBranch`
472
- - `vde-worktree.baseRemote`
473
- - `vde-worktree.enableGh`
474
- - `vde-worktree.hooksEnabled`
475
- - `vde-worktree.hookTimeoutMs`
476
- - `vde-worktree.lockTimeoutMs`
477
- - `vde-worktree.staleLockTTLSeconds`
471
+ ## 設定(config.yml
472
+
473
+ 設定ファイルは次の順で読み込みます:
474
+
475
+ - `$XDG_CONFIG_HOME/vde/worktree/config.yml`(fallback: `~/.config/vde/worktree/config.yml`)
476
+ - `cwd` から Git 境界(`.git`)まで探索した `.vde/worktree/config.yml`
477
+ - `<repoRoot>/.vde/worktree/config.yml`(linked worktree 実行時も参照)
478
+
479
+ 主な設定キー:
480
+
481
+ ```yaml
482
+ paths:
483
+ worktreeRoot: .worktree
484
+ git:
485
+ baseBranch: null
486
+ baseRemote: origin
487
+ github:
488
+ enabled: true
489
+ hooks:
490
+ enabled: true
491
+ timeoutMs: 30000
492
+ locks:
493
+ timeoutMs: 15000
494
+ staleLockTTLSeconds: 1800
495
+ list:
496
+ table:
497
+ columns: [branch, dirty, merged, pr, locked, ahead, behind, path]
498
+ selector:
499
+ cd:
500
+ prompt: "worktree> "
501
+ surface: auto # auto | inline | tmux-popup
502
+ tmuxPopupOpts: "80%,70%"
503
+ ```
478
504
 
479
505
  ## 現在のスコープ
480
506
 
package/README.md CHANGED
@@ -142,6 +142,7 @@ What it does:
142
142
 
143
143
  - Lists all worktrees from Git porcelain output
144
144
  - Includes metadata such as branch, path, dirty, lock, merged, PR status, and upstream status
145
+ - JSON metadata includes `pr.status` and `pr.url` for each non-base branch
145
146
  - In table output, long `path` values are truncated with `…` to fit terminal width by default
146
147
  - Use `--full-path` to disable path truncation in table output
147
148
  - With `--no-gh`, skips PR status checks (`pr.status` becomes `unknown`, `merged.byPR` becomes `null`)
@@ -428,6 +429,7 @@ Each worktree reports:
428
429
  - `merged.byPR`: PR-based merged check via GitHub CLI
429
430
  - `merged.overall`: final decision
430
431
  - `pr.status`: PR state (`none` / `open` / `merged` / `closed_unmerged` / `unknown`)
432
+ - `pr.url`: latest PR URL for the branch (`null` when unavailable)
431
433
 
432
434
  Overall policy:
433
435
 
@@ -440,7 +442,7 @@ Overall policy:
440
442
  - `byPR === false` or explicit lifecycle "not merged" evidence => `overall = false`
441
443
  - otherwise `overall = null`
442
444
 
443
- `byPR` becomes `null` and `pr.status` becomes `unknown` when PR lookup is unavailable (for example: `gh` missing, auth missing, API error, `vde-worktree.enableGh=false`, or `--no-gh`).
445
+ `byPR` becomes `null` and `pr.status` becomes `unknown` when PR lookup is unavailable (for example: `gh` missing, auth missing, API error, `github.enabled=false` in config.yml, or `--no-gh`).
444
446
 
445
447
  ## JSON Contract
446
448
 
@@ -460,17 +462,39 @@ Error shape:
460
462
  - `message`
461
463
  - `details`
462
464
 
463
- ## Configuration Keys
464
-
465
- Configured via `git config`:
466
-
467
- - `vde-worktree.baseBranch`
468
- - `vde-worktree.baseRemote`
469
- - `vde-worktree.enableGh`
470
- - `vde-worktree.hooksEnabled`
471
- - `vde-worktree.hookTimeoutMs`
472
- - `vde-worktree.lockTimeoutMs`
473
- - `vde-worktree.staleLockTTLSeconds`
465
+ ## Configuration (`config.yml`)
466
+
467
+ Configuration is loaded from:
468
+
469
+ - `$XDG_CONFIG_HOME/vde/worktree/config.yml` (fallback: `~/.config/vde/worktree/config.yml`)
470
+ - `.vde/worktree/config.yml` discovered from `cwd` to the local Git boundary (`.git`)
471
+ - `<repoRoot>/.vde/worktree/config.yml` (always considered, including linked worktree execution)
472
+
473
+ Supported keys (examples):
474
+
475
+ ```yaml
476
+ paths:
477
+ worktreeRoot: .worktree
478
+ git:
479
+ baseBranch: null
480
+ baseRemote: origin
481
+ github:
482
+ enabled: true
483
+ hooks:
484
+ enabled: true
485
+ timeoutMs: 30000
486
+ locks:
487
+ timeoutMs: 15000
488
+ staleLockTTLSeconds: 1800
489
+ list:
490
+ table:
491
+ columns: [branch, dirty, merged, pr, locked, ahead, behind, path]
492
+ selector:
493
+ cd:
494
+ prompt: "worktree> "
495
+ surface: auto # auto | inline | tmux-popup
496
+ tmuxPopupOpts: "80%,70%"
497
+ ```
474
498
 
475
499
  ## Current Scope
476
500
 
@@ -9,10 +9,24 @@ end
9
9
  function __vw_default_branch
10
10
  command git rev-parse --is-inside-work-tree >/dev/null 2>/dev/null; or return 0
11
11
 
12
- set -l configured (command git config --get vde-worktree.baseBranch 2>/dev/null)
13
- if test -n "$configured"
14
- echo $configured
15
- return
12
+ set -l vw_bin (__vw_current_bin)
13
+ if test -n "$vw_bin"
14
+ set -l configured (command $vw_bin list --json 2>/dev/null | command node -e '
15
+ const fs = require("fs")
16
+ let payload
17
+ try {
18
+ payload = JSON.parse(fs.readFileSync(0, "utf8"))
19
+ } catch {
20
+ process.exit(0)
21
+ }
22
+ if (typeof payload?.baseBranch === "string" && payload.baseBranch.length > 0) {
23
+ process.stdout.write(payload.baseBranch)
24
+ }
25
+ ' 2>/dev/null)
26
+ if test -n "$configured"
27
+ echo $configured
28
+ return
29
+ end
16
30
  end
17
31
 
18
32
  command git show-ref --verify --quiet refs/heads/main >/dev/null 2>/dev/null
@@ -117,12 +131,17 @@ try {
117
131
  process.exit(0)
118
132
  }
119
133
  const repoRoot = typeof payload?.repoRoot === "string" ? payload.repoRoot : ""
120
- if (repoRoot.length === 0) process.exit(0)
121
- const worktreeRoot = path.join(repoRoot, ".worktree")
134
+ const managedWorktreeRoot =
135
+ typeof payload?.managedWorktreeRoot === "string" && payload.managedWorktreeRoot.length > 0
136
+ ? payload.managedWorktreeRoot
137
+ : repoRoot.length > 0
138
+ ? path.join(repoRoot, ".worktree")
139
+ : ""
140
+ if (managedWorktreeRoot.length === 0) process.exit(0)
122
141
  const worktrees = Array.isArray(payload.worktrees) ? payload.worktrees : []
123
142
  for (const worktree of worktrees) {
124
143
  if (typeof worktree?.path !== "string" || worktree.path.length === 0) continue
125
- const rel = path.relative(worktreeRoot, worktree.path)
144
+ const rel = path.relative(managedWorktreeRoot, worktree.path)
126
145
  if (!rel || rel === "." || rel === ".." || rel.startsWith(`..${path.sep}`)) continue
127
146
  const name = rel.split(path.sep).join("/")
128
147
  const branch = typeof worktree?.branch === "string" && worktree.branch.length > 0 ? worktree.branch : "(detached)"
@@ -9,11 +9,27 @@ _vw_worktree_branches_raw() {
9
9
 
10
10
  _vw_default_branch_raw() {
11
11
  command git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0
12
- local configured
13
- configured="$(command git config --get vde-worktree.baseBranch 2>/dev/null)"
14
- if [[ -n "${configured}" ]]; then
15
- print -r -- "${configured}"
16
- return 0
12
+ local vw_bin="${words[1]:-vw}"
13
+ if command -v "$vw_bin" >/dev/null 2>&1; then
14
+ local configured
15
+ configured="$(
16
+ command "$vw_bin" list --json 2>/dev/null | command node -e '
17
+ const fs = require("fs")
18
+ let payload
19
+ try {
20
+ payload = JSON.parse(fs.readFileSync(0, "utf8"))
21
+ } catch {
22
+ process.exit(0)
23
+ }
24
+ if (typeof payload?.baseBranch === "string" && payload.baseBranch.length > 0) {
25
+ process.stdout.write(payload.baseBranch)
26
+ }
27
+ ' 2>/dev/null
28
+ )"
29
+ if [[ -n "${configured}" ]]; then
30
+ print -r -- "${configured}"
31
+ return 0
32
+ fi
17
33
  fi
18
34
  if command git show-ref --verify --quiet refs/heads/main 2>/dev/null; then
19
35
  print -r -- "main"
@@ -94,12 +110,17 @@ try {
94
110
  process.exit(0)
95
111
  }
96
112
  const repoRoot = typeof payload?.repoRoot === "string" ? payload.repoRoot : ""
97
- if (repoRoot.length === 0) process.exit(0)
98
- const worktreeRoot = path.join(repoRoot, ".worktree")
113
+ const managedWorktreeRoot =
114
+ typeof payload?.managedWorktreeRoot === "string" && payload.managedWorktreeRoot.length > 0
115
+ ? payload.managedWorktreeRoot
116
+ : repoRoot.length > 0
117
+ ? path.join(repoRoot, ".worktree")
118
+ : ""
119
+ if (managedWorktreeRoot.length === 0) process.exit(0)
99
120
  const worktrees = Array.isArray(payload.worktrees) ? payload.worktrees : []
100
121
  for (const worktree of worktrees) {
101
122
  if (typeof worktree?.path !== "string" || worktree.path.length === 0) continue
102
- const rel = path.relative(worktreeRoot, worktree.path)
123
+ const rel = path.relative(managedWorktreeRoot, worktree.path)
103
124
  if (!rel || rel === "." || rel === ".." || rel.startsWith(`..${path.sep}`)) continue
104
125
  const name = rel.split(path.sep).join("/")
105
126
  const branch = typeof worktree?.branch === "string" && worktree.branch.length > 0 ? worktree.branch : "(detached)"