@cestoliv/wt 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -1,50 +1,79 @@
1
1
  # wt
2
2
 
3
- A fast, interactive TUI for managing git worktrees.
3
+ A fast TUI for git worktrees — browse, create, open, and delete without leaving
4
+ the terminal. Plus **one-command AI agents**:
4
5
 
5
- Browse, create, open, and delete worktrees without leaving the terminal. Fuzzy search across branches, auto-open your IDE, and run setup commands on new worktrees — all from one tool.
6
+ ```bash
7
+ wt agent fix-auth "Plan the auth refactor"
8
+ ```
9
+
10
+ → spins up an isolated worktree and launches Claude in Zed, pre-loaded with your
11
+ prompt.
6
12
 
7
13
  ## Install
8
14
 
9
15
  ```bash
10
- npm install -g @cestoliv/wt
16
+ npm install -g @cestoliv/wt # also the update command
11
17
  ```
12
18
 
13
19
  Requires Node.js 20+ and Git. The command is `wt`.
14
20
 
15
- ### Update
21
+ ## Let your AI assistant set it up
16
22
 
17
- ```bash
18
- npm install -g @cestoliv/wt
19
- ```
23
+ Already using an AI coding assistant (Claude Code, Cursor, …)? Paste this prompt
24
+ it reads `wt skill` to learn the tool, then configures `wt` for you:
20
25
 
21
- ### Pre-release builds
26
+ ```text
27
+ Run `wt skill` to learn how the `wt` CLI works and what its config options are.
28
+ Then configure it for me: choose sensible values for my editor, base branch,
29
+ setup commands, and the `wt agent` settings — ask me about anything you can't
30
+ infer from this project. Write the result to the config file (find its path with
31
+ `wt config --path`), then show me the final config.
32
+ ```
22
33
 
23
- Add the `publish-dev` label to a PR to publish that branch under the `dev`
24
- dist-tag (the label is removed automatically afterwards):
34
+ ## Quick start
25
35
 
26
36
  ```bash
27
- npm install -g @cestoliv/wt@dev
37
+ wt # Browse worktrees (interactive TUI)
38
+ wt create my-feat # New worktree, opens your IDE
39
+ wt agent my-feat "Plan the feature" # New worktree + AI agent in Zed (macOS)
40
+ wt config # Edit config in $EDITOR
41
+ wt skill # Print the skill file (for AI agents)
28
42
  ```
29
43
 
30
- ## Quick Start
44
+ ## `wt agent <branch> <plan_prompt>` — the standout
31
45
 
32
46
  ```bash
33
- # Inside any git repo
34
- wt # Browse worktrees
35
- wt create my-feat # Create a new worktree and open it in your IDE
36
- wt config # Edit config in $EDITOR
47
+ wt agent feat/login "Read the codebase, then propose a plan for login."
37
48
  ```
38
49
 
39
- ## Usage
50
+ Creates a worktree exactly like `wt create`, then auto-starts your agent
51
+ (default `claude --permission-mode plan`) in Zed's integrated terminal —
52
+ pre-filled with your prompt and left interactive for you to take over.
53
+
54
+ Under the hood it writes a temporary `.zed/tasks.json`, installs a global Zed
55
+ keymap chord, opens Zed and fires the chord via `osascript`, then removes the
56
+ temp task so the repo stays clean.
57
+
58
+ **Requires** macOS, Zed, and Accessibility permission for the app running `wt`.
59
+ Not granted yet? `wt agent` opens *System Settings → Privacy & Security →
60
+ Accessibility*, waits while you grant it (you may need to quit and reopen the
61
+ app), then retries automatically. On other platforms — or when `ide` isn't
62
+ `zed` — the worktree is still created and opened, just without the agent.
40
63
 
41
- ### Browse worktrees `wt`
64
+ If the path already exists, `wt agent` offers to open it — or open it and start
65
+ the agent — instead of erroring (in a non-interactive shell it exits non-zero).
42
66
 
43
- Launches an interactive list of worktrees with fuzzy search.
67
+ > **Tip:** trust the parent directory of your worktrees in Claude once, and
68
+ > every worktree created beneath it starts hands-free.
69
+
70
+ ## Browse — `wt`
71
+
72
+ An interactive, fuzzy-searchable list of your worktrees:
44
73
 
45
74
  ```
46
75
  MY-PROJECT
47
- ▶ main ~/dev/my-project
76
+ ▶ main ~/dev/my-project
48
77
  fix: resolve auth bug (2h ago)
49
78
  feat/dashboard ~/dev/my-project-feat-dashboard
50
79
  wip: add chart component (1d ago)
@@ -52,51 +81,46 @@ MY-PROJECT
52
81
  ↕ navigate · Enter open · D delete · C create · Q quit
53
82
  ```
54
83
 
55
- | Key | Action |
56
- | ------- | ------------------------------- |
57
- | `↑` `↓` | Navigate |
58
- | `Enter` | Open worktree in IDE |
59
- | `D` | Delete worktree (with confirm) |
60
- | `C` | Create new worktree (repo mode) |
61
- | `Q` | Quit |
62
-
63
- Type to fuzzy-filter branches instantly.
64
-
65
- **Repo mode** (inside a git repo): shows worktrees for that repo.
66
- **Global mode** (outside a repo): shows worktrees across all registered repos.
84
+ Type to fuzzy-filter branches instantly. Inside a repo it shows that repo's
85
+ worktrees (and `C` creates one); run it outside any repo to browse worktrees
86
+ across all registered repos.
67
87
 
68
- ### Create a worktree — `wt create [branch]`
88
+ ## Create — `wt create [branch]`
69
89
 
70
90
  ```bash
71
- wt create feat/login # Create from base branch (origin/main by default)
72
- wt create # Prompts for branch name
91
+ wt create feat/login # From base branch (origin/main by default)
92
+ wt create # Prompts for a branch name
73
93
  ```
74
94
 
75
- What happens:
95
+ Creates a worktree as a sibling directory (`../my-project-feat-login`), runs your
96
+ `setup_commands`, and opens it in your IDE. Run it outside a repo to pick from
97
+ registered repos.
76
98
 
77
- 1. Creates a worktree as a sibling directory: `../my-project-feat-login`
78
- 2. Runs configured setup commands (e.g., `npm install`)
79
- 3. Opens the worktree in your IDE
99
+ If the path already exists, `wt create` offers to open it in your IDE instead of
100
+ erroring (in a non-interactive shell it exits non-zero).
80
101
 
81
- Run outside a repo to pick from registered repos via an interactive picker.
82
-
83
- ### Edit config — `wt config`
102
+ ## Configuration
84
103
 
