pi-zellij 0.3.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/CHANGELOG.md +20 -0
- package/README.md +296 -0
- package/extensions/git-core.ts +184 -0
- package/extensions/index.ts +16 -0
- package/extensions/templates/handoff-same-checkout.md +1 -0
- package/extensions/templates/handoff-worktree.md +1 -0
- package/extensions/zv-continue.ts +449 -0
- package/extensions/zv-core.ts +136 -0
- package/extensions/zv-notify.ts +303 -0
- package/extensions/zv-open.ts +293 -0
- package/extensions/zv-review.ts +137 -0
- package/extensions/zv-split.ts +87 -0
- package/extensions/zv-zoxide.ts +148 -0
- package/install.mjs +149 -0
- package/package.json +55 -0
- package/prompts/review-diff.md +8 -0
- package/prompts/review.md +8 -0
- package/skills/code-review/SKILL.md +81 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Initial `pi-zellij` release with zellij-powered pane workflows for Pi.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Clarified README notification wording to match the current `zv-notify` behavior: `Waiting`, `Task Complete`, and `Error`.
|
|
12
|
+
- Added `zv-notify` for desktop notifications using `osascript` on macOS or `notify-send` on Linux.
|
|
13
|
+
- Added `/zv`, `/zj`, and `/zt` to open a new zellij pane or tab and start a fresh Pi session in the same working directory.
|
|
14
|
+
- Added `/zo` and `/zoh` to open a new pane and run any shell command there.
|
|
15
|
+
- Added configurable floating app commands via `pi-zellij.commands` in Pi `settings.json`, including shorthand entries such as `"zh": "hx"` and `"zg": "lazygit"`, plus object entries with `acceptArgs` support.
|
|
16
|
+
- Added compatibility fallback for legacy `pi-zv.commands` settings during the rename to `pi-zellij`.
|
|
17
|
+
- Reserved Pi built-in slash commands such as `/settings`, `/model`, and `/reload` so configured floating commands cannot shadow them.
|
|
18
|
+
- Added `/zz` and `/zzh` to open a new pane from a zoxide match or direct directory path and start Pi there.
|
|
19
|
+
- Added `zv-review` with `/zrv` and `/zrh`, plus bundled `code-review` skill and `/review` / `/review-diff` prompt templates for focused review workflows, including GitHub pull request review via `gh` when given a PR URL.
|
|
20
|
+
- Added `zv-continue` with `/zcv` and `/zch` for split-based task handoff in the current checkout or by creating a git worktree branch with `-c <branch>`.
|
package/README.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# pi-zellij
|
|
2
|
+
|
|
3
|
+
Pi package with zellij-powered terminal integrations for [Pi](https://pi.dev).
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
[Pi](https://pi.dev) works well in the terminal, but pane orchestration is better handled by a terminal multiplexer. `pi-zellij` adds zellij-native split workflows for Pi, plus optional desktop notifications.
|
|
8
|
+
|
|
9
|
+
It includes split and tab commands, generic tool launchers, settings-driven floating app shortcuts, zoxide jumps, review workflows, split-based task handoff, and automatic run notifications.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Install with pi:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pi install npm:pi-zellij
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or with the installer:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx pi-zellij
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If pi is already running, use:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
/reload
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
- `zellij` must be installed
|
|
34
|
+
- pane, tab, and floating commands must be run from inside an active zellij session
|
|
35
|
+
- `zoxide` is required for the zoxide commands
|
|
36
|
+
- notifications use:
|
|
37
|
+
- `osascript` on macOS
|
|
38
|
+
- `notify-send` on Linux
|
|
39
|
+
|
|
40
|
+
## Feature overview
|
|
41
|
+
|
|
42
|
+
### Pane and tab workflows
|
|
43
|
+
|
|
44
|
+
- `/zv`, `/zj`, `/zt`
|
|
45
|
+
- start a fresh Pi session in a new right pane, lower pane, or tab
|
|
46
|
+
- `/zo <command...>`, `/zoh <command...>`
|
|
47
|
+
- run any shell command in a new right pane or lower pane
|
|
48
|
+
- `/zz <query>`, `/zzh <query>`
|
|
49
|
+
- jump to a zoxide match or direct directory path and start Pi there
|
|
50
|
+
|
|
51
|
+
### Floating tools
|
|
52
|
+
|
|
53
|
+
- `pi-zellij.commands` in `settings.json`
|
|
54
|
+
- registers floating app shortcuts such as `/zh` for `hx` or `/zg` for `lazygit`
|
|
55
|
+
|
|
56
|
+
### Review and handoff workflows
|
|
57
|
+
|
|
58
|
+
- `/zcv`, `/zch`
|
|
59
|
+
- open continuation sessions in a split, optionally in a new git worktree
|
|
60
|
+
- `/review <target>`, `/review-diff [focus-or-pr-url]`
|
|
61
|
+
- expand bundled review prompts in the current pane
|
|
62
|
+
- `/zrv`, `/zrh`, plus review flags
|
|
63
|
+
- open review-focused Pi sessions in a split
|
|
64
|
+
- `/skill:code-review`
|
|
65
|
+
- loads the bundled structured review skill for files, directories, diffs, and PRs
|
|
66
|
+
|
|
67
|
+
### Notifications
|
|
68
|
+
|
|
69
|
+
- automatic via `zv-notify`
|
|
70
|
+
- sends desktop notifications for Pi run states such as `Waiting`, `Task Complete`, and `Error`
|
|
71
|
+
|
|
72
|
+
## Bundled extensions and resources
|
|
73
|
+
|
|
74
|
+
Extensions:
|
|
75
|
+
- `zv-notify`
|
|
76
|
+
- `zv-split`
|
|
77
|
+
- `zv-open`
|
|
78
|
+
- `zv-zoxide`
|
|
79
|
+
- `zv-review`
|
|
80
|
+
- `zv-continue`
|
|
81
|
+
|
|
82
|
+
Other bundled resources:
|
|
83
|
+
- `code-review` skill
|
|
84
|
+
- `/review` prompt template
|
|
85
|
+
- `/review-diff` prompt template
|
|
86
|
+
|
|
87
|
+
## Commands
|
|
88
|
+
|
|
89
|
+
### Split and tab commands
|
|
90
|
+
|
|
91
|
+
- `/zv`
|
|
92
|
+
- opens a new pane to the right
|
|
93
|
+
- starts a fresh `pi` session in the same `cwd`
|
|
94
|
+
- `/zj`
|
|
95
|
+
- opens a new pane below
|
|
96
|
+
- starts a fresh `pi` session in the same `cwd`
|
|
97
|
+
- `/zt`
|
|
98
|
+
- opens a new zellij tab
|
|
99
|
+
- starts a fresh `pi` session in the same `cwd`
|
|
100
|
+
|
|
101
|
+
All three commands also accept optional initial prompt text.
|
|
102
|
+
|
|
103
|
+
Examples:
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
/zv Review the auth flow in this repo
|
|
107
|
+
/zt Investigate flaky tests in this repo
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Tool split commands
|
|
111
|
+
|
|
112
|
+
- `/zo <command...>`
|
|
113
|
+
- opens a new pane to the right
|
|
114
|
+
- runs the given shell command in the same `cwd`
|
|
115
|
+
- `/zoh <command...>`
|
|
116
|
+
- opens a new pane below
|
|
117
|
+
- runs the given shell command in the same `cwd`
|
|
118
|
+
|
|
119
|
+
Examples:
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
/zo hx
|
|
123
|
+
/zo npm test
|
|
124
|
+
/zoh npm run dev
|
|
125
|
+
/zo watch -n 1 git status --short
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Commands are executed via `sh -lc` in the current project directory.
|
|
129
|
+
|
|
130
|
+
### Configured floating commands
|
|
131
|
+
|
|
132
|
+
You can register your own floating app shortcuts in Pi's main settings file under `pi-zellij.commands`.
|
|
133
|
+
|
|
134
|
+
Supported locations:
|
|
135
|
+
- `~/.pi/agent/settings.json` for global commands
|
|
136
|
+
- `.pi/settings.json` for project-local commands
|
|
137
|
+
|
|
138
|
+
During the rename from `pi-zv` to `pi-zellij`, legacy `pi-zv.commands` is still accepted for compatibility. If both keys exist, `pi-zellij.commands` wins.
|
|
139
|
+
|
|
140
|
+
Simple form:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"pi-zellij": {
|
|
145
|
+
"commands": {
|
|
146
|
+
"zh": "hx",
|
|
147
|
+
"zg": "lazygit"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Each configured command opens in a floating zellij pane using a default `90%` by `90%` popup with `5%` margins.
|
|
154
|
+
|
|
155
|
+
Examples:
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
/zh
|
|
159
|
+
/zg
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
For commands that should accept extra arguments, use the object form:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"pi-zellij": {
|
|
167
|
+
"commands": {
|
|
168
|
+
"zh": {
|
|
169
|
+
"run": "hx",
|
|
170
|
+
"acceptArgs": true,
|
|
171
|
+
"description": "Open Helix in a floating pane"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Then you can pass arguments through to the configured command:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
/zh src/auth.ts
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Configured command names cannot reuse built-in Pi commands such as `/settings`, `/model`, or `/reload`, and they also cannot replace pi-zellij's own slash commands such as `/zv`, `/zj`, `/zt`, `/zz`, or `/zcv`.
|
|
185
|
+
|
|
186
|
+
If the same command exists in both global and project settings, the project setting wins. After changing settings, run `/reload` in Pi.
|
|
187
|
+
|
|
188
|
+
### Zoxide jump commands
|
|
189
|
+
|
|
190
|
+
- `/zz <query>`
|
|
191
|
+
- resolves the query with `zoxide query`
|
|
192
|
+
- opens a new pane to the right
|
|
193
|
+
- starts a fresh pi session in the matched directory
|
|
194
|
+
- `/zzh <query>`
|
|
195
|
+
- resolves the query with `zoxide query`
|
|
196
|
+
- opens a new pane below
|
|
197
|
+
- starts a fresh pi session in the matched directory
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
/zz mono
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
If the argument is already a valid directory path, `/zz` and `/zzh` use it directly instead of querying zoxide.
|
|
206
|
+
|
|
207
|
+
### Continuation and worktree helpers
|
|
208
|
+
|
|
209
|
+
- `/zcv`
|
|
210
|
+
- opens a new pane to the right
|
|
211
|
+
- creates a related handoff session in the current checkout
|
|
212
|
+
- `/zch`
|
|
213
|
+
- opens a new pane below
|
|
214
|
+
- creates a related handoff session in the current checkout
|
|
215
|
+
- `/zcv <note>` / `/zch <note>`
|
|
216
|
+
- same as above, but adds a focus note to the handoff context
|
|
217
|
+
- `/zcv -c <branch>` / `/zch -c <branch>`
|
|
218
|
+
- creates a new branch worktree from the current `HEAD`, then opens a new pane there
|
|
219
|
+
- `/zcv -c <branch> --from <ref>` / `/zch -c <branch> --from <ref>`
|
|
220
|
+
- creates a new branch worktree from an explicit base ref such as `main` or `origin/main`
|
|
221
|
+
- `/zcv -c <branch> [--from <ref>] <note...>` / `/zch -c <branch> [--from <ref>] <note...>`
|
|
222
|
+
- same as above, but also adds a focus note to the worktree handoff
|
|
223
|
+
|
|
224
|
+
Examples:
|
|
225
|
+
|
|
226
|
+
```text
|
|
227
|
+
/zcv
|
|
228
|
+
/zcv focus on tests
|
|
229
|
+
/zcv -c fix/notify-bug
|
|
230
|
+
/zcv -c fix/notify-bug --from main
|
|
231
|
+
/zcv -c fix/notify-bug --from main review the existing changes first
|
|
232
|
+
/zch -c feature/review-ui focus on edge cases
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Review helpers
|
|
236
|
+
|
|
237
|
+
`pi-zellij` also bundles a reusable `code-review` skill plus prompt templates for in-place review:
|
|
238
|
+
|
|
239
|
+
- `/review <target>`
|
|
240
|
+
- prompt template for reviewing a file, directory, or GitHub pull request URL in the current pane
|
|
241
|
+
- `/review-diff [focus-or-pr-url]`
|
|
242
|
+
- prompt template for reviewing the current git diff in the current pane, or a GitHub pull request URL via `gh`
|
|
243
|
+
|
|
244
|
+
Split review commands:
|
|
245
|
+
|
|
246
|
+
- `/zrv`
|
|
247
|
+
- with no arguments, reviews the current git diff in a new right pane
|
|
248
|
+
- `/zrh`
|
|
249
|
+
- with no arguments, reviews the current git diff in a new lower pane
|
|
250
|
+
- `/zrv [--bugs|--refactor|--tests] <target>` or `/zrv --diff [focus]`
|
|
251
|
+
- opens a new pane to the right
|
|
252
|
+
- starts a fresh pi review session in the same `cwd`
|
|
253
|
+
- `/zrh [--bugs|--refactor|--tests] <target>` or `/zrh --diff [focus]`
|
|
254
|
+
- opens a new pane below
|
|
255
|
+
- starts a fresh pi review session in the same `cwd`
|
|
256
|
+
|
|
257
|
+
`--diff` is the default, so `/zrv` and `/zrh` usually do not need the flag.
|
|
258
|
+
|
|
259
|
+
There are no `/review-v` or `/review-h` aliases in `pi-zellij`, so it can coexist more cleanly with other Pi packages.
|
|
260
|
+
|
|
261
|
+
Examples:
|
|
262
|
+
|
|
263
|
+
```text
|
|
264
|
+
/zrv
|
|
265
|
+
/zrh
|
|
266
|
+
/zrv src/auth.ts
|
|
267
|
+
/zrv --bugs src/auth.ts
|
|
268
|
+
/zrh --refactor src/auth/
|
|
269
|
+
/zrv --diff
|
|
270
|
+
/zrh --diff focus on token refresh and retries
|
|
271
|
+
/zrv https://github.com/owner/repo/pull/123
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
If the target is a GitHub pull request URL, the review workflow switches to PR review and instructs pi to inspect the pull request with `gh pr view` and `gh pr diff`.
|
|
275
|
+
|
|
276
|
+
## Notifications
|
|
277
|
+
|
|
278
|
+
The bundled `zv-notify` extension summarizes each run and sends a desktop notification when supported by the host system.
|
|
279
|
+
|
|
280
|
+
Current notification types:
|
|
281
|
+
- `Waiting`
|
|
282
|
+
- `Task Complete`
|
|
283
|
+
- `Error`
|
|
284
|
+
|
|
285
|
+
You can control notification noise with one setting:
|
|
286
|
+
- `PI_ZV_NOTIFY_LEVEL=all` - `Waiting`, `Task Complete`, and `Error`
|
|
287
|
+
- `PI_ZV_NOTIFY_LEVEL=medium` - `Task Complete` and `Error`
|
|
288
|
+
- `PI_ZV_NOTIFY_LEVEL=low` - `Error` only
|
|
289
|
+
- `PI_ZV_NOTIFY_LEVEL=disabled` - disable notifications
|
|
290
|
+
|
|
291
|
+
## Environment variables
|
|
292
|
+
|
|
293
|
+
- `PI_ZV_NOTIFY_LEVEL` - notification level: `all`, `medium`, `low`, or `disabled` (default: `all`)
|
|
294
|
+
- `PI_ZV_NOTIFY_THRESHOLD_MS` - duration threshold before a run is labeled `Task Complete` instead of `Waiting` (default: `15000`)
|
|
295
|
+
- `PI_ZV_NOTIFY_DEBOUNCE_MS` - minimum delay between duplicate notifications (default: `3000`)
|
|
296
|
+
- `PI_ZV_NOTIFY_TITLE` - notification title override (default: `Pi`)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
4
|
+
|
|
5
|
+
const GIT_TIMEOUT_MS = 10000;
|
|
6
|
+
const DEFAULT_MAX_STATUS_LINES = 20;
|
|
7
|
+
|
|
8
|
+
export interface GitExecResult {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface GitRepoInfo {
|
|
16
|
+
repoRoot: string;
|
|
17
|
+
branch?: string;
|
|
18
|
+
statusLines: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface GitWorktreeInfo {
|
|
22
|
+
path: string;
|
|
23
|
+
branch?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function execGit(pi: ExtensionAPI, cwd: string, args: string[]): Promise<GitExecResult> {
|
|
27
|
+
const result = await pi.exec("git", args, { timeout: GIT_TIMEOUT_MS, cwd });
|
|
28
|
+
if (result.killed) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
stdout: result.stdout,
|
|
32
|
+
stderr: result.stderr,
|
|
33
|
+
error: "git command timed out",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (result.code !== 0) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
stdout: result.stdout,
|
|
40
|
+
stderr: result.stderr,
|
|
41
|
+
error: result.stderr.trim() || result.stdout.trim() || `git exited with code ${result.code}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
ok: true,
|
|
46
|
+
stdout: result.stdout,
|
|
47
|
+
stderr: result.stderr,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function getGitRepoInfo(pi: ExtensionAPI, cwd: string, maxStatusLines: number = DEFAULT_MAX_STATUS_LINES): Promise<GitRepoInfo | undefined> {
|
|
52
|
+
const rootResult = await execGit(pi, cwd, ["rev-parse", "--show-toplevel"]);
|
|
53
|
+
if (!rootResult.ok) return undefined;
|
|
54
|
+
const repoRoot = rootResult.stdout.trim();
|
|
55
|
+
if (!repoRoot) return undefined;
|
|
56
|
+
|
|
57
|
+
const branchResult = await execGit(pi, cwd, ["branch", "--show-current"]);
|
|
58
|
+
const statusResult = await execGit(pi, cwd, ["status", "--short", "--untracked-files=all"]);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
repoRoot,
|
|
62
|
+
branch: branchResult.ok ? branchResult.stdout.trim() || undefined : undefined,
|
|
63
|
+
statusLines: statusResult.ok
|
|
64
|
+
? statusResult.stdout
|
|
65
|
+
.split("\n")
|
|
66
|
+
.map((line) => line.trimEnd())
|
|
67
|
+
.filter((line) => line.trim().length > 0)
|
|
68
|
+
.slice(0, maxStatusLines)
|
|
69
|
+
: [],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseWorktreeList(text: string): GitWorktreeInfo[] {
|
|
74
|
+
const worktrees: GitWorktreeInfo[] = [];
|
|
75
|
+
let current: GitWorktreeInfo | undefined;
|
|
76
|
+
|
|
77
|
+
for (const rawLine of text.split("\n")) {
|
|
78
|
+
const line = rawLine.trimEnd();
|
|
79
|
+
if (line.startsWith("worktree ")) {
|
|
80
|
+
if (current) worktrees.push(current);
|
|
81
|
+
current = { path: line.slice("worktree ".length).trim() };
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!current) continue;
|
|
85
|
+
if (line.startsWith("branch ")) {
|
|
86
|
+
current.branch = line.slice("branch ".length).trim().replace(/^refs\/heads\//, "") || undefined;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (line.length === 0) {
|
|
90
|
+
worktrees.push(current);
|
|
91
|
+
current = undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (current) worktrees.push(current);
|
|
96
|
+
return worktrees;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function slugifyBranchName(branch: string): string {
|
|
100
|
+
return branch
|
|
101
|
+
.trim()
|
|
102
|
+
.replace(/[^A-Za-z0-9._-]+/g, "-")
|
|
103
|
+
.replace(/-+/g, "-")
|
|
104
|
+
.replace(/^-|-$/g, "") || "worktree";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function branchExists(pi: ExtensionAPI, repoRoot: string, branch: string): Promise<boolean> {
|
|
108
|
+
const result = await pi.exec("git", ["show-ref", "--verify", "--", `refs/heads/${branch}`], { timeout: GIT_TIMEOUT_MS, cwd: repoRoot });
|
|
109
|
+
return !result.killed && result.code === 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function resolveWorktreePath(
|
|
113
|
+
pi: ExtensionAPI,
|
|
114
|
+
repoRoot: string,
|
|
115
|
+
branch: string,
|
|
116
|
+
): Promise<{ ok: true; path: string; reused: boolean } | { ok: false; error: string }> {
|
|
117
|
+
const listResult = await execGit(pi, repoRoot, ["worktree", "list", "--porcelain"]);
|
|
118
|
+
if (!listResult.ok) {
|
|
119
|
+
return { ok: false, error: listResult.error || "Failed to list git worktrees" };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const existing = parseWorktreeList(listResult.stdout).find((worktree) => worktree.branch === branch);
|
|
123
|
+
if (existing) {
|
|
124
|
+
return { ok: true, path: existing.path, reused: true };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const worktreeRoot = join(dirname(repoRoot), `${basename(repoRoot)}-worktrees`);
|
|
128
|
+
const targetPath = join(worktreeRoot, slugifyBranchName(branch));
|
|
129
|
+
if (existsSync(targetPath)) {
|
|
130
|
+
return { ok: false, error: `Worktree path already exists and cannot be reused: ${targetPath}` };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
mkdirSync(worktreeRoot, { recursive: true });
|
|
134
|
+
return { ok: true, path: targetPath, reused: false };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function ensureExistingBranchWorktree(
|
|
138
|
+
pi: ExtensionAPI,
|
|
139
|
+
repoRoot: string,
|
|
140
|
+
branch: string,
|
|
141
|
+
): Promise<{ ok: true; path: string; reused: boolean } | { ok: false; error: string }> {
|
|
142
|
+
if (!(await branchExists(pi, repoRoot, branch))) {
|
|
143
|
+
return { ok: false, error: `Local branch does not exist: ${branch}` };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const pathResult = await resolveWorktreePath(pi, repoRoot, branch);
|
|
147
|
+
if (!pathResult.ok) return pathResult;
|
|
148
|
+
if (pathResult.reused) return pathResult;
|
|
149
|
+
|
|
150
|
+
const addResult = await execGit(pi, repoRoot, ["worktree", "add", pathResult.path, branch]);
|
|
151
|
+
if (!addResult.ok) {
|
|
152
|
+
return { ok: false, error: addResult.error || `Failed to create worktree for branch ${branch}` };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return pathResult;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function ensureCreatedBranchWorktree(
|
|
159
|
+
pi: ExtensionAPI,
|
|
160
|
+
repoRoot: string,
|
|
161
|
+
branch: string,
|
|
162
|
+
fromRef?: string,
|
|
163
|
+
): Promise<{ ok: true; path: string } | { ok: false; error: string }> {
|
|
164
|
+
if (await branchExists(pi, repoRoot, branch)) {
|
|
165
|
+
return { ok: false, error: `Branch already exists: ${branch}` };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const pathResult = await resolveWorktreePath(pi, repoRoot, branch);
|
|
169
|
+
if (!pathResult.ok) return pathResult;
|
|
170
|
+
if (pathResult.reused) {
|
|
171
|
+
return { ok: true, path: pathResult.path };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const args = ["worktree", "add", "-b", branch, pathResult.path];
|
|
175
|
+
if (fromRef?.trim()) {
|
|
176
|
+
args.push(fromRef.trim());
|
|
177
|
+
}
|
|
178
|
+
const addResult = await execGit(pi, repoRoot, args);
|
|
179
|
+
if (!addResult.ok) {
|
|
180
|
+
return { ok: false, error: addResult.error || `Failed to create branch worktree ${branch}` };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { ok: true, path: pathResult.path };
|
|
184
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import zvNotifyExtension from "./zv-notify.ts";
|
|
3
|
+
import zvSplitExtension from "./zv-split.ts";
|
|
4
|
+
import zvZoxideExtension from "./zv-zoxide.ts";
|
|
5
|
+
import zvReviewExtension from "./zv-review.ts";
|
|
6
|
+
import zvContinueExtension from "./zv-continue.ts";
|
|
7
|
+
import zvOpenExtension from "./zv-open.ts";
|
|
8
|
+
|
|
9
|
+
export default function piZellijExtensionBundle(pi: ExtensionAPI) {
|
|
10
|
+
zvNotifyExtension(pi);
|
|
11
|
+
zvSplitExtension(pi);
|
|
12
|
+
zvZoxideExtension(pi);
|
|
13
|
+
zvReviewExtension(pi);
|
|
14
|
+
zvContinueExtension(pi);
|
|
15
|
+
zvOpenExtension(pi);
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Continue the current task from this new pane.{{FOCUS_NOTE_SENTENCE}} Use the inherited session history and the handoff summary already present in this session. Start with the highest-priority next step.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Continue the current task in this git worktree for branch {{TARGET_BRANCH}}.{{FOCUS_NOTE_SENTENCE}} Use the handoff summary already present in this session. First confirm the branch and repository state in this worktree, then proceed with the highest-priority next step.
|