@zenobius/pi-worktrees 0.2.0 → 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.
- package/README.md +79 -9
- package/dist/cmds/shared.d.ts +7 -0
- package/dist/index.js +237 -23
- package/dist/services/completions.d.ts +4 -0
- package/dist/services/config/config.d.ts +42 -0
- package/dist/services/config/migrations/04-oncreate-display-output-max-lines.d.ts +2 -0
- package/dist/services/config/migrations/05-oncreate-command-display-format.d.ts +2 -0
- package/dist/services/config/schema.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# `@zenobius/pi-worktrees`
|
|
2
2
|
|
|
3
|
+
<img width="1531" height="1172" alt="image" src="https://github.com/user-attachments/assets/33fe4c01-4d9b-41ec-a326-116db6e750df" />
|
|
4
|
+
|
|
5
|
+
|
|
3
6
|
Git worktree management for [Pi Coding Agent](https://github.com/badlogic/pi-mono) with a clean `/worktree` command surface.
|
|
4
7
|
|
|
5
8
|
This extension helps you spin up isolated feature workspaces quickly, with safety checks and optional post-create automation.
|
|
@@ -44,6 +47,28 @@ If Pi is already running, use `/reload` to load newly installed extensions.
|
|
|
44
47
|
|
|
45
48
|
---
|
|
46
49
|
|
|
50
|
+
## Getting started in 2 minutes
|
|
51
|
+
|
|
52
|
+
1. Install the extension:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pi install npm:@zenobius/pi-worktrees
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
2. Open a git repo in Pi and run:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
/worktree init
|
|
62
|
+
/worktree create auth-refactor
|
|
63
|
+
/worktree list
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
3. Optional: jump into it from your shell using the printed path:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
/worktree cd auth-refactor
|
|
70
|
+
```
|
|
71
|
+
|
|
47
72
|
## Quick start
|
|
48
73
|
|
|
49
74
|
In Pi:
|
|
@@ -58,11 +83,9 @@ In Pi:
|
|
|
58
83
|
/worktree prune
|
|
59
84
|
```
|
|
60
85
|
|
|
61
|
-
### How I use `/
|
|
62
|
-
|
|
63
|
-
Since I use nvim and zellij, i want the worktrees I create to be clones of the workspace
|
|
64
|
-
I have for the current one, so to this end, my `onCreate` looks like:
|
|
86
|
+
### Example: How I use `/worktree`
|
|
65
87
|
|
|
88
|
+
I use Neovim and Zellij, and I want each new worktree to boot a ready-to-code workspace. My `onCreate` looks like:
|
|
66
89
|
```json
|
|
67
90
|
{
|
|
68
91
|
"worktrees": {
|
|
@@ -78,12 +101,9 @@ I have for the current one, so to this end, my `onCreate` looks like:
|
|
|
78
101
|
}
|
|
79
102
|
}
|
|
80
103
|
}
|
|
81
|
-
|
|
82
104
|
```
|
|
83
105
|
|
|
84
|
-
This
|
|
85
|
-
in it on the new worktree path.
|
|
86
|
-
|
|
106
|
+
This creates a new Zellij tab with Neovim and Pi running in the new worktree path.
|
|
87
107
|
---
|
|
88
108
|
|
|
89
109
|
## Command reference
|
|
@@ -92,7 +112,7 @@ in it on the new worktree path.
|
|
|
92
112
|
|---|---|
|
|
93
113
|
| `/worktree init` | Interactive setup for extension settings |
|
|
94
114
|
| `/worktree settings` | Show all current settings |
|
|
95
|
-
| `/worktree settings <key>` | Get one setting (`worktreeRoot`, `onCreate`) |
|
|
115
|
+
| `/worktree settings <key>` | Get one setting (`worktreeRoot`, `parentDir` alias, `onCreate`) |
|
|
96
116
|
| `/worktree settings <key> <value>` | Set one setting |
|
|
97
117
|
| `/worktree create <feature-name>` | Create a new worktree + branch `feature/<feature-name>` |
|
|
98
118
|
| `/worktree list` | List all worktrees (`/worktree ls` alias) |
|
|
@@ -121,6 +141,13 @@ Settings live in `~/.pi/agent/pi-worktrees-settings.json`.
|
|
|
121
141
|
}
|
|
122
142
|
},
|
|
123
143
|
"matchingStrategy": "fail-on-tie",
|
|
144
|
+
"onCreateDisplayOutputMaxLines": 5,
|
|
145
|
+
"onCreateCmdDisplayPending": "[ ] {{cmd}}",
|
|
146
|
+
"onCreateCmdDisplaySuccess": "[x] {{cmd}}",
|
|
147
|
+
"onCreateCmdDisplayError": "[ ] {{cmd}} [ERROR]",
|
|
148
|
+
"onCreateCmdDisplayPendingColor": "dim",
|
|
149
|
+
"onCreateCmdDisplaySuccessColor": "success",
|
|
150
|
+
"onCreateCmdDisplayErrorColor": "error",
|
|
124
151
|
"worktree": {
|
|
125
152
|
"worktreeRoot": "~/.local/share/worktrees/{{project}}",
|
|
126
153
|
"onCreate": "mise setup"
|
|
@@ -128,6 +155,21 @@ Settings live in `~/.pi/agent/pi-worktrees-settings.json`.
|
|
|
128
155
|
}
|
|
129
156
|
```
|
|
130
157
|
|
|
158
|
+
### Configuration reference
|
|
159
|
+
|
|
160
|
+
| Key | Type | Default | Description |
|
|
161
|
+
|---|---|---|---|
|
|
162
|
+
| `worktrees` | `Record<string, WorktreeSettings>` | `{}` | Pattern-matched settings by repo URL or glob. |
|
|
163
|
+
| `matchingStrategy` | `'fail-on-tie' \| 'first-wins' \| 'last-wins'` | `fail-on-tie` | Tie-break behavior for equally specific patterns. |
|
|
164
|
+
| `onCreateDisplayOutputMaxLines` | `number` (integer, `>= 0`) | `5` | Number of latest stdout/stderr lines shown in live UI updates during `onCreate`. |
|
|
165
|
+
| `onCreateCmdDisplayPending` | `string` | `[ ] {{cmd}}` | Template for pending/running command display lines. |
|
|
166
|
+
| `onCreateCmdDisplaySuccess` | `string` | `[x] {{cmd}}` | Template for successful command display lines. |
|
|
167
|
+
| `onCreateCmdDisplayError` | `string` | `[ ] {{cmd}} [ERROR]` | Template for failed command display lines. |
|
|
168
|
+
| `onCreateCmdDisplayPendingColor` | `string` | `dim` | Pi theme color name for pending/running command lines. |
|
|
169
|
+
| `onCreateCmdDisplaySuccessColor` | `string` | `success` | Pi theme color name for successful command lines. |
|
|
170
|
+
| `onCreateCmdDisplayErrorColor` | `string` | `error` | Pi theme color name for failed command lines. |
|
|
171
|
+
| `worktree` (legacy) | `WorktreeSettings` | n/a | Legacy fallback shape; migrated automatically. |
|
|
172
|
+
|
|
131
173
|
### Matching model
|
|
132
174
|
|
|
133
175
|
For the current repository, settings are resolved in this order:
|
|
@@ -151,6 +193,34 @@ For the current repository, settings are resolved in this order:
|
|
|
151
193
|
|
|
152
194
|
When an array is used, commands run sequentially and stop on first failure.
|
|
153
195
|
|
|
196
|
+
### `onCreateDisplayOutputMaxLines`
|
|
197
|
+
|
|
198
|
+
Controls only live UI output verbosity for `onCreate` command execution.
|
|
199
|
+
- **Default**: `5`
|
|
200
|
+
- **Scope**: display only
|
|
201
|
+
- **Does not affect**: logfile contents (full stdout/stderr is still logged)
|
|
202
|
+
|
|
203
|
+
### `onCreate` command line display templates
|
|
204
|
+
|
|
205
|
+
These templates control how each command line is rendered in the live progress list.
|
|
206
|
+
|
|
207
|
+
- `onCreateCmdDisplayPending` (default: `[ ] {{cmd}}`)
|
|
208
|
+
- `onCreateCmdDisplaySuccess` (default: `[x] {{cmd}}`)
|
|
209
|
+
- `onCreateCmdDisplayError` (default: `[ ] {{cmd}} [ERROR]`)
|
|
210
|
+
|
|
211
|
+
Supported token:
|
|
212
|
+
- `{{cmd}}` (or `{cmd}`) → expanded command string
|
|
213
|
+
|
|
214
|
+
### `onCreate` command line display colors
|
|
215
|
+
|
|
216
|
+
These settings use Pi theme color names:
|
|
217
|
+
|
|
218
|
+
- `onCreateCmdDisplayPendingColor` (default: `dim`)
|
|
219
|
+
- `onCreateCmdDisplaySuccessColor` (default: `success`)
|
|
220
|
+
- `onCreateCmdDisplayErrorColor` (default: `error`)
|
|
221
|
+
|
|
222
|
+
Supported color names in this extension: `dim`, `accent`, `info`, `success`, `warning`, `error`.
|
|
223
|
+
|
|
154
224
|
### `worktreeRoot`
|
|
155
225
|
|
|
156
226
|
Where new worktrees are created.
|
package/dist/cmds/shared.d.ts
CHANGED
|
@@ -11,6 +11,13 @@ export interface OnCreateResult {
|
|
|
11
11
|
}
|
|
12
12
|
export interface OnCreateHookOptions {
|
|
13
13
|
logPath?: string;
|
|
14
|
+
displayOutputMaxLines?: number;
|
|
15
|
+
cmdDisplayPending?: string;
|
|
16
|
+
cmdDisplaySuccess?: string;
|
|
17
|
+
cmdDisplayError?: string;
|
|
18
|
+
cmdDisplayPendingColor?: string;
|
|
19
|
+
cmdDisplaySuccessColor?: string;
|
|
20
|
+
cmdDisplayErrorColor?: string;
|
|
14
21
|
}
|
|
15
22
|
/**
|
|
16
23
|
* Runs post-create hooks sequentially.
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,8 @@ import {
|
|
|
21
21
|
Optional,
|
|
22
22
|
Record as TypeRecord,
|
|
23
23
|
String as TypeString,
|
|
24
|
-
Union
|
|
24
|
+
Union,
|
|
25
|
+
Integer as TypeInteger
|
|
25
26
|
} from "typebox";
|
|
26
27
|
var OnCreateSchema = Union([TypeString(), TypeArray(TypeString())]);
|
|
27
28
|
var WorktreeSettingsSchema = TypeObject({
|
|
@@ -40,10 +41,24 @@ var MatchingStrategySchema = Union([
|
|
|
40
41
|
var MatchStrategyResultSchema = Union([Literal("exact"), Literal("unmatched")]);
|
|
41
42
|
var WorktreesMapSchema = TypeRecord(TypeString(), WorktreeSettingsSchema);
|
|
42
43
|
var LogfileSchema = TypeString();
|
|
44
|
+
var OnCreateDisplayOutputMaxLinesSchema = TypeInteger({ minimum: 0 });
|
|
45
|
+
var OnCreateCmdDisplayPendingSchema = TypeString();
|
|
46
|
+
var OnCreateCmdDisplaySuccessSchema = TypeString();
|
|
47
|
+
var OnCreateCmdDisplayErrorSchema = TypeString();
|
|
48
|
+
var OnCreateCmdDisplayPendingColorSchema = TypeString();
|
|
49
|
+
var OnCreateCmdDisplaySuccessColorSchema = TypeString();
|
|
50
|
+
var OnCreateCmdDisplayErrorColorSchema = TypeString();
|
|
43
51
|
var PiWorktreeConfigSchema = TypeObject({
|
|
44
52
|
worktrees: Optional(WorktreesMapSchema),
|
|
45
53
|
matchingStrategy: Optional(MatchingStrategySchema),
|
|
46
|
-
logfile: Optional(LogfileSchema)
|
|
54
|
+
logfile: Optional(LogfileSchema),
|
|
55
|
+
onCreateDisplayOutputMaxLines: Optional(OnCreateDisplayOutputMaxLinesSchema),
|
|
56
|
+
onCreateCmdDisplayPending: Optional(OnCreateCmdDisplayPendingSchema),
|
|
57
|
+
onCreateCmdDisplaySuccess: Optional(OnCreateCmdDisplaySuccessSchema),
|
|
58
|
+
onCreateCmdDisplayError: Optional(OnCreateCmdDisplayErrorSchema),
|
|
59
|
+
onCreateCmdDisplayPendingColor: Optional(OnCreateCmdDisplayPendingColorSchema),
|
|
60
|
+
onCreateCmdDisplaySuccessColor: Optional(OnCreateCmdDisplaySuccessColorSchema),
|
|
61
|
+
onCreateCmdDisplayErrorColor: Optional(OnCreateCmdDisplayErrorColorSchema)
|
|
47
62
|
}, {
|
|
48
63
|
$id: "UnresolvedConfig",
|
|
49
64
|
additionalProperties: true
|
|
@@ -268,8 +283,94 @@ var migration3 = {
|
|
|
268
283
|
}
|
|
269
284
|
};
|
|
270
285
|
|
|
286
|
+
// src/services/config/migrations/04-oncreate-display-output-max-lines.ts
|
|
287
|
+
var DEFAULT_ONCREATE_DISPLAY_OUTPUT_MAX_LINES = 5;
|
|
288
|
+
function toRecord4(value) {
|
|
289
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
290
|
+
return {};
|
|
291
|
+
}
|
|
292
|
+
return { ...value };
|
|
293
|
+
}
|
|
294
|
+
var migration4 = {
|
|
295
|
+
id: "oncreate-display-output-max-lines-default",
|
|
296
|
+
up(config) {
|
|
297
|
+
const record = toRecord4(config);
|
|
298
|
+
if (record.onCreateDisplayOutputMaxLines !== undefined) {
|
|
299
|
+
return record;
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
...record,
|
|
303
|
+
onCreateDisplayOutputMaxLines: DEFAULT_ONCREATE_DISPLAY_OUTPUT_MAX_LINES
|
|
304
|
+
};
|
|
305
|
+
},
|
|
306
|
+
down(config) {
|
|
307
|
+
const record = toRecord4(config);
|
|
308
|
+
const next = { ...record };
|
|
309
|
+
delete next.onCreateDisplayOutputMaxLines;
|
|
310
|
+
return next;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// src/services/config/migrations/05-oncreate-command-display-format.ts
|
|
315
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_PENDING = "[ ] {{cmd}}";
|
|
316
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_SUCCESS = "[x] {{cmd}}";
|
|
317
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_ERROR = "[ ] {{cmd}} [ERROR]";
|
|
318
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_PENDING_COLOR = "dim";
|
|
319
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_SUCCESS_COLOR = "success";
|
|
320
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_ERROR_COLOR = "error";
|
|
321
|
+
function toRecord5(value) {
|
|
322
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
323
|
+
return {};
|
|
324
|
+
}
|
|
325
|
+
return { ...value };
|
|
326
|
+
}
|
|
327
|
+
var migration5 = {
|
|
328
|
+
id: "oncreate-command-display-format-defaults",
|
|
329
|
+
up(config) {
|
|
330
|
+
const record = toRecord5(config);
|
|
331
|
+
const next = { ...record };
|
|
332
|
+
if (next.onCreateCmdDisplayPending === undefined) {
|
|
333
|
+
next.onCreateCmdDisplayPending = DEFAULT_ONCREATE_CMD_DISPLAY_PENDING;
|
|
334
|
+
}
|
|
335
|
+
if (next.onCreateCmdDisplaySuccess === undefined) {
|
|
336
|
+
next.onCreateCmdDisplaySuccess = DEFAULT_ONCREATE_CMD_DISPLAY_SUCCESS;
|
|
337
|
+
}
|
|
338
|
+
if (next.onCreateCmdDisplayError === undefined) {
|
|
339
|
+
next.onCreateCmdDisplayError = DEFAULT_ONCREATE_CMD_DISPLAY_ERROR;
|
|
340
|
+
}
|
|
341
|
+
if (next.onCreateCmdDisplayPendingColor === undefined) {
|
|
342
|
+
next.onCreateCmdDisplayPendingColor = DEFAULT_ONCREATE_CMD_DISPLAY_PENDING_COLOR;
|
|
343
|
+
}
|
|
344
|
+
if (next.onCreateCmdDisplaySuccessColor === undefined) {
|
|
345
|
+
next.onCreateCmdDisplaySuccessColor = DEFAULT_ONCREATE_CMD_DISPLAY_SUCCESS_COLOR;
|
|
346
|
+
}
|
|
347
|
+
if (next.onCreateCmdDisplayErrorColor === undefined) {
|
|
348
|
+
next.onCreateCmdDisplayErrorColor = DEFAULT_ONCREATE_CMD_DISPLAY_ERROR_COLOR;
|
|
349
|
+
}
|
|
350
|
+
return next;
|
|
351
|
+
},
|
|
352
|
+
down(config) {
|
|
353
|
+
const record = toRecord5(config);
|
|
354
|
+
const next = { ...record };
|
|
355
|
+
delete next.onCreateCmdDisplayPending;
|
|
356
|
+
delete next.onCreateCmdDisplaySuccess;
|
|
357
|
+
delete next.onCreateCmdDisplayError;
|
|
358
|
+
delete next.onCreateCmdDisplayPendingColor;
|
|
359
|
+
delete next.onCreateCmdDisplaySuccessColor;
|
|
360
|
+
delete next.onCreateCmdDisplayErrorColor;
|
|
361
|
+
return next;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
271
365
|
// src/services/config/config.ts
|
|
272
366
|
var DEFAULT_LOGFILE_TEMPLATE = "/tmp/pi-worktree-{sessionId}-{name}.log";
|
|
367
|
+
var DEFAULT_ONCREATE_DISPLAY_OUTPUT_MAX_LINES2 = 5;
|
|
368
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_PENDING2 = "[ ] {{cmd}}";
|
|
369
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_SUCCESS2 = "[x] {{cmd}}";
|
|
370
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_ERROR2 = "[ ] {{cmd}} [ERROR]";
|
|
371
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_PENDING_COLOR2 = "dim";
|
|
372
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_SUCCESS_COLOR2 = "success";
|
|
373
|
+
var DEFAULT_ONCREATE_CMD_DISPLAY_ERROR_COLOR2 = "error";
|
|
273
374
|
async function createPiWorktreeConfigService() {
|
|
274
375
|
const parse = (value) => {
|
|
275
376
|
return Parse2(PiWorktreeConfigSchema, value);
|
|
@@ -277,7 +378,7 @@ async function createPiWorktreeConfigService() {
|
|
|
277
378
|
const store = await createConfigService("pi-worktrees", {
|
|
278
379
|
defaults: {},
|
|
279
380
|
parse,
|
|
280
|
-
migrations: [migration, migration2, migration3]
|
|
381
|
+
migrations: [migration, migration2, migration3, migration4, migration5]
|
|
281
382
|
});
|
|
282
383
|
await store.reload();
|
|
283
384
|
const save = async (data) => {
|
|
@@ -290,6 +391,27 @@ async function createPiWorktreeConfigService() {
|
|
|
290
391
|
if (data.logfile !== undefined) {
|
|
291
392
|
await store.set("logfile", data.logfile, "home");
|
|
292
393
|
}
|
|
394
|
+
if (data.onCreateDisplayOutputMaxLines !== undefined) {
|
|
395
|
+
await store.set("onCreateDisplayOutputMaxLines", data.onCreateDisplayOutputMaxLines, "home");
|
|
396
|
+
}
|
|
397
|
+
if (data.onCreateCmdDisplayPending !== undefined) {
|
|
398
|
+
await store.set("onCreateCmdDisplayPending", data.onCreateCmdDisplayPending, "home");
|
|
399
|
+
}
|
|
400
|
+
if (data.onCreateCmdDisplaySuccess !== undefined) {
|
|
401
|
+
await store.set("onCreateCmdDisplaySuccess", data.onCreateCmdDisplaySuccess, "home");
|
|
402
|
+
}
|
|
403
|
+
if (data.onCreateCmdDisplayError !== undefined) {
|
|
404
|
+
await store.set("onCreateCmdDisplayError", data.onCreateCmdDisplayError, "home");
|
|
405
|
+
}
|
|
406
|
+
if (data.onCreateCmdDisplayPendingColor !== undefined) {
|
|
407
|
+
await store.set("onCreateCmdDisplayPendingColor", data.onCreateCmdDisplayPendingColor, "home");
|
|
408
|
+
}
|
|
409
|
+
if (data.onCreateCmdDisplaySuccessColor !== undefined) {
|
|
410
|
+
await store.set("onCreateCmdDisplaySuccessColor", data.onCreateCmdDisplaySuccessColor, "home");
|
|
411
|
+
}
|
|
412
|
+
if (data.onCreateCmdDisplayErrorColor !== undefined) {
|
|
413
|
+
await store.set("onCreateCmdDisplayErrorColor", data.onCreateCmdDisplayErrorColor, "home");
|
|
414
|
+
}
|
|
293
415
|
await store.save("home");
|
|
294
416
|
};
|
|
295
417
|
const worktrees = new Map(Object.entries(store.config.worktrees || {}));
|
|
@@ -310,6 +432,13 @@ async function createPiWorktreeConfigService() {
|
|
|
310
432
|
mainWorktree,
|
|
311
433
|
parentDir,
|
|
312
434
|
logfile: store.config.logfile ?? DEFAULT_LOGFILE_TEMPLATE,
|
|
435
|
+
onCreateDisplayOutputMaxLines: store.config.onCreateDisplayOutputMaxLines ?? DEFAULT_ONCREATE_DISPLAY_OUTPUT_MAX_LINES2,
|
|
436
|
+
onCreateCmdDisplayPending: store.config.onCreateCmdDisplayPending ?? DEFAULT_ONCREATE_CMD_DISPLAY_PENDING2,
|
|
437
|
+
onCreateCmdDisplaySuccess: store.config.onCreateCmdDisplaySuccess ?? DEFAULT_ONCREATE_CMD_DISPLAY_SUCCESS2,
|
|
438
|
+
onCreateCmdDisplayError: store.config.onCreateCmdDisplayError ?? DEFAULT_ONCREATE_CMD_DISPLAY_ERROR2,
|
|
439
|
+
onCreateCmdDisplayPendingColor: store.config.onCreateCmdDisplayPendingColor ?? DEFAULT_ONCREATE_CMD_DISPLAY_PENDING_COLOR2,
|
|
440
|
+
onCreateCmdDisplaySuccessColor: store.config.onCreateCmdDisplaySuccessColor ?? DEFAULT_ONCREATE_CMD_DISPLAY_SUCCESS_COLOR2,
|
|
441
|
+
onCreateCmdDisplayErrorColor: store.config.onCreateCmdDisplayErrorColor ?? DEFAULT_ONCREATE_CMD_DISPLAY_ERROR_COLOR2,
|
|
313
442
|
matchedPattern: resolution.matchedPattern
|
|
314
443
|
};
|
|
315
444
|
};
|
|
@@ -622,20 +751,45 @@ var ANSI = {
|
|
|
622
751
|
gray: "\x1B[90m",
|
|
623
752
|
blue: "\x1B[34m",
|
|
624
753
|
green: "\x1B[32m",
|
|
625
|
-
red: "\x1B[31m"
|
|
754
|
+
red: "\x1B[31m",
|
|
755
|
+
yellow: "\x1B[33m"
|
|
626
756
|
};
|
|
627
|
-
function
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
757
|
+
function applyCommandTemplate(template, command) {
|
|
758
|
+
return template.replace(/\{\{cmd\}\}|\{cmd\}/g, command);
|
|
759
|
+
}
|
|
760
|
+
function resolveAnsiColor(colorName) {
|
|
761
|
+
if (colorName === "dim") {
|
|
762
|
+
return ANSI.gray;
|
|
631
763
|
}
|
|
632
|
-
if (
|
|
633
|
-
return
|
|
764
|
+
if (colorName === "accent" || colorName === "info") {
|
|
765
|
+
return ANSI.blue;
|
|
766
|
+
}
|
|
767
|
+
if (colorName === "success") {
|
|
768
|
+
return ANSI.green;
|
|
769
|
+
}
|
|
770
|
+
if (colorName === "error") {
|
|
771
|
+
return ANSI.red;
|
|
772
|
+
}
|
|
773
|
+
if (colorName === "warning") {
|
|
774
|
+
return ANSI.yellow;
|
|
775
|
+
}
|
|
776
|
+
return "";
|
|
777
|
+
}
|
|
778
|
+
function colorize(text, colorName) {
|
|
779
|
+
const ansi = resolveAnsiColor(colorName);
|
|
780
|
+
if (!ansi) {
|
|
781
|
+
return text;
|
|
634
782
|
}
|
|
783
|
+
return `${ansi}${text}${ANSI.reset}`;
|
|
784
|
+
}
|
|
785
|
+
function formatCommandLine(command, state, config) {
|
|
635
786
|
if (state === "success") {
|
|
636
|
-
return
|
|
787
|
+
return colorize(applyCommandTemplate(config.successTemplate, command), config.successColor);
|
|
788
|
+
}
|
|
789
|
+
if (state === "failed") {
|
|
790
|
+
return colorize(applyCommandTemplate(config.errorTemplate, command), config.errorColor);
|
|
637
791
|
}
|
|
638
|
-
return
|
|
792
|
+
return colorize(applyCommandTemplate(config.pendingTemplate, command), config.pendingColor);
|
|
639
793
|
}
|
|
640
794
|
function toLines(text) {
|
|
641
795
|
return text.replace(/\r/g, "").split(`
|
|
@@ -648,15 +802,25 @@ function formatOutputLine(stream, line, state) {
|
|
|
648
802
|
}
|
|
649
803
|
return `${ANSI.gray} ${prefix} ${line}${ANSI.reset}`;
|
|
650
804
|
}
|
|
651
|
-
function
|
|
805
|
+
function getDisplayLines(text, maxLines) {
|
|
806
|
+
const lines = toLines(text);
|
|
807
|
+
if (maxLines < 0) {
|
|
808
|
+
return lines;
|
|
809
|
+
}
|
|
810
|
+
if (maxLines === 0) {
|
|
811
|
+
return [];
|
|
812
|
+
}
|
|
813
|
+
return lines.slice(-maxLines);
|
|
814
|
+
}
|
|
815
|
+
function formatCommandList(commands, states, outputs, commandDisplay, logPath, displayOutputMaxLines = 5) {
|
|
652
816
|
const lines = ["onCreate steps:"];
|
|
653
817
|
for (const [index, command] of commands.entries()) {
|
|
654
818
|
const state = states[index];
|
|
655
|
-
lines.push(formatCommandLine(
|
|
656
|
-
for (const line of
|
|
819
|
+
lines.push(formatCommandLine(command, state, commandDisplay));
|
|
820
|
+
for (const line of getDisplayLines(outputs[index].stdout, displayOutputMaxLines)) {
|
|
657
821
|
lines.push(formatOutputLine("stdout", line, state));
|
|
658
822
|
}
|
|
659
|
-
for (const line of
|
|
823
|
+
for (const line of getDisplayLines(outputs[index].stderr, displayOutputMaxLines)) {
|
|
660
824
|
lines.push(formatOutputLine("stderr", line, state));
|
|
661
825
|
}
|
|
662
826
|
}
|
|
@@ -738,13 +902,22 @@ async function runOnCreateHook(createdCtx, settings, notify, options) {
|
|
|
738
902
|
].join(`
|
|
739
903
|
`));
|
|
740
904
|
}
|
|
741
|
-
|
|
905
|
+
const displayOutputMaxLines = options?.displayOutputMaxLines ?? 5;
|
|
906
|
+
const commandDisplay = {
|
|
907
|
+
pendingTemplate: options?.cmdDisplayPending ?? "[ ] {{cmd}}",
|
|
908
|
+
successTemplate: options?.cmdDisplaySuccess ?? "[x] {{cmd}}",
|
|
909
|
+
errorTemplate: options?.cmdDisplayError ?? "[ ] {{cmd}} [ERROR]",
|
|
910
|
+
pendingColor: options?.cmdDisplayPendingColor ?? "dim",
|
|
911
|
+
successColor: options?.cmdDisplaySuccessColor ?? "success",
|
|
912
|
+
errorColor: options?.cmdDisplayErrorColor ?? "error"
|
|
913
|
+
};
|
|
914
|
+
notify(formatCommandList(commands, commandStates, commandOutputs, commandDisplay, undefined, displayOutputMaxLines), "info");
|
|
742
915
|
for (const [index, command] of commands.entries()) {
|
|
743
916
|
commandStates[index] = "running";
|
|
744
|
-
notify(formatCommandList(commands, commandStates, commandOutputs), "info");
|
|
917
|
+
notify(formatCommandList(commands, commandStates, commandOutputs, commandDisplay, undefined, displayOutputMaxLines), "info");
|
|
745
918
|
const result = await runCommand(command, createdCtx.path, (stream, chunk) => {
|
|
746
919
|
commandOutputs[index][stream] += chunk;
|
|
747
|
-
notify(formatCommandList(commands, commandStates, commandOutputs), "info");
|
|
920
|
+
notify(formatCommandList(commands, commandStates, commandOutputs, commandDisplay, undefined, displayOutputMaxLines), "info");
|
|
748
921
|
});
|
|
749
922
|
if (options?.logPath) {
|
|
750
923
|
appendCommandLog(options.logPath, command, result);
|
|
@@ -752,7 +925,7 @@ async function runOnCreateHook(createdCtx, settings, notify, options) {
|
|
|
752
925
|
executed.push(command);
|
|
753
926
|
if (!result.success) {
|
|
754
927
|
commandStates[index] = "failed";
|
|
755
|
-
notify(formatCommandList(commands, commandStates, commandOutputs, options?.logPath), "error");
|
|
928
|
+
notify(formatCommandList(commands, commandStates, commandOutputs, commandDisplay, options?.logPath, displayOutputMaxLines), "error");
|
|
756
929
|
notify(`onCreate failed (exit ${result.code}): ${result.stderr.slice(0, 200)}${options?.logPath ? `
|
|
757
930
|
log: ${options.logPath}` : ""}`, "error");
|
|
758
931
|
return {
|
|
@@ -766,9 +939,9 @@ log: ${options.logPath}` : ""}`, "error");
|
|
|
766
939
|
};
|
|
767
940
|
}
|
|
768
941
|
commandStates[index] = "success";
|
|
769
|
-
notify(formatCommandList(commands, commandStates, commandOutputs), "info");
|
|
942
|
+
notify(formatCommandList(commands, commandStates, commandOutputs, commandDisplay, undefined, displayOutputMaxLines), "info");
|
|
770
943
|
}
|
|
771
|
-
notify(formatCommandList(commands, commandStates, commandOutputs, options?.logPath), "info");
|
|
944
|
+
notify(formatCommandList(commands, commandStates, commandOutputs, commandDisplay, options?.logPath, displayOutputMaxLines), "info");
|
|
772
945
|
return { success: true, executed };
|
|
773
946
|
}
|
|
774
947
|
|
|
@@ -824,7 +997,16 @@ async function cmdCreate(args, ctx, deps) {
|
|
|
824
997
|
name: safeName,
|
|
825
998
|
timestamp
|
|
826
999
|
});
|
|
827
|
-
await runOnCreateHook(createdCtx, current, ctx.ui.notify.bind(ctx.ui), {
|
|
1000
|
+
await runOnCreateHook(createdCtx, current, ctx.ui.notify.bind(ctx.ui), {
|
|
1001
|
+
logPath,
|
|
1002
|
+
displayOutputMaxLines: current.onCreateDisplayOutputMaxLines,
|
|
1003
|
+
cmdDisplayPending: current.onCreateCmdDisplayPending,
|
|
1004
|
+
cmdDisplaySuccess: current.onCreateCmdDisplaySuccess,
|
|
1005
|
+
cmdDisplayError: current.onCreateCmdDisplayError,
|
|
1006
|
+
cmdDisplayPendingColor: current.onCreateCmdDisplayPendingColor,
|
|
1007
|
+
cmdDisplaySuccessColor: current.onCreateCmdDisplaySuccessColor,
|
|
1008
|
+
cmdDisplayErrorColor: current.onCreateCmdDisplayErrorColor
|
|
1009
|
+
});
|
|
828
1010
|
}
|
|
829
1011
|
|
|
830
1012
|
// src/cmds/cmdInit.ts
|
|
@@ -1237,6 +1419,27 @@ async function cmdTemplates(_args, ctx, deps) {
|
|
|
1237
1419
|
`), "info");
|
|
1238
1420
|
}
|
|
1239
1421
|
|
|
1422
|
+
// src/services/completions.ts
|
|
1423
|
+
function toItem(command) {
|
|
1424
|
+
return {
|
|
1425
|
+
value: command,
|
|
1426
|
+
label: command
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
function createCompletionFactory(commands) {
|
|
1430
|
+
const commandNames = Object.keys(commands).sort();
|
|
1431
|
+
return (argumentPrefix) => {
|
|
1432
|
+
const prefix = argumentPrefix.trimStart();
|
|
1433
|
+
if (prefix.includes(" ")) {
|
|
1434
|
+
return null;
|
|
1435
|
+
}
|
|
1436
|
+
if (!prefix) {
|
|
1437
|
+
return commandNames.map(toItem);
|
|
1438
|
+
}
|
|
1439
|
+
return commandNames.filter((command) => command.startsWith(prefix)).map(toItem);
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1240
1443
|
// src/index.ts
|
|
1241
1444
|
var HELP_TEXT = `
|
|
1242
1445
|
/worktree - Git worktree management
|
|
@@ -1266,6 +1469,13 @@ Configuration (~/.pi/agent/pi-worktrees-settings.json):
|
|
|
1266
1469
|
},
|
|
1267
1470
|
"matchingStrategy": "fail-on-tie",
|
|
1268
1471
|
"logfile": "/tmp/pi-worktree-{sessionId}-{name}.log",
|
|
1472
|
+
"onCreateDisplayOutputMaxLines": 5,
|
|
1473
|
+
"onCreateCmdDisplayPending": "[ ] {{cmd}}",
|
|
1474
|
+
"onCreateCmdDisplaySuccess": "[x] {{cmd}}",
|
|
1475
|
+
"onCreateCmdDisplayError": "[ ] {{cmd}} [ERROR]",
|
|
1476
|
+
"onCreateCmdDisplayPendingColor": "dim",
|
|
1477
|
+
"onCreateCmdDisplaySuccessColor": "success",
|
|
1478
|
+
"onCreateCmdDisplayErrorColor": "error",
|
|
1269
1479
|
"worktree": {
|
|
1270
1480
|
"worktreeRoot": "~/.worktrees/{{project}}",
|
|
1271
1481
|
"onCreate": "mise setup"
|
|
@@ -1320,8 +1530,12 @@ var PiWorktreeExtension = async function(pi) {
|
|
|
1320
1530
|
ctx.ui.setStatus(`Worktrees`, notification.msg);
|
|
1321
1531
|
}
|
|
1322
1532
|
});
|
|
1533
|
+
const getSubcommandCompletions = createCompletionFactory(commands);
|
|
1323
1534
|
pi.registerCommand("worktree", {
|
|
1324
1535
|
description: "Git worktree management for isolated workspaces",
|
|
1536
|
+
getArgumentCompletions(argumentPrefix) {
|
|
1537
|
+
return getSubcommandCompletions(argumentPrefix);
|
|
1538
|
+
},
|
|
1325
1539
|
handler: async (args, ctx) => {
|
|
1326
1540
|
const [cmd, ...rest] = args.trim().split(/\s+/);
|
|
1327
1541
|
const command = commands[cmd];
|
|
@@ -13,6 +13,13 @@ export declare function createPiWorktreeConfigService(): Promise<{
|
|
|
13
13
|
mainWorktree: string;
|
|
14
14
|
parentDir: string;
|
|
15
15
|
logfile: string;
|
|
16
|
+
onCreateDisplayOutputMaxLines: number;
|
|
17
|
+
onCreateCmdDisplayPending: string;
|
|
18
|
+
onCreateCmdDisplaySuccess: string;
|
|
19
|
+
onCreateCmdDisplayError: string;
|
|
20
|
+
onCreateCmdDisplayPendingColor: string;
|
|
21
|
+
onCreateCmdDisplaySuccessColor: string;
|
|
22
|
+
onCreateCmdDisplayErrorColor: string;
|
|
16
23
|
matchedPattern: string | null;
|
|
17
24
|
onCreate?: string | string[] | undefined;
|
|
18
25
|
worktreeRoot?: string | undefined;
|
|
@@ -27,12 +34,26 @@ export declare function createPiWorktreeConfigService(): Promise<{
|
|
|
27
34
|
}>>>;
|
|
28
35
|
matchingStrategy: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"fail-on-tie">, import("typebox").TLiteral<"first-wins">, import("typebox").TLiteral<"last-wins">]>>;
|
|
29
36
|
logfile: import("typebox").TOptional<import("typebox").TString>;
|
|
37
|
+
onCreateDisplayOutputMaxLines: import("typebox").TOptional<import("typebox").TInteger>;
|
|
38
|
+
onCreateCmdDisplayPending: import("typebox").TOptional<import("typebox").TString>;
|
|
39
|
+
onCreateCmdDisplaySuccess: import("typebox").TOptional<import("typebox").TString>;
|
|
40
|
+
onCreateCmdDisplayError: import("typebox").TOptional<import("typebox").TString>;
|
|
41
|
+
onCreateCmdDisplayPendingColor: import("typebox").TOptional<import("typebox").TString>;
|
|
42
|
+
onCreateCmdDisplaySuccessColor: import("typebox").TOptional<import("typebox").TString>;
|
|
43
|
+
onCreateCmdDisplayErrorColor: import("typebox").TOptional<import("typebox").TString>;
|
|
30
44
|
}, "^.*$", import("typebox").TObject<{
|
|
31
45
|
worktreeRoot: import("typebox").TOptional<import("typebox").TString>;
|
|
32
46
|
parentDir: import("typebox").TOptional<import("typebox").TString>;
|
|
33
47
|
onCreate: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TString, import("typebox").TArray<import("typebox").TString>]>>;
|
|
34
48
|
}>> | undefined;
|
|
35
49
|
logfile?: string | undefined;
|
|
50
|
+
onCreateDisplayOutputMaxLines?: number | undefined;
|
|
51
|
+
onCreateCmdDisplayPending?: string | undefined;
|
|
52
|
+
onCreateCmdDisplaySuccess?: string | undefined;
|
|
53
|
+
onCreateCmdDisplayError?: string | undefined;
|
|
54
|
+
onCreateCmdDisplayPendingColor?: string | undefined;
|
|
55
|
+
onCreateCmdDisplaySuccessColor?: string | undefined;
|
|
56
|
+
onCreateCmdDisplayErrorColor?: string | undefined;
|
|
36
57
|
matchingStrategy?: "fail-on-tie" | "first-wins" | "last-wins" | undefined;
|
|
37
58
|
};
|
|
38
59
|
ready: Promise<void>;
|
|
@@ -47,16 +68,37 @@ export declare function createPiWorktreeConfigService(): Promise<{
|
|
|
47
68
|
}>>>;
|
|
48
69
|
matchingStrategy: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"fail-on-tie">, import("typebox").TLiteral<"first-wins">, import("typebox").TLiteral<"last-wins">]>>;
|
|
49
70
|
logfile: import("typebox").TOptional<import("typebox").TString>;
|
|
71
|
+
onCreateDisplayOutputMaxLines: import("typebox").TOptional<import("typebox").TInteger>;
|
|
72
|
+
onCreateCmdDisplayPending: import("typebox").TOptional<import("typebox").TString>;
|
|
73
|
+
onCreateCmdDisplaySuccess: import("typebox").TOptional<import("typebox").TString>;
|
|
74
|
+
onCreateCmdDisplayError: import("typebox").TOptional<import("typebox").TString>;
|
|
75
|
+
onCreateCmdDisplayPendingColor: import("typebox").TOptional<import("typebox").TString>;
|
|
76
|
+
onCreateCmdDisplaySuccessColor: import("typebox").TOptional<import("typebox").TString>;
|
|
77
|
+
onCreateCmdDisplayErrorColor: import("typebox").TOptional<import("typebox").TString>;
|
|
50
78
|
}, "^.*$", import("typebox").TObject<{
|
|
51
79
|
worktreeRoot: import("typebox").TOptional<import("typebox").TString>;
|
|
52
80
|
parentDir: import("typebox").TOptional<import("typebox").TString>;
|
|
53
81
|
onCreate: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TString, import("typebox").TArray<import("typebox").TString>]>>;
|
|
54
82
|
}>> | undefined;
|
|
55
83
|
logfile?: string | undefined;
|
|
84
|
+
onCreateDisplayOutputMaxLines?: number | undefined;
|
|
85
|
+
onCreateCmdDisplayPending?: string | undefined;
|
|
86
|
+
onCreateCmdDisplaySuccess?: string | undefined;
|
|
87
|
+
onCreateCmdDisplayError?: string | undefined;
|
|
88
|
+
onCreateCmdDisplayPendingColor?: string | undefined;
|
|
89
|
+
onCreateCmdDisplaySuccessColor?: string | undefined;
|
|
90
|
+
onCreateCmdDisplayErrorColor?: string | undefined;
|
|
56
91
|
matchingStrategy?: "fail-on-tie" | "first-wins" | "last-wins" | undefined;
|
|
57
92
|
}>;
|
|
58
93
|
}>;
|
|
59
94
|
export declare const DefaultWorktreeSettings: WorktreeSettingsConfig;
|
|
60
95
|
export declare const DefaultLogfileTemplate = "/tmp/pi-worktree-{sessionId}-{name}.log";
|
|
96
|
+
export declare const DefaultOnCreateDisplayOutputMaxLines = 5;
|
|
97
|
+
export declare const DefaultOnCreateCmdDisplayPending = "[ ] {{cmd}}";
|
|
98
|
+
export declare const DefaultOnCreateCmdDisplaySuccess = "[x] {{cmd}}";
|
|
99
|
+
export declare const DefaultOnCreateCmdDisplayError = "[ ] {{cmd}} [ERROR]";
|
|
100
|
+
export declare const DefaultOnCreateCmdDisplayPendingColor = "dim";
|
|
101
|
+
export declare const DefaultOnCreateCmdDisplaySuccessColor = "success";
|
|
102
|
+
export declare const DefaultOnCreateCmdDisplayErrorColor = "error";
|
|
61
103
|
export type PiWorktreeConfigService = Awaited<ReturnType<typeof createPiWorktreeConfigService>>;
|
|
62
104
|
export type PiWorktreeConfiguredWorktreeMap = PiWorktreeConfigService['worktrees'];
|
|
@@ -14,6 +14,13 @@ export declare const PiWorktreeConfigSchema: import("typebox").TObject<{
|
|
|
14
14
|
}>>>;
|
|
15
15
|
matchingStrategy: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"fail-on-tie">, import("typebox").TLiteral<"first-wins">, import("typebox").TLiteral<"last-wins">]>>;
|
|
16
16
|
logfile: import("typebox").TOptional<import("typebox").TString>;
|
|
17
|
+
onCreateDisplayOutputMaxLines: import("typebox").TOptional<import("typebox").TInteger>;
|
|
18
|
+
onCreateCmdDisplayPending: import("typebox").TOptional<import("typebox").TString>;
|
|
19
|
+
onCreateCmdDisplaySuccess: import("typebox").TOptional<import("typebox").TString>;
|
|
20
|
+
onCreateCmdDisplayError: import("typebox").TOptional<import("typebox").TString>;
|
|
21
|
+
onCreateCmdDisplayPendingColor: import("typebox").TOptional<import("typebox").TString>;
|
|
22
|
+
onCreateCmdDisplaySuccessColor: import("typebox").TOptional<import("typebox").TString>;
|
|
23
|
+
onCreateCmdDisplayErrorColor: import("typebox").TOptional<import("typebox").TString>;
|
|
17
24
|
}>;
|
|
18
25
|
export type WorktreeSettingsConfig = Static<typeof WorktreeSettingsSchema>;
|
|
19
26
|
export type MatchingStrategy = Static<typeof MatchingStrategySchema>;
|