85
- Opens the config file in `$EDITOR`.
104
+ Edit with `wt config` (`wt config --path` prints the file location
105
+ `~/Library/Preferences/wt-nodejs/config.json` on macOS).
86
106
 
87
- ## Configuration
107
+ | Key | Default | Description |
108
+ | --------------------- | --------------------------------- | ------------------------------------------------- |
109
+ | `ide` | `"zed"` | Editor to open worktrees with |
110
+ | `ide_open_args` | `["-n"]` | Extra args passed to the IDE command |
111
+ | `base_branch` | `"origin/main"` | Branch new worktrees are created from |
112
+ | `worktree_path` | `"../"` | Where worktrees are placed (relative to repo) |
113
+ | `setup_commands` | `[]` | Commands to run in new worktrees |
114
+ | `agent_command` | `"claude --permission-mode plan"` | What `wt agent` runs in Zed (prompt appended) |
115
+ | `agent_trigger_chord` | `"ctrl-shift-cmd-c"` | Zed keymap chord `wt agent` installs and presses |
116
+ | `repo_overrides` | `{}` | Per-repo overrides for any key above |
88
117
 
89
- Config is stored at `~/Library/Preferences/wt-nodejs/config.json` (macOS).
118
+ Override any key per repo:
90
119
 
91
120
  ```json
92
121
  {
93
- "ide": "code",
94
- "ide_open_args": ["-n"],
95
- "base_branch": "origin/main",
96
- "worktree_path": "../",
97
- "setup_commands": ["npm install"],
98
122
  "repo_overrides": {
99
- "/path/to/special-repo": {
123
+ "/path/to/repo": {
100
124
  "base_branch": "origin/develop",
101
125
  "setup_commands": ["pnpm install", "pnpm build"]
102
126
  }
@@ -104,14 +128,12 @@ Config is stored at `~/Library/Preferences/wt-nodejs/config.json` (macOS).
104
128
  }
105
129
  ```
106
130
 
107
- | Key | Default | Description |
108
- | ---------------- | --------------- | --------------------------------------------- |
109
- | `ide` | `"zed"` | Editor to open worktrees with |
110
- | `ide_open_args` | `["-n"]` | Extra args passed to the IDE command |
111
- | `base_branch` | `"origin/main"` | Branch new worktrees are created from |
112
- | `worktree_path` | `"../"` | Where worktrees are placed (relative to repo) |
113
- | `setup_commands` | `[]` | Commands to run in new worktrees |
114
- | `repo_overrides` | `{}` | Per-repo overrides for any of the above |
131
+ ## Pre-release builds
132
+
133
+ Add the `publish-dev` label to a PR to publish that branch as a unique, pinned
134
+ prerelease (e.g. `0.1.0-pr12.gabc1234`); the exact install command is posted as a
135
+ PR comment. There's no rolling `dev` channel each build is a distinct version
136
+ you install explicitly.
115
137
 
116
138
  ## License
117
139
 
package/SKILL.md ADDED
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: wt-worktree-manager
3
+ description: Use the wt CLI to create, browse, open, and delete git worktrees across repos. Use when the user asks to manage worktrees, create isolated branches, or configure worktree defaults.
4
+ ---
5
+
6
+ # wt — Git Worktree Manager
7
+
8
+ `wt` is a CLI for managing git worktrees. It provides an interactive TUI to browse, create, open in your IDE, and delete worktrees across multiple repos.
9
+
10
+ ## Commands
11
+
12
+ ### `wt` (no subcommand)
13
+
14
+ Launch the interactive TUI. Shows worktrees for the current repo (repo mode) or all registered repos (global mode, when run outside a repo).
15
+
16
+ **Keybindings in the TUI:**
17
+
18
+ - Arrow keys / `j`/`k` — navigate
19
+ - `Enter` — open worktree in IDE
20
+ - `d` — delete worktree
21
+ - `c` — create new worktree (repo mode only)
22
+ - `/` — search
23
+ - `q` / `Esc` — quit
24
+
25
+ ### `wt create [branch]`
26
+
27
+ Create a new worktree. If `branch` is omitted, prompts interactively.
28
+
29
+ The worktree is created as a sibling directory to the repo: `<parent>/<repo-name>-<branch-name>`.
30
+
31
+ After creation, `wt` runs any configured `setup_commands` and opens the worktree in your IDE.
32
+
33
+ If the worktree path already exists, `wt create` doesn't error — it prompts you
34
+ to **open it in the IDE** or **quit**. (In a non-interactive shell it errors
35
+ with a non-zero exit instead of prompting.)
36
+
37
+ ### `wt agent <branch> <plan_prompt>`
38
+
39
+ Create a worktree (same as `wt create`) **and** auto-start an AI agent in Zed's
40
+ integrated terminal, pre-filled with `<plan_prompt>` and left interactive for
41
+ you to take over.
42
+
43
+ ```bash
44
+ wt agent feature/login 'Read the codebase, then propose a plan for login.'
45
+ ```
46
+
47
+ It writes a temporary `.zed/tasks.json` running
48
+ `<agent_command> '<plan_prompt>'`, ensures a global Zed keymap chord
49
+ (`agent_trigger_chord`) spawns that task, opens Zed, presses the chord via
50
+ `osascript`, then removes the temporary task so the repo is left clean.
51
+
52
+ **macOS + Zed only.** Requires Accessibility permission for the app that runs
53
+ `wt` (Zed itself, when run from its integrated terminal). If it isn't granted,
54
+ `wt agent` opens the *Privacy & Security → Accessibility* settings pane and waits
55
+ for you to grant it and confirm, then retries automatically. On other platforms
56
+ (or when `ide` is not `zed`) the worktree is still created and opened, but the
57
+ agent is not auto-started.
58
+
59
+ If the worktree path already exists, `wt agent` prompts you to **open it in the
60
+ IDE**, **open it and start the agent**, or **quit** — instead of erroring. (In a
61
+ non-interactive shell it errors with a non-zero exit instead of prompting.)
62
+
63
+ ### `wt config`
64
+
65
+ Open the global config file in `$EDITOR` (defaults to `nano`).
66
+
67
+ ```bash
68
+ wt config # open in editor
69
+ wt config --path # print config file path only
70
+ ```
71
+
72
+ ### `wt skill`
73
+
74
+ Print this skill file to stdout. Useful for piping to agents or copying to a project.
75
+
76
+ ## Configuration
77
+
78
+ Config is stored as JSON. Get the path with `wt config --path`.
79
+
80
+ ### Schema
81
+
82
+ | Key | Type | Default | Description |
83
+ | ---------------- | ---------- | --------------- | ------------------------------------------------------------------------- |
84
+ | `worktree_path` | `string` | `"../"` | Where to place new worktrees, relative to the repo root |
85
+ | `base_branch` | `string` | `"origin/main"` | Branch to base new worktrees on |
86
+ | `setup_commands` | `string[]` | `[]` | Commands to run in a new worktree after creation (e.g. `["npm install"]`) |
87
+ | `ide` | `string` | `"zed"` | IDE command to open worktrees with |
88
+ | `ide_open_args` | `string[]` | `["-n"]` | Arguments passed to the IDE command |
89
+ | `agent_command` | `string` | `"claude --permission-mode plan"` | Command `wt agent` runs in Zed; `<plan_prompt>` is appended single-quoted |
90
+ | `agent_trigger_chord` | `string` | `"ctrl-shift-cmd-c"` | Zed keymap chord `wt agent` installs/presses to spawn the agent task |
91
+ | `repos` | `string[]` | `[]` | Registered repo paths (auto-populated on first use) |
92
+ | `repo_overrides` | `object` | `{}` | Per-repo config overrides (see below) |
93
+
94
+ ### Per-repo overrides
95
+
96
+ Override any field (`worktree_path`, `base_branch`, `setup_commands`, `ide`, `ide_open_args`, `agent_command`, `agent_trigger_chord`) for a specific repo:
97
+
98
+ ```json
99
+ {
100
+ "base_branch": "origin/main",
101
+ "ide": "zed",
102
+ "repo_overrides": {
103
+ "/path/to/my-repo": {
104
+ "base_branch": "origin/develop",
105
+ "setup_commands": ["npm install", "npm run build"]
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Common workflows
112
+
113
+ ### Create a worktree for a new feature
114
+
115
+ ```bash
116
+ cd /path/to/repo
117
+ wt create feature/my-branch
118
+ ```
119
+
120
+ ### Configure setup commands for a repo
121
+
122
+ ```bash
123
+ wt config
124
+ # Then add to the JSON:
125
+ # "repo_overrides": {
126
+ # "/path/to/repo": {
127
+ # "setup_commands": ["npm install"]
128
+ # }
129
+ # }
130
+ ```
131
+
132
+ ### Browse all worktrees across repos
133
+
134
+ Run `wt` from any directory outside a git repo to see worktrees from all registered repos.
@@ -0,0 +1,428 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ openConfiguredIde,
4
+ prepareWorktree,
5
+ promptExistingWorktree
6
+ } from "./chunk-BYWCGKUP.js";
7
+ import "./chunk-FSARWOCW.js";
8
+ import "./chunk-IQWENLPW.js";
9
+
10
+ // src/commands/agent.ts
11
+ import * as clack from "@clack/prompts";
12
+ import pc from "picocolors";
13
+
14
+ // src/lib/zed.ts
15
+ import { spawn } from "child_process";
16
+ import {
17
+ existsSync,
18
+ mkdirSync,
19
+ readdirSync,
20
+ readFileSync,
21
+ rmSync,
22
+ writeFileSync
23
+ } from "fs";
24
+ import { homedir } from "os";
25
+ import { dirname, join } from "path";
26
+ import { applyEdits, modify, parse } from "jsonc-parser";
27
+ var AGENT_TASK_LABEL = "wt: agent";
28
+ function buildAgentTask(agentCommand, prompt, label) {
29
+ const escaped = prompt.replace(/'/g, "'\\''");
30
+ return {
31
+ label,
32
+ command: `${agentCommand} '${escaped}'`,
33
+ cwd: "$ZED_WORKTREE_ROOT",
34
+ use_new_terminal: true,
35
+ allow_concurrent_runs: false,
36
+ reveal: "always",
37
+ reveal_target: "dock",
38
+ shell: "system"
39
+ };
40
+ }
41
+ function removeTask(tasks, label) {
42
+ return tasks.filter((t) => t.label !== label);
43
+ }
44
+ function buildKeymapBinding(chord, label) {
45
+ return {
46
+ context: "Workspace",
47
+ bindings: { [chord]: ["task::Spawn", { task_name: label }] }
48
+ };
49
+ }
50
+ function upsertKeymapBinding(keymap, chord, label) {
51
+ const target = buildKeymapBinding(chord, label);
52
+ const targetValue = JSON.stringify(target.bindings[chord]);
53
+ const already = keymap.some(
54
+ (e) => e.context === "Workspace" && e.bindings != null && JSON.stringify(e.bindings[chord]) === targetValue
55
+ );
56
+ if (already) return keymap;
57
+ return [...keymap, target];
58
+ }
59
+ function hasConflictingChord(keymap, chord, label) {
60
+ const ours = JSON.stringify(buildKeymapBinding(chord, label).bindings[chord]);
61
+ return keymap.some(
62
+ (e) => e.bindings != null && e.bindings[chord] !== void 0 && JSON.stringify(e.bindings[chord]) !== ours
63
+ );
64
+ }
65
+ var MODIFIER_MAP = {
66
+ ctrl: "control down",
67
+ control: "control down",
68
+ shift: "shift down",
69
+ cmd: "command down",
70
+ command: "command down",
71
+ super: "command down",
72
+ alt: "option down",
73
+ opt: "option down",
74
+ option: "option down"
75
+ };
76
+ var KEY_CODE_MAP = {
77
+ space: 49,
78
+ tab: 48,
79
+ return: 36,
80
+ enter: 36,
81
+ escape: 53,
82
+ esc: 53,
83
+ delete: 51,
84
+ backspace: 51,
85
+ forwarddelete: 117,
86
+ up: 126,
87
+ down: 125,
88
+ left: 123,
89
+ right: 124,
90
+ home: 115,
91
+ end: 119,
92
+ pageup: 116,
93
+ pagedown: 121,
94
+ f1: 122,
95
+ f2: 120,
96
+ f3: 99,
97
+ f4: 118,
98
+ f5: 96,
99
+ f6: 97,
100
+ f7: 98,
101
+ f8: 100,
102
+ f9: 101,
103
+ f10: 109,
104
+ f11: 103,
105
+ f12: 111
106
+ };
107
+ function parseChord(chord) {
108
+ const parts = chord.split("-").map((p) => p.trim().toLowerCase()).filter(Boolean);
109
+ if (parts.length === 0) {
110
+ throw new Error(`Invalid chord: "${chord}"`);
111
+ }
112
+ const key = parts[parts.length - 1];
113
+ const modifiers = parts.slice(0, -1).map((m) => {
114
+ const mapped = MODIFIER_MAP[m];
115
+ if (!mapped) {
116
+ throw new Error(`Unknown modifier "${m}" in chord "${chord}"`);
117
+ }
118
+ return mapped;
119
+ });
120
+ return { key, modifiers };
121
+ }
122
+ function buildOsascript(chord, opts = {}) {
123
+ const { loadDelay = 3, activateDelay = 0.8 } = opts;
124
+ const { key, modifiers } = parseChord(chord);
125
+ const using = modifiers.length > 0 ? ` using {${modifiers.join(", ")}}` : "";
126
+ let press;
127
+ if (key.length === 1) {
128
+ const escaped = key.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
129
+ press = `keystroke "${escaped}"`;
130
+ } else {
131
+ const code = KEY_CODE_MAP[key];
132
+ if (code === void 0) {
133
+ throw new Error(
134
+ `Unsupported key "${key}" in chord "${chord}". Use a single character or one of: ${Object.keys(KEY_CODE_MAP).join(", ")}.`
135
+ );
136
+ }
137
+ press = `key code ${code}`;
138
+ }
139
+ return [
140
+ `delay ${loadDelay}`,
141
+ 'tell application "Zed" to activate',
142
+ `delay ${activateDelay}`,
143
+ `tell application "System Events" to ${press}${using}`
144
+ ].join("\n");
145
+ }
146
+ function parseTasks(raw) {
147
+ const errors = [];
148
+ const parsed = parse(raw, errors, { allowTrailingComma: true });
149
+ if (errors.length > 0 || !Array.isArray(parsed)) return null;
150
+ return parsed;
151
+ }
152
+ function readTasks(tasksPath) {
153
+ try {
154
+ return parseTasks(readFileSync(tasksPath, "utf8")) ?? [];
155
+ } catch {
156
+ return [];
157
+ }
158
+ }
159
+ function writeAgentTask(worktreePath, task) {
160
+ const zedDir = join(worktreePath, ".zed");
161
+ const tasksPath = join(zedDir, "tasks.json");
162
+ const createdDir = !existsSync(zedDir);
163
+ if (createdDir) mkdirSync(zedDir, { recursive: true });
164
+ const createdFile = !existsSync(tasksPath);
165
+ if (createdFile) {
166
+ writeFileSync(tasksPath, `${JSON.stringify([task], null, 2)}
167
+ `);
168
+ return { createdDir, createdFile };
169
+ }
170
+ const raw = readFileSync(tasksPath, "utf8");
171
+ const tasks = parseTasks(raw);
172
+ if (tasks === null) {
173
+ process.stderr.write(
174
+ `
175
+ Warning: could not parse ${tasksPath}; not modifying it. The agent task was not added.
176
+ `
177
+ );
178
+ return { createdDir, createdFile };
179
+ }
180
+ const idx = tasks.findIndex((t) => t.label === task.label);
181
+ const edits = modify(raw, idx === -1 ? [tasks.length] : [idx], task, {
182
+ isArrayInsertion: idx === -1,
183
+ formattingOptions: { insertSpaces: true, tabSize: 2 }
184
+ });
185
+ const updated = applyEdits(raw, edits);
186
+ writeFileSync(tasksPath, updated.endsWith("\n") ? updated : `${updated}
187
+ `);
188
+ return { createdDir, createdFile };
189
+ }
190
+ function cleanupAgentTask(worktreePath, label, created) {
191
+ const zedDir = join(worktreePath, ".zed");
192
+ const tasksPath = join(zedDir, "tasks.json");
193
+ if (existsSync(tasksPath)) {
194
+ try {
195
+ if (created.createdFile) {
196
+ const next = removeTask(readTasks(tasksPath), label);
197
+ if (next.length === 0) {
198
+ rmSync(tasksPath, { force: true });
199
+ } else {
200
+ writeFileSync(tasksPath, `${JSON.stringify(next, null, 2)}
201
+ `);
202
+ }
203
+ } else {
204
+ const raw = readFileSync(tasksPath, "utf8");
205
+ const tasks = parseTasks(raw);
206
+ const idx = tasks?.findIndex((t) => t.label === label) ?? -1;
207
+ if (idx !== -1) {
208
+ const edits = modify(raw, [idx], void 0, {});
209
+ const updated = applyEdits(raw, edits);
210
+ writeFileSync(
211
+ tasksPath,
212
+ updated.endsWith("\n") ? updated : `${updated}
213
+ `
214
+ );
215
+ }
216
+ }
217
+ } catch {
218
+ }
219
+ }
220
+ if (created.createdDir && existsSync(zedDir)) {
221
+ try {
222
+ if (readdirSync(zedDir).length === 0) {
223
+ rmSync(zedDir, { recursive: true, force: true });
224
+ }
225
+ } catch {
226
+ }
227
+ }
228
+ }
229
+ function defaultKeymapPath() {
230
+ return join(homedir(), ".config", "zed", "keymap.json");
231
+ }
232
+ function ensureKeymap(chord, label, keymapPath = defaultKeymapPath()) {
233
+ const binding = buildKeymapBinding(chord, label);
234
+ if (!existsSync(keymapPath)) {
235
+ mkdirSync(dirname(keymapPath), { recursive: true });
236
+ writeFileSync(keymapPath, `${JSON.stringify([binding], null, 2)}
237
+ `);
238
+ return true;
239
+ }
240
+ const raw = readFileSync(keymapPath, "utf8");
241
+ const errors = [];
242
+ const parsed = parse(raw, errors, {
243
+ allowTrailingComma: true,
244
+ disallowComments: false
245
+ });
246
+ if (errors.length > 0 || !Array.isArray(parsed)) {
247
+ process.stderr.write(
248
+ `
249
+ Warning: could not parse ${keymapPath}; not modifying it. Add this binding manually:
250
+ ${JSON.stringify(binding, null, 2)}
251
+ `
252
+ );
253
+ return false;
254
+ }
255
+ const keymap = parsed;
256
+ if (upsertKeymapBinding(keymap, chord, label) === keymap) {
257
+ return true;
258
+ }
259
+ if (hasConflictingChord(keymap, chord, label)) {
260
+ process.stderr.write(
261
+ `
262
+ Note: "${chord}" is already bound in ${keymapPath}; wt's agent binding will take precedence.
263
+ `
264
+ );
265
+ }
266
+ const edits = modify(raw, [keymap.length], binding, {
267
+ isArrayInsertion: true,
268
+ formattingOptions: { insertSpaces: true, tabSize: 2 }
269
+ });
270
+ const updated = applyEdits(raw, edits);
271
+ writeFileSync(keymapPath, updated.endsWith("\n") ? updated : `${updated}
272
+ `);
273
+ return true;
274
+ }
275
+ var ACCESSIBILITY_SETTINGS_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
276
+ function defaultRunner(script) {
277
+ return new Promise((resolve) => {
278
+ const child = spawn("osascript", ["-e", script], {
279
+ stdio: ["ignore", "ignore", "pipe"]
280
+ });
281
+ let stderr = "";
282
+ child.stderr?.on("data", (d) => {
283
+ stderr += d.toString();
284
+ });
285
+ child.on("error", (err) => resolve({ code: null, stderr: err.message }));
286
+ child.on("close", (code) => resolve({ code, stderr }));
287
+ });
288
+ }
289
+ function isAccessibilityError(stderr) {
290
+ return /\b1002\b/.test(stderr) || /-1719\b/.test(stderr) || /not allowed to send keystrokes/i.test(stderr) || /not allowed assistive access/i.test(stderr);
291
+ }
292
+ async function triggerChord(chord, opts = {}) {
293
+ if (process.platform !== "darwin") {
294
+ return { ok: false, reason: "unsupported" };
295
+ }
296
+ const { runner = defaultRunner, loadDelay, activateDelay } = opts;
297
+ const { code, stderr } = await runner(
298
+ buildOsascript(chord, { loadDelay, activateDelay })
299
+ );
300
+ if (code === 0) return { ok: true };
301
+ if (isAccessibilityError(stderr)) {
302
+ return { ok: false, reason: "accessibility", message: stderr.trim() };
303
+ }
304
+ return {
305
+ ok: false,
306
+ reason: "error",
307
+ message: stderr.trim() || `osascript exited with code ${code}`
308
+ };
309
+ }
310
+ function defaultOpen(url) {
311
+ const child = spawn("open", [url], { detached: true, stdio: "ignore" });
312
+ child.on("error", () => {
313
+ });
314
+ child.unref();
315
+ }
316
+ function openAccessibilitySettings(open = defaultOpen) {
317
+ if (process.platform !== "darwin") return;
318
+ open(ACCESSIBILITY_SETTINGS_URL);
319
+ }
320
+
321
+ // src/commands/agent.ts
322
+ var CLEANUP_DELAY_MS = 2e4;
323
+ var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
324
+ async function createAgentWorktree(branch, planPrompt, options = {}) {
325
+ const prepared = await prepareWorktree(branch, options);
326
+ if (!prepared) return;
327
+ const { status, config, worktreePath } = prepared;
328
+ if (status === "exists") {
329
+ const prompt = options.existingWorktreePrompt ?? promptExistingWorktree;
330
+ const action = await prompt(worktreePath, { allowAgent: true });
331
+ if (action === "quit") return;
332
+ if (action === "open") {
333
+ await openConfiguredIde(config, worktreePath);
334
+ return;
335
+ }
336
+ }
337
+ await startAgentInWorktree(config, worktreePath, planPrompt);
338
+ }
339
+ async function startAgentInWorktree(config, worktreePath, planPrompt) {
340
+ if (config.ide !== "zed") {
341
+ console.warn(
342
+ pc.yellow(
343
+ `\u26A0 Agent auto-start requires Zed (ide is "${config.ide}"). Opening without starting the agent.`
344
+ )
345
+ );
346
+ await openConfiguredIde(config, worktreePath);
347
+ return;
348
+ }
349
+ if (!config.agent_command) {
350
+ console.error(
351
+ pc.red("No agent_command configured. Set it with `wt config`.")
352
+ );
353
+ await openConfiguredIde(config, worktreePath);
354
+ return;
355
+ }
356
+ const task = buildAgentTask(
357
+ config.agent_command,
358
+ planPrompt,
359
+ AGENT_TASK_LABEL
360
+ );
361
+ const created = writeAgentTask(worktreePath, task);
362
+ const keymapOk = ensureKeymap(config.agent_trigger_chord, AGENT_TASK_LABEL);
363
+ const opened = await openConfiguredIde(config, worktreePath);
364
+ if (!opened) {
365
+ console.error(pc.red("\u2717 Could not open Zed."));
366
+ cleanupAgentTask(worktreePath, AGENT_TASK_LABEL, created);
367
+ return;
368
+ }
369
+ if (!keymapOk) {
370
+ console.warn(
371
+ pc.yellow(
372
+ `\u26A0 Could not install the Zed keybinding (see above). In Zed, press ${config.agent_trigger_chord} to start the agent manually.`
373
+ )
374
+ );
375
+ return;
376
+ }
377
+ console.log(pc.dim("Starting agent in Zed\u2026"));
378
+ let result = await triggerChord(config.agent_trigger_chord);
379
+ while (!result.ok && result.reason === "accessibility" && process.stdin.isTTY) {
380
+ console.warn(
381
+ pc.yellow(
382
+ "\u26A0 macOS Accessibility permission is required to send the keystroke that starts the agent."
383
+ )
384
+ );
385
+ openAccessibilitySettings();
386
+ const proceed = await clack.confirm({
387
+ message: "Grant Accessibility to the app running wt (e.g. Zed) in the panel that opened, then confirm to retry. (If that app was already running, you may need to quit and reopen it for the grant to take effect.)"
388
+ });
389
+ if (clack.isCancel(proceed) || !proceed) break;
390
+ result = await triggerChord(config.agent_trigger_chord, {
391
+ loadDelay: 0,
392
+ activateDelay: 0.5
393
+ });
394
+ }
395
+ if (!result.ok) {
396
+ reportTriggerFailure(result, config.agent_trigger_chord);
397
+ return;
398
+ }
399
+ console.log(pc.green("\u2713 Agent started"));
400
+ await delay(CLEANUP_DELAY_MS);
401
+ cleanupAgentTask(worktreePath, AGENT_TASK_LABEL, created);
402
+ }
403
+ function reportTriggerFailure(result, chord) {
404
+ if (result.reason === "unsupported") {
405
+ console.warn(
406
+ pc.yellow(
407
+ `\u26A0 Agent auto-start is only supported on macOS. The worktree and Zed are open; press ${chord} in Zed to start the agent.`
408
+ )
409
+ );
410
+ return;
411
+ }
412
+ if (result.reason === "accessibility") {
413
+ console.warn(
414
+ pc.yellow(
415
+ `\u26A0 Agent not started (Accessibility not granted). In Zed, press ${chord} to start it manually.`
416
+ )
417
+ );
418
+ return;
419
+ }
420
+ console.warn(
421
+ pc.yellow(
422
+ `\u26A0 Could not auto-start the agent${result.message ? `: ${result.message}` : ""}. In Zed, press ${chord} to start it manually.`
423
+ )
424
+ );
425
+ }
426
+ export {
427
+ createAgentWorktree
428
+ };
@@ -2,18 +2,20 @@
2
2
  import {
3
3
  addWorktree,
4
4
  branchExists,
5
+ fetchRemote,
5
6
  getRegisteredRepos,
6
7
  getRepoRoot,
8
+ listWorktrees,
7
9
  openIde,
8
10
  registerRepo,
9
11
  resolveWorktreePath,
10
12
  runBranchInput,
11
13
  runRepoPicker
12
- } from "./chunk-25JVZOCI.js";
14
+ } from "./chunk-FSARWOCW.js";
13
15
  import {
14
16
  createStore,
15
17
  getEffectiveConfig
16
- } from "./chunk-HIGP6FSR.js";
18
+ } from "./chunk-IQWENLPW.js";
17
19
 
18
20
  // src/commands/create.ts
19
21
  import { existsSync } from "fs";
@@ -48,7 +50,7 @@ function runCommand(command, cwd) {
48
50
  }
49
51
 
50
52
  // src/commands/create.ts
51
- async function createWorktree(branch, options = {}) {
53
+ async function prepareWorktree(branch, options = {}) {
52
54
  const {
53
55
  cwd = process.cwd(),
54
56
  store = createStore(),
@@ -66,7 +68,7 @@ async function createWorktree(branch, options = {}) {
66
68
  "No repos registered. cd into a repo and run wt create to get started."
67
69
  )
68
70
  );
69
- return;
71
+ return null;
70
72
  }
71
73
  if (!process.stdin.isTTY) {
72
74
  console.error(
@@ -77,21 +79,21 @@ async function createWorktree(branch, options = {}) {
77
79
  process.exit(1);
78
80
  }
79
81
  const picked = await repoPicker(repos);
80
- if (!picked) return;
82
+ if (!picked) return null;
81
83
  repoRoot = picked;
82
84
  if (!branch) {
83
85
  const entered = await branchInput(repoRoot);
84
- if (!entered) return;
86
+ if (!entered) return null;
85
87
  branch = entered;
86
88
  }
87
89
  }
88
- if (!repoRoot) return;
90
+ if (!repoRoot) return null;
89
91
  if (!branch) {
90
92
  const input = await clack.text({
91
93
  message: "Branch name:",
92
- validate: (v) => v.length === 0 ? "Required" : void 0
94
+ validate: (v) => !v || v.length === 0 ? "Required" : void 0
93
95
  });
94
- if (clack.isCancel(input)) return;
96
+ if (clack.isCancel(input)) return null;
95
97
  branch = input;
96
98
  }
97
99
  registerRepo(repoRoot, store);
@@ -102,7 +104,31 @@ async function createWorktree(branch, options = {}) {
102
104
  branch
103
105
  );
104
106
  if (existsSync(worktreePath)) {
105
- throw new Error(`Worktree path already exists: ${worktreePath}`);
107
+ const isWorktree = listWorktrees(repoRoot).some(
108
+ (wt) => wt.path === worktreePath
109
+ );
110
+ if (!isWorktree) {
111
+ console.error(
112
+ pc.red(
113
+ `Path already exists but is not a git worktree: ${worktreePath}`
114
+ )
115
+ );
116
+ process.exit(1);
117
+ }
118
+ return { status: "exists", repoRoot, worktreePath, config };
119
+ }
120
+ const parts = config.base_branch.split("/", 2);
121
+ if (parts.length === 2) {
122
+ const remote = parts[0];
123
+ try {
124
+ fetchRemote(repoRoot, remote);
125
+ } catch (err) {
126
+ console.warn(
127
+ pc.yellow(
128
+ `\u26A0 Could not fetch from ${remote} \u2014 using local state${err instanceof Error ? ` (${err.message})` : ""}`
129
+ )
130
+ );
131
+ }
106
132
  }
107
133
  const exists = branchExists(repoRoot, branch);
108
134
  if (exists) {
@@ -124,17 +150,47 @@ async function createWorktree(branch, options = {}) {
124
150
  process.exit(1);
125
151
  }
126
152
  }
127
- if (config.ide) {
128
- const opened = await openIde(
129
- config.ide,
130
- config.ide_open_args,
131
- worktreePath
132
- );
133
- if (opened) {
134
- console.log(pc.green(`\u2713 Opened ${config.ide}`));
135
- }
153
+ return { status: "created", repoRoot, worktreePath, config };
154
+ }
155
+ async function promptExistingWorktree(worktreePath, opts) {
156
+ if (!process.stdin.isTTY) {
157
+ console.error(pc.red(`Worktree path already exists: ${worktreePath}`));
158
+ process.exit(1);
136
159
  }
160
+ const choice = await clack.select({
161
+ message: `Worktree already exists at ${worktreePath}.`,
162
+ options: [
163
+ { value: "open", label: "Open in IDE" },
164
+ ...opts.allowAgent ? [{ value: "agent", label: "Open and start agent" }] : [],
165
+ { value: "quit", label: "Ignore and quit" }
166
+ ]
167
+ });
168
+ if (clack.isCancel(choice)) return "quit";
169
+ return choice;
137
170
  }
171
+ async function openConfiguredIde(config, worktreePath) {
172
+ if (!config.ide) return false;
173
+ const opened = await openIde(config.ide, config.ide_open_args, worktreePath);
174
+ if (opened) {
175
+ console.log(pc.green(`\u2713 Opened ${config.ide}`));
176
+ }
177
+ return opened;
178
+ }
179
+ async function createWorktree(branch, options = {}) {
180
+ const prepared = await prepareWorktree(branch, options);
181
+ if (!prepared) return;
182
+ const { status, config, worktreePath } = prepared;
183
+ if (status === "exists") {
184
+ const prompt = options.existingWorktreePrompt ?? promptExistingWorktree;
185
+ const action = await prompt(worktreePath, { allowAgent: false });
186
+ if (action !== "open") return;
187
+ }
188
+ await openConfiguredIde(config, worktreePath);
189
+ }
190
+
138
191
  export {
192
+ prepareWorktree,
193
+ promptExistingWorktree,
194
+ openConfiguredIde,
139
195
  createWorktree
140
196
  };
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  createStore,
4
4
  getGlobalConfig
5
- } from "./chunk-HIGP6FSR.js";
5
+ } from "./chunk-IQWENLPW.js";
6
6
 
7
7
  // src/lib/git.ts
8
8
  import { execFileSync } from "child_process";
9
- import { realpathSync } from "fs";
9
+ import { existsSync, realpathSync, rmSync } from "fs";
10
10
  import path from "path";
11
11
  function getRepoRoot(cwd = process.cwd()) {
12
12
  try {
@@ -75,11 +75,22 @@ function addWorktree(repoRoot, worktreePath, branch, baseBranch) {
75
75
  }
76
76
  }
77
77
  function removeWorktree(repoRoot, worktreePath, force = false) {
78
- execFileSync(
79
- "git",
80
- ["worktree", "remove", ...force ? ["--force"] : [], worktreePath],
81
- { cwd: repoRoot, stdio: "pipe" }
82
- );
78
+ try {
79
+ execFileSync(
80
+ "git",
81
+ ["worktree", "remove", ...force ? ["--force"] : [], worktreePath],
82
+ { cwd: repoRoot, stdio: "pipe" }
83
+ );
84
+ } catch (err) {
85
+ if (!force) throw err;
86
+ if (existsSync(worktreePath)) {
87
+ rmSync(worktreePath, { recursive: true, force: true });
88
+ }
89
+ execFileSync("git", ["worktree", "prune"], {
90
+ cwd: repoRoot,
91
+ stdio: "pipe"
92
+ });
93
+ }
83
94
  }
84
95
  function listWorktreeDirtyFiles(worktreePath) {
85
96
  try {
@@ -113,6 +124,13 @@ function branchExists(repoRoot, branch) {
113
124
  return false;
114
125
  }
115
126
  }
127
+ function fetchRemote(repoRoot, remote = "origin") {
128
+ execFileSync("git", ["fetch", remote], {
129
+ cwd: repoRoot,
130
+ stdio: "pipe",
131
+ timeout: 3e4
132
+ });
133
+ }
116
134
  function resolveWorktreePath(repoRoot, worktreePath, branch) {
117
135
  const repoName = path.basename(repoRoot);
118
136
  const safeBranch = branch.replace(/\//g, "-");
@@ -480,6 +498,7 @@ export {
480
498
  removeWorktree,
481
499
  listWorktreeDirtyFiles,
482
500
  branchExists,
501
+ fetchRemote,
483
502
  resolveWorktreePath,
484
503
  openIde,
485
504
  registerRepo,
@@ -8,6 +8,8 @@ var DEFAULT_CONFIG = {
8
8
  setup_commands: [],
9
9
  ide: "zed",
10
10
  ide_open_args: ["-n"],
11
+ agent_command: "claude --permission-mode plan",
12
+ agent_trigger_chord: "ctrl-shift-cmd-c",
11
13
  repos: [],
12
14
  repo_overrides: {}
13
15
  };
package/dist/cli.js CHANGED
@@ -3,16 +3,29 @@
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
5
  var program = new Command();
6
- program.name("wt").description("Git worktree manager").version("0.1.0").action(async () => {
7
- const { runList } = await import("./list-ZKJZEMCV.js");
6
+ program.name("wt").description("Git worktree manager").version("0.2.0").action(async () => {
7
+ const { runList } = await import("./list-7UDMSJOS.js");
8
8
  await runList();
9
9
  });
10
10
  program.command("create [branch]").description("Create a new worktree").action(async (branch) => {
11
- const { createWorktree } = await import("./create-FV5XL2ON.js");
11
+ const { createWorktree } = await import("./create-5J5NGGGM.js");
12
12
  await createWorktree(branch);
13
13
  });
14
- program.command("config").description("Open the config file in $EDITOR").action(async () => {
15
- const { openConfig } = await import("./config-FRLXDMRY.js");
16
- openConfig();
14
+ program.command("agent <branch> <plan_prompt>").description("Create a worktree and auto-start an AI agent in Zed (macOS)").action(async (branch, planPrompt) => {
15
+ const { createAgentWorktree } = await import("./agent-PKCZHGN7.js");
16
+ await createAgentWorktree(branch, planPrompt);
17
+ });
18
+ program.command("config").description("Open the config file in $EDITOR").option("--path", "Print the config file path and exit").action(async (options) => {
19
+ if (options.path) {
20
+ const { printConfigPath } = await import("./config-OSNYQCZL.js");
21
+ printConfigPath();
22
+ } else {
23
+ const { openConfig } = await import("./config-OSNYQCZL.js");
24
+ openConfig();
25
+ }
26
+ });
27
+ program.command("skill").description("Print the wt skill file to stdout").action(async () => {
28
+ const { printSkill } = await import("./skill-RR6MNLWH.js");
29
+ printSkill();
17
30
  });
18
31
  await program.parseAsync(process.argv);
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createStore
4
- } from "./chunk-HIGP6FSR.js";
4
+ } from "./chunk-IQWENLPW.js";
5
5
 
6
6
  // src/commands/config.ts
7
7
  import { spawn } from "child_process";
8
8
  function getConfigPath(store = createStore()) {
9
9
  return store.path;
10
10
  }
11
+ function printConfigPath(store = createStore()) {
12
+ console.log(store.path);
13
+ }
11
14
  function openConfig(store = createStore()) {
12
15
  const configPath = store.path;
13
16
  console.log(`Config: ${configPath}`);
@@ -21,5 +24,6 @@ function openConfig(store = createStore()) {
21
24
  }
22
25
  export {
23
26
  getConfigPath,
24
- openConfig
27
+ openConfig,
28
+ printConfigPath
25
29
  };
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createWorktree,
4
+ openConfiguredIde,
5
+ prepareWorktree,
6
+ promptExistingWorktree
7
+ } from "./chunk-BYWCGKUP.js";
8
+ import "./chunk-FSARWOCW.js";
9
+ import "./chunk-IQWENLPW.js";
10
+ export {
11
+ createWorktree,
12
+ openConfiguredIde,
13
+ prepareWorktree,
14
+ promptExistingWorktree
15
+ };
@@ -8,11 +8,11 @@ import {
8
8
  registerRepo,
9
9
  removeWorktree,
10
10
  runInteractiveList
11
- } from "./chunk-25JVZOCI.js";
11
+ } from "./chunk-FSARWOCW.js";
12
12
  import {
13
13
  createStore,
14
14
  getEffectiveConfig
15
- } from "./chunk-HIGP6FSR.js";
15
+ } from "./chunk-IQWENLPW.js";
16
16
 
17
17
  // src/commands/list.ts
18
18
  import * as clack from "@clack/prompts";
@@ -93,7 +93,7 @@ ${dirty.map((f) => ` ${f}`).join("\n")}`
93
93
  },
94
94
  onCreate: async () => {
95
95
  if (repoRoot) {
96
- const { createWorktree } = await import("./create-FV5XL2ON.js");
96
+ const { createWorktree } = await import("./create-5J5NGGGM.js");
97
97
  await createWorktree(void 0, { cwd: repoRoot, store });
98
98
  }
99
99
  }
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/skill.ts
4
+ function printSkill() {
5
+ console.log('---\nname: wt-worktree-manager\ndescription: Use the wt CLI to create, browse, open, and delete git worktrees across repos. Use when the user asks to manage worktrees, create isolated branches, or configure worktree defaults.\n---\n\n# wt \u2014 Git Worktree Manager\n\n`wt` is a CLI for managing git worktrees. It provides an interactive TUI to browse, create, open in your IDE, and delete worktrees across multiple repos.\n\n## Commands\n\n### `wt` (no subcommand)\n\nLaunch the interactive TUI. Shows worktrees for the current repo (repo mode) or all registered repos (global mode, when run outside a repo).\n\n**Keybindings in the TUI:**\n\n- Arrow keys / `j`/`k` \u2014 navigate\n- `Enter` \u2014 open worktree in IDE\n- `d` \u2014 delete worktree\n- `c` \u2014 create new worktree (repo mode only)\n- `/` \u2014 search\n- `q` / `Esc` \u2014 quit\n\n### `wt create [branch]`\n\nCreate a new worktree. If `branch` is omitted, prompts interactively.\n\nThe worktree is created as a sibling directory to the repo: `<parent>/<repo-name>-<branch-name>`.\n\nAfter creation, `wt` runs any configured `setup_commands` and opens the worktree in your IDE.\n\nIf the worktree path already exists, `wt create` doesn\'t error \u2014 it prompts you\nto **open it in the IDE** or **quit**. (In a non-interactive shell it errors\nwith a non-zero exit instead of prompting.)\n\n### `wt agent <branch> <plan_prompt>`\n\nCreate a worktree (same as `wt create`) **and** auto-start an AI agent in Zed\'s\nintegrated terminal, pre-filled with `<plan_prompt>` and left interactive for\nyou to take over.\n\n```bash\nwt agent feature/login \'Read the codebase, then propose a plan for login.\'\n```\n\nIt writes a temporary `.zed/tasks.json` running\n`<agent_command> \'<plan_prompt>\'`, ensures a global Zed keymap chord\n(`agent_trigger_chord`) spawns that task, opens Zed, presses the chord via\n`osascript`, then removes the temporary task so the repo is left clean.\n\n**macOS + Zed only.** Requires Accessibility permission for the app that runs\n`wt` (Zed itself, when run from its integrated terminal). If it isn\'t granted,\n`wt agent` opens the *Privacy & Security \u2192 Accessibility* settings pane and waits\nfor you to grant it and confirm, then retries automatically. On other platforms\n(or when `ide` is not `zed`) the worktree is still created and opened, but the\nagent is not auto-started.\n\nIf the worktree path already exists, `wt agent` prompts you to **open it in the\nIDE**, **open it and start the agent**, or **quit** \u2014 instead of erroring. (In a\nnon-interactive shell it errors with a non-zero exit instead of prompting.)\n\n### `wt config`\n\nOpen the global config file in `$EDITOR` (defaults to `nano`).\n\n```bash\nwt config # open in editor\nwt config --path # print config file path only\n```\n\n### `wt skill`\n\nPrint this skill file to stdout. Useful for piping to agents or copying to a project.\n\n## Configuration\n\nConfig is stored as JSON. Get the path with `wt config --path`.\n\n### Schema\n\n| Key | Type | Default | Description |\n| ---------------- | ---------- | --------------- | ------------------------------------------------------------------------- |\n| `worktree_path` | `string` | `"../"` | Where to place new worktrees, relative to the repo root |\n| `base_branch` | `string` | `"origin/main"` | Branch to base new worktrees on |\n| `setup_commands` | `string[]` | `[]` | Commands to run in a new worktree after creation (e.g. `["npm install"]`) |\n| `ide` | `string` | `"zed"` | IDE command to open worktrees with |\n| `ide_open_args` | `string[]` | `["-n"]` | Arguments passed to the IDE command |\n| `agent_command` | `string` | `"claude --permission-mode plan"` | Command `wt agent` runs in Zed; `<plan_prompt>` is appended single-quoted |\n| `agent_trigger_chord` | `string` | `"ctrl-shift-cmd-c"` | Zed keymap chord `wt agent` installs/presses to spawn the agent task |\n| `repos` | `string[]` | `[]` | Registered repo paths (auto-populated on first use) |\n| `repo_overrides` | `object` | `{}` | Per-repo config overrides (see below) |\n\n### Per-repo overrides\n\nOverride any field (`worktree_path`, `base_branch`, `setup_commands`, `ide`, `ide_open_args`, `agent_command`, `agent_trigger_chord`) for a specific repo:\n\n```json\n{\n "base_branch": "origin/main",\n "ide": "zed",\n "repo_overrides": {\n "/path/to/my-repo": {\n "base_branch": "origin/develop",\n "setup_commands": ["npm install", "npm run build"]\n }\n }\n}\n```\n\n## Common workflows\n\n### Create a worktree for a new feature\n\n```bash\ncd /path/to/repo\nwt create feature/my-branch\n```\n\n### Configure setup commands for a repo\n\n```bash\nwt config\n# Then add to the JSON:\n# "repo_overrides": {\n# "/path/to/repo": {\n# "setup_commands": ["npm install"]\n# }\n# }\n```\n\n### Browse all worktrees across repos\n\nRun `wt` from any directory outside a git repo to see worktrees from all registered repos.\n');
6
+ }
7
+ export {
8
+ printSkill
9
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cestoliv/wt",
3
- "version": "0.1.0",
4
- "description": "Git worktree manager",
3
+ "version": "0.2.0",
4
+ "description": "Fast, interactive TUI to browse, create, open, and delete git worktrees across repos.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/cestoliv/worktrees.git"
@@ -14,7 +14,8 @@
14
14
  "wt": "./dist/cli.js"
15
15
  },
16
16
  "files": [
17
- "dist"
17
+ "dist",
18
+ "SKILL.md"
18
19
  ],
19
20
  "publishConfig": {
20
21
  "access": "public"
@@ -25,6 +26,7 @@
25
26
  "prepublishOnly": "npm run build",
26
27
  "test": "vitest run",
27
28
  "test:watch": "vitest",
29
+ "typecheck": "tsc --noEmit",
28
30
  "lint": "biome check .",
29
31
  "format": "biome format --write ."
30
32
  },
@@ -33,6 +35,7 @@
33
35
  "commander": "^14.0.3",
34
36
  "conf": "^15.1.0",
35
37
  "fuse.js": "^7.1.0",
38
+ "jsonc-parser": "^3.3.1",
36
39
  "picocolors": "^1.1.1"
37
40
  },
38
41
  "devDependencies": {