@posthog/agent 2.0.0 → 2.0.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/LICENSE +1 -1
- package/README.md +221 -219
- package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +21 -0
- package/dist/adapters/claude/conversion/tool-use-to-acp.js +547 -0
- package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -0
- package/dist/adapters/claude/permissions/permission-options.d.ts +13 -0
- package/dist/adapters/claude/permissions/permission-options.js +117 -0
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -0
- package/dist/adapters/claude/questions/utils.d.ts +132 -0
- package/dist/adapters/claude/questions/utils.js +63 -0
- package/dist/adapters/claude/questions/utils.js.map +1 -0
- package/dist/adapters/claude/tools.d.ts +18 -0
- package/dist/adapters/claude/tools.js +95 -0
- package/dist/adapters/claude/tools.js.map +1 -0
- package/dist/agent-DBQY1BfC.d.ts +123 -0
- package/dist/agent.d.ts +5 -0
- package/dist/agent.js +3656 -0
- package/dist/agent.js.map +1 -0
- package/dist/claude-cli/cli.js +3695 -2746
- package/dist/claude-cli/vendor/ripgrep/COPYING +3 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-darwin/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-darwin/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-linux/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-linux/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-darwin/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-darwin/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-linux/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-linux/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-win32/rg.exe +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-win32/ripgrep.node +0 -0
- package/dist/gateway-models.d.ts +24 -0
- package/dist/gateway-models.js +93 -0
- package/dist/gateway-models.js.map +1 -0
- package/dist/index.d.ts +170 -1157
- package/dist/index.js +3252 -5074
- package/dist/index.js.map +1 -1
- package/dist/logger-DDBiMOOD.d.ts +24 -0
- package/dist/posthog-api.d.ts +40 -0
- package/dist/posthog-api.js +175 -0
- package/dist/posthog-api.js.map +1 -0
- package/dist/server/agent-server.d.ts +41 -0
- package/dist/server/agent-server.js +4451 -0
- package/dist/server/agent-server.js.map +1 -0
- package/dist/server/bin.d.ts +1 -0
- package/dist/server/bin.js +4507 -0
- package/dist/server/bin.js.map +1 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -14
- package/src/acp-extensions.ts +98 -16
- package/src/adapters/acp-connection.ts +494 -0
- package/src/adapters/base-acp-agent.ts +150 -0
- package/src/adapters/claude/claude-agent.ts +596 -0
- package/src/adapters/claude/conversion/acp-to-sdk.ts +102 -0
- package/src/adapters/claude/conversion/sdk-to-acp.ts +571 -0
- package/src/adapters/claude/conversion/tool-use-to-acp.ts +618 -0
- package/src/adapters/claude/hooks.ts +64 -0
- package/src/adapters/claude/mcp/tool-metadata.ts +102 -0
- package/src/adapters/claude/permissions/permission-handlers.ts +433 -0
- package/src/adapters/claude/permissions/permission-options.ts +103 -0
- package/src/adapters/claude/plan/utils.ts +56 -0
- package/src/adapters/claude/questions/utils.ts +92 -0
- package/src/adapters/claude/session/commands.ts +38 -0
- package/src/adapters/claude/session/mcp-config.ts +37 -0
- package/src/adapters/claude/session/models.ts +12 -0
- package/src/adapters/claude/session/options.ts +236 -0
- package/src/adapters/claude/tool-meta.ts +143 -0
- package/src/adapters/claude/tools.ts +53 -688
- package/src/adapters/claude/types.ts +61 -0
- package/src/adapters/codex/spawn.ts +130 -0
- package/src/agent.ts +96 -587
- package/src/execution-mode.ts +43 -0
- package/src/gateway-models.ts +135 -0
- package/src/index.ts +79 -0
- package/src/otel-log-writer.test.ts +105 -0
- package/src/otel-log-writer.ts +94 -0
- package/src/posthog-api.ts +75 -235
- package/src/resume.ts +115 -0
- package/src/sagas/apply-snapshot-saga.test.ts +690 -0
- package/src/sagas/apply-snapshot-saga.ts +88 -0
- package/src/sagas/capture-tree-saga.test.ts +892 -0
- package/src/sagas/capture-tree-saga.ts +141 -0
- package/src/sagas/resume-saga.test.ts +558 -0
- package/src/sagas/resume-saga.ts +332 -0
- package/src/sagas/test-fixtures.ts +250 -0
- package/src/server/agent-server.test.ts +220 -0
- package/src/server/agent-server.ts +748 -0
- package/src/server/bin.ts +88 -0
- package/src/server/jwt.ts +65 -0
- package/src/server/schemas.ts +47 -0
- package/src/server/types.ts +13 -0
- package/src/server/utils/retry.test.ts +122 -0
- package/src/server/utils/retry.ts +61 -0
- package/src/server/utils/sse-parser.test.ts +93 -0
- package/src/server/utils/sse-parser.ts +46 -0
- package/src/session-log-writer.test.ts +140 -0
- package/src/session-log-writer.ts +137 -0
- package/src/test/assertions.ts +114 -0
- package/src/test/controllers/sse-controller.ts +107 -0
- package/src/test/fixtures/api.ts +111 -0
- package/src/test/fixtures/config.ts +33 -0
- package/src/test/fixtures/notifications.ts +92 -0
- package/src/test/mocks/claude-sdk.ts +251 -0
- package/src/test/mocks/msw-handlers.ts +48 -0
- package/src/test/setup.ts +114 -0
- package/src/test/wait.ts +41 -0
- package/src/tree-tracker.ts +173 -0
- package/src/types.ts +54 -137
- package/src/utils/acp-content.ts +58 -0
- package/src/utils/async-mutex.test.ts +104 -0
- package/src/utils/async-mutex.ts +31 -0
- package/src/utils/common.ts +15 -0
- package/src/utils/gateway.ts +9 -6
- package/src/utils/logger.ts +0 -30
- package/src/utils/streams.ts +220 -0
- package/CLAUDE.md +0 -331
- package/src/adapters/claude/claude.ts +0 -1947
- package/src/adapters/claude/mcp-server.ts +0 -810
- package/src/adapters/claude/utils.ts +0 -267
- package/src/adapters/connection.ts +0 -95
- package/src/file-manager.ts +0 -273
- package/src/git-manager.ts +0 -577
- package/src/schemas.ts +0 -241
- package/src/session-store.ts +0 -259
- package/src/task-manager.ts +0 -163
- package/src/todo-manager.ts +0 -180
- package/src/tools/registry.ts +0 -134
- package/src/tools/types.ts +0 -133
- package/src/utils/tapped-stream.ts +0 -60
- package/src/worktree-manager.ts +0 -974
package/src/worktree-manager.ts
DELETED
|
@@ -1,974 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import * as crypto from "node:crypto";
|
|
3
|
-
import * as fs from "node:fs/promises";
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
import { promisify } from "node:util";
|
|
6
|
-
import type { WorktreeInfo } from "./types.js";
|
|
7
|
-
import { Logger } from "./utils/logger.js";
|
|
8
|
-
|
|
9
|
-
const execFileAsync = promisify(execFile);
|
|
10
|
-
|
|
11
|
-
export interface WorktreeConfig {
|
|
12
|
-
mainRepoPath: string;
|
|
13
|
-
worktreeBasePath?: string;
|
|
14
|
-
logger?: Logger;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const ADJECTIVES = [
|
|
18
|
-
"swift",
|
|
19
|
-
"bright",
|
|
20
|
-
"calm",
|
|
21
|
-
"bold",
|
|
22
|
-
"gentle",
|
|
23
|
-
"quick",
|
|
24
|
-
"soft",
|
|
25
|
-
"warm",
|
|
26
|
-
"cool",
|
|
27
|
-
"wise",
|
|
28
|
-
"keen",
|
|
29
|
-
"brave",
|
|
30
|
-
"clear",
|
|
31
|
-
"crisp",
|
|
32
|
-
"deep",
|
|
33
|
-
"fair",
|
|
34
|
-
"fine",
|
|
35
|
-
"free",
|
|
36
|
-
"glad",
|
|
37
|
-
"good",
|
|
38
|
-
"grand",
|
|
39
|
-
"great",
|
|
40
|
-
"happy",
|
|
41
|
-
"kind",
|
|
42
|
-
"light",
|
|
43
|
-
"lively",
|
|
44
|
-
"neat",
|
|
45
|
-
"nice",
|
|
46
|
-
"plain",
|
|
47
|
-
"proud",
|
|
48
|
-
"pure",
|
|
49
|
-
"rare",
|
|
50
|
-
"rich",
|
|
51
|
-
"safe",
|
|
52
|
-
"sharp",
|
|
53
|
-
"shy",
|
|
54
|
-
"simple",
|
|
55
|
-
"slim",
|
|
56
|
-
"smart",
|
|
57
|
-
"smooth",
|
|
58
|
-
"solid",
|
|
59
|
-
"sound",
|
|
60
|
-
"spare",
|
|
61
|
-
"stable",
|
|
62
|
-
"steady",
|
|
63
|
-
"still",
|
|
64
|
-
"strong",
|
|
65
|
-
"sure",
|
|
66
|
-
"sweet",
|
|
67
|
-
"tall",
|
|
68
|
-
"agile",
|
|
69
|
-
"ancient",
|
|
70
|
-
"autumn",
|
|
71
|
-
"azure",
|
|
72
|
-
"cosmic",
|
|
73
|
-
"daring",
|
|
74
|
-
"dawn",
|
|
75
|
-
"dusty",
|
|
76
|
-
"eager",
|
|
77
|
-
"early",
|
|
78
|
-
"endless",
|
|
79
|
-
"fading",
|
|
80
|
-
"fallen",
|
|
81
|
-
"famous",
|
|
82
|
-
"feral",
|
|
83
|
-
"fierce",
|
|
84
|
-
"fleet",
|
|
85
|
-
"foggy",
|
|
86
|
-
"forest",
|
|
87
|
-
"frozen",
|
|
88
|
-
"gleeful",
|
|
89
|
-
"golden",
|
|
90
|
-
"hazy",
|
|
91
|
-
"hidden",
|
|
92
|
-
"hollow",
|
|
93
|
-
"humble",
|
|
94
|
-
"hushed",
|
|
95
|
-
"icy",
|
|
96
|
-
"inner",
|
|
97
|
-
"late",
|
|
98
|
-
"lazy",
|
|
99
|
-
"little",
|
|
100
|
-
"lone",
|
|
101
|
-
"long",
|
|
102
|
-
"lost",
|
|
103
|
-
"lucky",
|
|
104
|
-
"lunar",
|
|
105
|
-
"magic",
|
|
106
|
-
"mellow",
|
|
107
|
-
"mighty",
|
|
108
|
-
"misty",
|
|
109
|
-
"modest",
|
|
110
|
-
"mossy",
|
|
111
|
-
"mystic",
|
|
112
|
-
"nimble",
|
|
113
|
-
"noble",
|
|
114
|
-
"ocean",
|
|
115
|
-
"outer",
|
|
116
|
-
"pale",
|
|
117
|
-
"paper",
|
|
118
|
-
"patient",
|
|
119
|
-
"peaceful",
|
|
120
|
-
"phantom",
|
|
121
|
-
"polite",
|
|
122
|
-
"primal",
|
|
123
|
-
"quiet",
|
|
124
|
-
"rapid",
|
|
125
|
-
"restless",
|
|
126
|
-
"rising",
|
|
127
|
-
"roaming",
|
|
128
|
-
"rocky",
|
|
129
|
-
"rustic",
|
|
130
|
-
"sacred",
|
|
131
|
-
"sandy",
|
|
132
|
-
"secret",
|
|
133
|
-
"serene",
|
|
134
|
-
"shadow",
|
|
135
|
-
"shining",
|
|
136
|
-
"silent",
|
|
137
|
-
"silky",
|
|
138
|
-
"silver",
|
|
139
|
-
"sleek",
|
|
140
|
-
"snowy",
|
|
141
|
-
"solar",
|
|
142
|
-
"solemn",
|
|
143
|
-
"spring",
|
|
144
|
-
"starry",
|
|
145
|
-
"stormy",
|
|
146
|
-
"summer",
|
|
147
|
-
"sunny",
|
|
148
|
-
"tender",
|
|
149
|
-
"thorny",
|
|
150
|
-
"tiny",
|
|
151
|
-
"tranquil",
|
|
152
|
-
"twilight",
|
|
153
|
-
"upward",
|
|
154
|
-
"velvet",
|
|
155
|
-
"vivid",
|
|
156
|
-
"wandering",
|
|
157
|
-
"wary",
|
|
158
|
-
"wild",
|
|
159
|
-
"windy",
|
|
160
|
-
"winter",
|
|
161
|
-
"wispy",
|
|
162
|
-
"young",
|
|
163
|
-
];
|
|
164
|
-
|
|
165
|
-
const COLORS = [
|
|
166
|
-
"blue",
|
|
167
|
-
"red",
|
|
168
|
-
"green",
|
|
169
|
-
"amber",
|
|
170
|
-
"coral",
|
|
171
|
-
"jade",
|
|
172
|
-
"pearl",
|
|
173
|
-
"ruby",
|
|
174
|
-
"sage",
|
|
175
|
-
"teal",
|
|
176
|
-
"gold",
|
|
177
|
-
"silver",
|
|
178
|
-
"bronze",
|
|
179
|
-
"copper",
|
|
180
|
-
"ivory",
|
|
181
|
-
"onyx",
|
|
182
|
-
"opal",
|
|
183
|
-
"rose",
|
|
184
|
-
"slate",
|
|
185
|
-
"violet",
|
|
186
|
-
"aqua",
|
|
187
|
-
"azure",
|
|
188
|
-
"beige",
|
|
189
|
-
"black",
|
|
190
|
-
"brass",
|
|
191
|
-
"brick",
|
|
192
|
-
"brown",
|
|
193
|
-
"cedar",
|
|
194
|
-
"charcoal",
|
|
195
|
-
"cherry",
|
|
196
|
-
"chestnut",
|
|
197
|
-
"chrome",
|
|
198
|
-
"cider",
|
|
199
|
-
"cinnamon",
|
|
200
|
-
"citrus",
|
|
201
|
-
"clay",
|
|
202
|
-
"cloud",
|
|
203
|
-
"cobalt",
|
|
204
|
-
"cocoa",
|
|
205
|
-
"cream",
|
|
206
|
-
"crimson",
|
|
207
|
-
"crystal",
|
|
208
|
-
"cyan",
|
|
209
|
-
"denim",
|
|
210
|
-
"dusk",
|
|
211
|
-
"ebony",
|
|
212
|
-
"ember",
|
|
213
|
-
"emerald",
|
|
214
|
-
"fern",
|
|
215
|
-
"flame",
|
|
216
|
-
"flint",
|
|
217
|
-
"forest",
|
|
218
|
-
"frost",
|
|
219
|
-
"garnet",
|
|
220
|
-
"ginger",
|
|
221
|
-
"glacier",
|
|
222
|
-
"granite",
|
|
223
|
-
"grape",
|
|
224
|
-
"gray",
|
|
225
|
-
"hazel",
|
|
226
|
-
"honey",
|
|
227
|
-
"indigo",
|
|
228
|
-
"iron",
|
|
229
|
-
"lapis",
|
|
230
|
-
"lava",
|
|
231
|
-
"lavender",
|
|
232
|
-
"lemon",
|
|
233
|
-
"lilac",
|
|
234
|
-
"lime",
|
|
235
|
-
"magenta",
|
|
236
|
-
"mahogany",
|
|
237
|
-
"maple",
|
|
238
|
-
"marble",
|
|
239
|
-
"maroon",
|
|
240
|
-
"mauve",
|
|
241
|
-
"midnight",
|
|
242
|
-
"mint",
|
|
243
|
-
"mocha",
|
|
244
|
-
"moss",
|
|
245
|
-
"mustard",
|
|
246
|
-
"navy",
|
|
247
|
-
"nickel",
|
|
248
|
-
"obsidian",
|
|
249
|
-
"ochre",
|
|
250
|
-
"olive",
|
|
251
|
-
"orange",
|
|
252
|
-
"orchid",
|
|
253
|
-
"peach",
|
|
254
|
-
"pine",
|
|
255
|
-
"pink",
|
|
256
|
-
"plum",
|
|
257
|
-
"porcelain",
|
|
258
|
-
"purple",
|
|
259
|
-
"quartz",
|
|
260
|
-
"rust",
|
|
261
|
-
"saffron",
|
|
262
|
-
"salmon",
|
|
263
|
-
"sand",
|
|
264
|
-
"sapphire",
|
|
265
|
-
"scarlet",
|
|
266
|
-
"sepia",
|
|
267
|
-
"shadow",
|
|
268
|
-
"sienna",
|
|
269
|
-
"smoke",
|
|
270
|
-
"snow",
|
|
271
|
-
"steel",
|
|
272
|
-
"stone",
|
|
273
|
-
"storm",
|
|
274
|
-
"sunset",
|
|
275
|
-
"tan",
|
|
276
|
-
"tangerine",
|
|
277
|
-
"taupe",
|
|
278
|
-
"terra",
|
|
279
|
-
"timber",
|
|
280
|
-
"topaz",
|
|
281
|
-
"turquoise",
|
|
282
|
-
"umber",
|
|
283
|
-
"vanilla",
|
|
284
|
-
"walnut",
|
|
285
|
-
"wheat",
|
|
286
|
-
"white",
|
|
287
|
-
"wine",
|
|
288
|
-
"yellow",
|
|
289
|
-
];
|
|
290
|
-
|
|
291
|
-
const ANIMALS = [
|
|
292
|
-
"fox",
|
|
293
|
-
"owl",
|
|
294
|
-
"bear",
|
|
295
|
-
"wolf",
|
|
296
|
-
"hawk",
|
|
297
|
-
"deer",
|
|
298
|
-
"lynx",
|
|
299
|
-
"otter",
|
|
300
|
-
"raven",
|
|
301
|
-
"falcon",
|
|
302
|
-
"badger",
|
|
303
|
-
"beaver",
|
|
304
|
-
"bison",
|
|
305
|
-
"bobcat",
|
|
306
|
-
"crane",
|
|
307
|
-
"eagle",
|
|
308
|
-
"ferret",
|
|
309
|
-
"finch",
|
|
310
|
-
"gopher",
|
|
311
|
-
"heron",
|
|
312
|
-
"jaguar",
|
|
313
|
-
"koala",
|
|
314
|
-
"lemur",
|
|
315
|
-
"marten",
|
|
316
|
-
"mink",
|
|
317
|
-
"moose",
|
|
318
|
-
"newt",
|
|
319
|
-
"ocelot",
|
|
320
|
-
"osprey",
|
|
321
|
-
"panda",
|
|
322
|
-
"parrot",
|
|
323
|
-
"pelican",
|
|
324
|
-
"puma",
|
|
325
|
-
"quail",
|
|
326
|
-
"rabbit",
|
|
327
|
-
"raccoon",
|
|
328
|
-
"salmon",
|
|
329
|
-
"seal",
|
|
330
|
-
"shark",
|
|
331
|
-
"shrew",
|
|
332
|
-
"sloth",
|
|
333
|
-
"snake",
|
|
334
|
-
"spider",
|
|
335
|
-
"squid",
|
|
336
|
-
"stork",
|
|
337
|
-
"swan",
|
|
338
|
-
"tiger",
|
|
339
|
-
"toucan",
|
|
340
|
-
"turtle",
|
|
341
|
-
"whale",
|
|
342
|
-
"albatross",
|
|
343
|
-
"ant",
|
|
344
|
-
"antelope",
|
|
345
|
-
"armadillo",
|
|
346
|
-
"baboon",
|
|
347
|
-
"bat",
|
|
348
|
-
"bee",
|
|
349
|
-
"beetle",
|
|
350
|
-
"buffalo",
|
|
351
|
-
"butterfly",
|
|
352
|
-
"camel",
|
|
353
|
-
"cardinal",
|
|
354
|
-
"caribou",
|
|
355
|
-
"catfish",
|
|
356
|
-
"cheetah",
|
|
357
|
-
"chipmunk",
|
|
358
|
-
"cicada",
|
|
359
|
-
"clam",
|
|
360
|
-
"cobra",
|
|
361
|
-
"condor",
|
|
362
|
-
"corgi",
|
|
363
|
-
"cougar",
|
|
364
|
-
"coyote",
|
|
365
|
-
"crab",
|
|
366
|
-
"cricket",
|
|
367
|
-
"crow",
|
|
368
|
-
"dolphin",
|
|
369
|
-
"donkey",
|
|
370
|
-
"dove",
|
|
371
|
-
"dragonfly",
|
|
372
|
-
"duck",
|
|
373
|
-
"eel",
|
|
374
|
-
"egret",
|
|
375
|
-
"elephant",
|
|
376
|
-
"elk",
|
|
377
|
-
"emu",
|
|
378
|
-
"firefly",
|
|
379
|
-
"flamingo",
|
|
380
|
-
"frog",
|
|
381
|
-
"gazelle",
|
|
382
|
-
"gecko",
|
|
383
|
-
"gibbon",
|
|
384
|
-
"giraffe",
|
|
385
|
-
"goat",
|
|
386
|
-
"goose",
|
|
387
|
-
"gorilla",
|
|
388
|
-
"grasshopper",
|
|
389
|
-
"grouse",
|
|
390
|
-
"gull",
|
|
391
|
-
"hamster",
|
|
392
|
-
"hare",
|
|
393
|
-
"hedgehog",
|
|
394
|
-
"hippo",
|
|
395
|
-
"hornet",
|
|
396
|
-
"horse",
|
|
397
|
-
"hound",
|
|
398
|
-
"hummingbird",
|
|
399
|
-
"hyena",
|
|
400
|
-
"ibis",
|
|
401
|
-
"iguana",
|
|
402
|
-
"impala",
|
|
403
|
-
"jackal",
|
|
404
|
-
"jay",
|
|
405
|
-
"jellyfish",
|
|
406
|
-
"kangaroo",
|
|
407
|
-
"kestrel",
|
|
408
|
-
"kingfisher",
|
|
409
|
-
"kite",
|
|
410
|
-
"kiwi",
|
|
411
|
-
"lark",
|
|
412
|
-
"leopard",
|
|
413
|
-
"lion",
|
|
414
|
-
"lizard",
|
|
415
|
-
"llama",
|
|
416
|
-
"lobster",
|
|
417
|
-
"loon",
|
|
418
|
-
"macaw",
|
|
419
|
-
"magpie",
|
|
420
|
-
"mallard",
|
|
421
|
-
"mammoth",
|
|
422
|
-
"manatee",
|
|
423
|
-
"mantis",
|
|
424
|
-
"marlin",
|
|
425
|
-
"marmot",
|
|
426
|
-
"meerkat",
|
|
427
|
-
"mockingbird",
|
|
428
|
-
"mole",
|
|
429
|
-
"mongoose",
|
|
430
|
-
"monkey",
|
|
431
|
-
"moth",
|
|
432
|
-
"mouse",
|
|
433
|
-
"mule",
|
|
434
|
-
"narwhal",
|
|
435
|
-
"nightingale",
|
|
436
|
-
"octopus",
|
|
437
|
-
"opossum",
|
|
438
|
-
"orangutan",
|
|
439
|
-
"oriole",
|
|
440
|
-
"ostrich",
|
|
441
|
-
"oyster",
|
|
442
|
-
"panther",
|
|
443
|
-
"peacock",
|
|
444
|
-
"penguin",
|
|
445
|
-
"pheasant",
|
|
446
|
-
"pig",
|
|
447
|
-
"pigeon",
|
|
448
|
-
"pike",
|
|
449
|
-
"piranha",
|
|
450
|
-
"platypus",
|
|
451
|
-
"pony",
|
|
452
|
-
"porcupine",
|
|
453
|
-
"porpoise",
|
|
454
|
-
"python",
|
|
455
|
-
"raven",
|
|
456
|
-
"ray",
|
|
457
|
-
"reindeer",
|
|
458
|
-
"rhino",
|
|
459
|
-
"robin",
|
|
460
|
-
"rooster",
|
|
461
|
-
"salamander",
|
|
462
|
-
"sandpiper",
|
|
463
|
-
"sardine",
|
|
464
|
-
"scorpion",
|
|
465
|
-
"seagull",
|
|
466
|
-
"seahorse",
|
|
467
|
-
"skunk",
|
|
468
|
-
"snail",
|
|
469
|
-
"sparrow",
|
|
470
|
-
"squirrel",
|
|
471
|
-
"starfish",
|
|
472
|
-
"starling",
|
|
473
|
-
"stingray",
|
|
474
|
-
"swallow",
|
|
475
|
-
"tapir",
|
|
476
|
-
"termite",
|
|
477
|
-
"tern",
|
|
478
|
-
"toad",
|
|
479
|
-
"trout",
|
|
480
|
-
"tuna",
|
|
481
|
-
"viper",
|
|
482
|
-
"vulture",
|
|
483
|
-
"walrus",
|
|
484
|
-
"wasp",
|
|
485
|
-
"weasel",
|
|
486
|
-
"wombat",
|
|
487
|
-
"woodpecker",
|
|
488
|
-
"wren",
|
|
489
|
-
"yak",
|
|
490
|
-
"zebra",
|
|
491
|
-
];
|
|
492
|
-
|
|
493
|
-
const WORKTREE_FOLDER_NAME = ".array";
|
|
494
|
-
|
|
495
|
-
export class WorktreeManager {
|
|
496
|
-
private mainRepoPath: string;
|
|
497
|
-
private worktreeBasePath: string | null;
|
|
498
|
-
private repoName: string;
|
|
499
|
-
private logger: Logger;
|
|
500
|
-
|
|
501
|
-
constructor(config: WorktreeConfig) {
|
|
502
|
-
this.mainRepoPath = config.mainRepoPath;
|
|
503
|
-
this.worktreeBasePath = config.worktreeBasePath || null;
|
|
504
|
-
this.repoName = path.basename(config.mainRepoPath);
|
|
505
|
-
this.logger =
|
|
506
|
-
config.logger ||
|
|
507
|
-
new Logger({ debug: false, prefix: "[WorktreeManager]" });
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
private usesExternalPath(): boolean {
|
|
511
|
-
return this.worktreeBasePath !== null;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
private async runGitCommand(args: string[]): Promise<string> {
|
|
515
|
-
try {
|
|
516
|
-
const { stdout } = await execFileAsync("git", args, {
|
|
517
|
-
cwd: this.mainRepoPath,
|
|
518
|
-
});
|
|
519
|
-
return stdout.trim();
|
|
520
|
-
} catch (error) {
|
|
521
|
-
throw new Error(`Git command failed: git ${args.join(" ")}\n${error}`);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
private randomElement<T>(array: T[]): T {
|
|
526
|
-
return array[crypto.randomInt(array.length)];
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
generateWorktreeName(): string {
|
|
530
|
-
const adjective = this.randomElement(ADJECTIVES);
|
|
531
|
-
const color = this.randomElement(COLORS);
|
|
532
|
-
const animal = this.randomElement(ANIMALS);
|
|
533
|
-
return `${adjective}-${color}-${animal}`;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
private getWorktreeFolderPath(): string {
|
|
537
|
-
if (this.worktreeBasePath) {
|
|
538
|
-
return path.join(this.worktreeBasePath, this.repoName);
|
|
539
|
-
}
|
|
540
|
-
return path.join(this.mainRepoPath, WORKTREE_FOLDER_NAME);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
private getWorktreePath(name: string): string {
|
|
544
|
-
return path.join(this.getWorktreeFolderPath(), name);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
async worktreeExists(name: string): Promise<boolean> {
|
|
548
|
-
const worktreePath = this.getWorktreePath(name);
|
|
549
|
-
try {
|
|
550
|
-
await fs.access(worktreePath);
|
|
551
|
-
return true;
|
|
552
|
-
} catch {
|
|
553
|
-
return false;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
async ensureArrayDirIgnored(): Promise<void> {
|
|
558
|
-
// Use .git/info/exclude instead of .gitignore to avoid modifying tracked files
|
|
559
|
-
const excludePath = path.join(this.mainRepoPath, ".git", "info", "exclude");
|
|
560
|
-
const ignorePattern = `/${WORKTREE_FOLDER_NAME}/`;
|
|
561
|
-
|
|
562
|
-
let content = "";
|
|
563
|
-
try {
|
|
564
|
-
content = await fs.readFile(excludePath, "utf-8");
|
|
565
|
-
} catch {
|
|
566
|
-
// File doesn't exist or .git/info doesn't exist
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// Check if pattern is already present
|
|
570
|
-
if (
|
|
571
|
-
content.includes(`/${WORKTREE_FOLDER_NAME}/`) ||
|
|
572
|
-
content.includes(`/${WORKTREE_FOLDER_NAME}`)
|
|
573
|
-
) {
|
|
574
|
-
this.logger.debug("Exclude file already contains .array folder pattern");
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// Ensure .git/info directory exists
|
|
579
|
-
const infoDir = path.join(this.mainRepoPath, ".git", "info");
|
|
580
|
-
await fs.mkdir(infoDir, { recursive: true });
|
|
581
|
-
|
|
582
|
-
// Append the pattern
|
|
583
|
-
const newContent = `${content.trimEnd()}\n\n# Array worktrees\n${ignorePattern}\n`;
|
|
584
|
-
await fs.writeFile(excludePath, newContent);
|
|
585
|
-
this.logger.info("Added .array folder to .git/info/exclude");
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
private async generateUniqueWorktreeName(): Promise<string> {
|
|
589
|
-
let name = this.generateWorktreeName();
|
|
590
|
-
let attempts = 0;
|
|
591
|
-
const maxAttempts = 100;
|
|
592
|
-
|
|
593
|
-
while ((await this.worktreeExists(name)) && attempts < maxAttempts) {
|
|
594
|
-
name = this.generateWorktreeName();
|
|
595
|
-
attempts++;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
if (attempts >= maxAttempts) {
|
|
599
|
-
// Fallback: append timestamp
|
|
600
|
-
name = `${this.generateWorktreeName()}-${Date.now()}`;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
return name;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
private async getDefaultBranch(): Promise<string> {
|
|
607
|
-
// Try all methods in parallel for speed
|
|
608
|
-
const [symbolicRef, mainExists, masterExists] = await Promise.allSettled([
|
|
609
|
-
this.runGitCommand(["symbolic-ref", "refs/remotes/origin/HEAD"]),
|
|
610
|
-
this.runGitCommand(["rev-parse", "--verify", "main"]),
|
|
611
|
-
this.runGitCommand(["rev-parse", "--verify", "master"]),
|
|
612
|
-
]);
|
|
613
|
-
|
|
614
|
-
// Prefer symbolic ref (most accurate)
|
|
615
|
-
if (symbolicRef.status === "fulfilled") {
|
|
616
|
-
return symbolicRef.value.replace("refs/remotes/origin/", "");
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// Fallback to main if it exists
|
|
620
|
-
if (mainExists.status === "fulfilled") {
|
|
621
|
-
return "main";
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Fallback to master if it exists
|
|
625
|
-
if (masterExists.status === "fulfilled") {
|
|
626
|
-
return "master";
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
throw new Error(
|
|
630
|
-
"Cannot determine default branch. No main or master branch found.",
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
async createWorktree(options?: {
|
|
635
|
-
baseBranch?: string;
|
|
636
|
-
}): Promise<WorktreeInfo> {
|
|
637
|
-
const totalStart = Date.now();
|
|
638
|
-
|
|
639
|
-
// Run setup tasks in parallel for speed
|
|
640
|
-
const setupPromises: Promise<unknown>[] = [];
|
|
641
|
-
|
|
642
|
-
// Only modify .git/info/exclude when using in-repo storage
|
|
643
|
-
if (!this.usesExternalPath()) {
|
|
644
|
-
setupPromises.push(this.ensureArrayDirIgnored());
|
|
645
|
-
} else {
|
|
646
|
-
// Ensure the worktree folder exists when using external path
|
|
647
|
-
const folderPath = this.getWorktreeFolderPath();
|
|
648
|
-
setupPromises.push(fs.mkdir(folderPath, { recursive: true }));
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Generate unique worktree name (in parallel with above)
|
|
652
|
-
const worktreeNamePromise = this.generateUniqueWorktreeName();
|
|
653
|
-
setupPromises.push(worktreeNamePromise);
|
|
654
|
-
|
|
655
|
-
// Get default branch in parallel if not provided
|
|
656
|
-
const baseBranchPromise = options?.baseBranch
|
|
657
|
-
? Promise.resolve(options.baseBranch)
|
|
658
|
-
: this.getDefaultBranch();
|
|
659
|
-
setupPromises.push(baseBranchPromise);
|
|
660
|
-
|
|
661
|
-
// Wait for all setup to complete
|
|
662
|
-
await Promise.all(setupPromises);
|
|
663
|
-
const setupTime = Date.now() - totalStart;
|
|
664
|
-
|
|
665
|
-
const worktreeName = await worktreeNamePromise;
|
|
666
|
-
const baseBranch = await baseBranchPromise;
|
|
667
|
-
const worktreePath = this.getWorktreePath(worktreeName);
|
|
668
|
-
const branchName = `array/${worktreeName}`;
|
|
669
|
-
|
|
670
|
-
this.logger.info("Creating worktree", {
|
|
671
|
-
worktreeName,
|
|
672
|
-
worktreePath,
|
|
673
|
-
branchName,
|
|
674
|
-
baseBranch,
|
|
675
|
-
external: this.usesExternalPath(),
|
|
676
|
-
setupTimeMs: setupTime,
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
// Create the worktree with a new branch
|
|
680
|
-
const gitStart = Date.now();
|
|
681
|
-
if (this.usesExternalPath()) {
|
|
682
|
-
// Use absolute path for external worktrees
|
|
683
|
-
await this.runGitCommand([
|
|
684
|
-
"worktree",
|
|
685
|
-
"add",
|
|
686
|
-
"--quiet",
|
|
687
|
-
"-b",
|
|
688
|
-
branchName,
|
|
689
|
-
worktreePath,
|
|
690
|
-
baseBranch,
|
|
691
|
-
]);
|
|
692
|
-
} else {
|
|
693
|
-
// Use relative path from repo root for in-repo worktrees
|
|
694
|
-
const relativePath = `./${WORKTREE_FOLDER_NAME}/${worktreeName}`;
|
|
695
|
-
await this.runGitCommand([
|
|
696
|
-
"worktree",
|
|
697
|
-
"add",
|
|
698
|
-
"--quiet",
|
|
699
|
-
"-b",
|
|
700
|
-
branchName,
|
|
701
|
-
relativePath,
|
|
702
|
-
baseBranch,
|
|
703
|
-
]);
|
|
704
|
-
}
|
|
705
|
-
const gitTime = Date.now() - gitStart;
|
|
706
|
-
|
|
707
|
-
const createdAt = new Date().toISOString();
|
|
708
|
-
|
|
709
|
-
this.logger.info("Worktree created successfully", {
|
|
710
|
-
worktreeName,
|
|
711
|
-
worktreePath,
|
|
712
|
-
branchName,
|
|
713
|
-
setupTimeMs: setupTime,
|
|
714
|
-
gitWorktreeAddMs: gitTime,
|
|
715
|
-
totalMs: Date.now() - totalStart,
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
return {
|
|
719
|
-
worktreePath,
|
|
720
|
-
worktreeName,
|
|
721
|
-
branchName,
|
|
722
|
-
baseBranch,
|
|
723
|
-
createdAt,
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
async deleteWorktree(worktreePath: string): Promise<void> {
|
|
728
|
-
const resolvedWorktreePath = path.resolve(worktreePath);
|
|
729
|
-
const resolvedMainRepoPath = path.resolve(this.mainRepoPath);
|
|
730
|
-
|
|
731
|
-
// Safety check 1: Never delete the main repo path
|
|
732
|
-
if (resolvedWorktreePath === resolvedMainRepoPath) {
|
|
733
|
-
const error = new Error(
|
|
734
|
-
"Cannot delete worktree: path matches main repo path",
|
|
735
|
-
);
|
|
736
|
-
this.logger.error("Safety check failed", { worktreePath, error });
|
|
737
|
-
throw error;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// Safety check 2: Never delete a parent of the main repo path
|
|
741
|
-
if (
|
|
742
|
-
resolvedMainRepoPath.startsWith(resolvedWorktreePath) &&
|
|
743
|
-
resolvedMainRepoPath !== resolvedWorktreePath
|
|
744
|
-
) {
|
|
745
|
-
const error = new Error(
|
|
746
|
-
"Cannot delete worktree: path is a parent of main repo path",
|
|
747
|
-
);
|
|
748
|
-
this.logger.error("Safety check failed", { worktreePath, error });
|
|
749
|
-
throw error;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// Safety check 3: Check for .git directory (indicates main repo)
|
|
753
|
-
try {
|
|
754
|
-
const gitPath = path.join(resolvedWorktreePath, ".git");
|
|
755
|
-
const stat = await fs.stat(gitPath);
|
|
756
|
-
if (stat.isDirectory()) {
|
|
757
|
-
const error = new Error(
|
|
758
|
-
"Cannot delete worktree: path appears to be a main repository (contains .git directory)",
|
|
759
|
-
);
|
|
760
|
-
this.logger.error("Safety check failed", { worktreePath, error });
|
|
761
|
-
throw error;
|
|
762
|
-
}
|
|
763
|
-
} catch (error) {
|
|
764
|
-
// If .git doesn't exist or we can't read it, proceed (unless it was the directory check above)
|
|
765
|
-
if (
|
|
766
|
-
error instanceof Error &&
|
|
767
|
-
error.message.includes("Cannot delete worktree")
|
|
768
|
-
) {
|
|
769
|
-
throw error;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
this.logger.info("Deleting worktree", { worktreePath });
|
|
774
|
-
|
|
775
|
-
try {
|
|
776
|
-
// First, try to remove the worktree via git using execFileAsync for safety
|
|
777
|
-
await execFileAsync(
|
|
778
|
-
"git",
|
|
779
|
-
["worktree", "remove", worktreePath, "--force"],
|
|
780
|
-
{
|
|
781
|
-
cwd: this.mainRepoPath,
|
|
782
|
-
},
|
|
783
|
-
);
|
|
784
|
-
this.logger.info("Worktree deleted successfully", { worktreePath });
|
|
785
|
-
} catch (error) {
|
|
786
|
-
this.logger.warn(
|
|
787
|
-
"Git worktree remove failed, attempting manual cleanup",
|
|
788
|
-
{
|
|
789
|
-
worktreePath,
|
|
790
|
-
error,
|
|
791
|
-
},
|
|
792
|
-
);
|
|
793
|
-
|
|
794
|
-
// Manual cleanup if git command fails
|
|
795
|
-
try {
|
|
796
|
-
await fs.rm(worktreePath, { recursive: true, force: true });
|
|
797
|
-
// Also prune the worktree list
|
|
798
|
-
await this.runGitCommand(["worktree", "prune"]);
|
|
799
|
-
this.logger.info("Worktree cleaned up manually", { worktreePath });
|
|
800
|
-
} catch (cleanupError) {
|
|
801
|
-
this.logger.error("Failed to cleanup worktree", {
|
|
802
|
-
worktreePath,
|
|
803
|
-
cleanupError,
|
|
804
|
-
});
|
|
805
|
-
throw cleanupError;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
async getWorktreeInfo(worktreePath: string): Promise<WorktreeInfo | null> {
|
|
811
|
-
try {
|
|
812
|
-
// Parse the worktree list to find info about this worktree
|
|
813
|
-
const output = await this.runGitCommand([
|
|
814
|
-
"worktree",
|
|
815
|
-
"list",
|
|
816
|
-
"--porcelain",
|
|
817
|
-
]);
|
|
818
|
-
const worktrees = this.parseWorktreeList(output);
|
|
819
|
-
|
|
820
|
-
const worktree = worktrees.find((w) => w.worktreePath === worktreePath);
|
|
821
|
-
return worktree || null;
|
|
822
|
-
} catch (error) {
|
|
823
|
-
this.logger.debug("Failed to get worktree info", { worktreePath, error });
|
|
824
|
-
return null;
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
async listWorktrees(): Promise<WorktreeInfo[]> {
|
|
829
|
-
try {
|
|
830
|
-
const output = await this.runGitCommand([
|
|
831
|
-
"worktree",
|
|
832
|
-
"list",
|
|
833
|
-
"--porcelain",
|
|
834
|
-
]);
|
|
835
|
-
return this.parseWorktreeList(output);
|
|
836
|
-
} catch (error) {
|
|
837
|
-
this.logger.debug("Failed to list worktrees", { error });
|
|
838
|
-
return [];
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
private parseWorktreeList(output: string): WorktreeInfo[] {
|
|
843
|
-
const worktrees: WorktreeInfo[] = [];
|
|
844
|
-
const entries = output.split("\n\n").filter((e) => e.trim());
|
|
845
|
-
const worktreeFolderPath = this.getWorktreeFolderPath();
|
|
846
|
-
|
|
847
|
-
for (const entry of entries) {
|
|
848
|
-
const lines = entry.split("\n");
|
|
849
|
-
let worktreePath = "";
|
|
850
|
-
let branchName = "";
|
|
851
|
-
|
|
852
|
-
for (const line of lines) {
|
|
853
|
-
if (line.startsWith("worktree ")) {
|
|
854
|
-
worktreePath = line.replace("worktree ", "");
|
|
855
|
-
} else if (line.startsWith("branch refs/heads/")) {
|
|
856
|
-
branchName = line.replace("branch refs/heads/", "");
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
// Include worktrees that:
|
|
861
|
-
// 1. Are in our worktree folder (external or in-repo)
|
|
862
|
-
// 2. Have a posthog/ branch prefix (our naming convention)
|
|
863
|
-
const isInWorktreeFolder = worktreePath?.startsWith(worktreeFolderPath);
|
|
864
|
-
const isArrayBranch =
|
|
865
|
-
branchName?.startsWith("array/") || branchName?.startsWith("posthog/");
|
|
866
|
-
|
|
867
|
-
if (worktreePath && branchName && (isInWorktreeFolder || isArrayBranch)) {
|
|
868
|
-
const worktreeName = path.basename(worktreePath);
|
|
869
|
-
worktrees.push({
|
|
870
|
-
worktreePath,
|
|
871
|
-
worktreeName,
|
|
872
|
-
branchName,
|
|
873
|
-
baseBranch: "",
|
|
874
|
-
createdAt: "",
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
return worktrees;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
async isWorktree(repoPath: string): Promise<boolean> {
|
|
883
|
-
try {
|
|
884
|
-
const { stdout } = await execFileAsync(
|
|
885
|
-
"git",
|
|
886
|
-
["rev-parse", "--is-inside-work-tree"],
|
|
887
|
-
{ cwd: repoPath },
|
|
888
|
-
);
|
|
889
|
-
if (stdout.trim() !== "true") {
|
|
890
|
-
return false;
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
// Check if there's a .git file (worktrees have a .git file, not a .git directory)
|
|
894
|
-
const gitPath = path.join(repoPath, ".git");
|
|
895
|
-
const stat = await fs.stat(gitPath);
|
|
896
|
-
return stat.isFile(); // Worktrees have .git as a file, main repos have .git as a directory
|
|
897
|
-
} catch {
|
|
898
|
-
return false;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
async getMainRepoPathFromWorktree(
|
|
903
|
-
worktreePath: string,
|
|
904
|
-
): Promise<string | null> {
|
|
905
|
-
try {
|
|
906
|
-
const gitFilePath = path.join(worktreePath, ".git");
|
|
907
|
-
const content = await fs.readFile(gitFilePath, "utf-8");
|
|
908
|
-
|
|
909
|
-
// The .git file in a worktree contains: gitdir: /path/to/main/.git/worktrees/name
|
|
910
|
-
const match = content.match(/gitdir:\s*(.+)/);
|
|
911
|
-
if (match) {
|
|
912
|
-
const gitDir = match[1].trim();
|
|
913
|
-
// Go up from .git/worktrees/name to get the main repo path
|
|
914
|
-
// The gitdir points to something like: /main/repo/.git/worktrees/worktree-name
|
|
915
|
-
const mainGitDir = path.resolve(gitDir, "..", "..", "..");
|
|
916
|
-
return mainGitDir;
|
|
917
|
-
}
|
|
918
|
-
return null;
|
|
919
|
-
} catch {
|
|
920
|
-
return null;
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
async cleanupOrphanedWorktrees(associatedWorktreePaths: string[]): Promise<{
|
|
925
|
-
deleted: string[];
|
|
926
|
-
errors: Array<{ path: string; error: string }>;
|
|
927
|
-
}> {
|
|
928
|
-
this.logger.info("Starting cleanup of orphaned worktrees");
|
|
929
|
-
|
|
930
|
-
const allWorktrees = await this.listWorktrees();
|
|
931
|
-
const deleted: string[] = [];
|
|
932
|
-
const errors: Array<{ path: string; error: string }> = [];
|
|
933
|
-
|
|
934
|
-
const associatedPathsSet = new Set(
|
|
935
|
-
associatedWorktreePaths.map((p) => path.resolve(p)),
|
|
936
|
-
);
|
|
937
|
-
|
|
938
|
-
for (const worktree of allWorktrees) {
|
|
939
|
-
const resolvedPath = path.resolve(worktree.worktreePath);
|
|
940
|
-
|
|
941
|
-
if (!associatedPathsSet.has(resolvedPath)) {
|
|
942
|
-
this.logger.info("Found orphaned worktree", {
|
|
943
|
-
path: worktree.worktreePath,
|
|
944
|
-
});
|
|
945
|
-
|
|
946
|
-
try {
|
|
947
|
-
await this.deleteWorktree(worktree.worktreePath);
|
|
948
|
-
deleted.push(worktree.worktreePath);
|
|
949
|
-
this.logger.info("Deleted orphaned worktree", {
|
|
950
|
-
path: worktree.worktreePath,
|
|
951
|
-
});
|
|
952
|
-
} catch (error) {
|
|
953
|
-
const errorMessage =
|
|
954
|
-
error instanceof Error ? error.message : String(error);
|
|
955
|
-
errors.push({
|
|
956
|
-
path: worktree.worktreePath,
|
|
957
|
-
error: errorMessage,
|
|
958
|
-
});
|
|
959
|
-
this.logger.error("Failed to delete orphaned worktree", {
|
|
960
|
-
path: worktree.worktreePath,
|
|
961
|
-
error: errorMessage,
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
this.logger.info("Cleanup completed", {
|
|
968
|
-
deleted: deleted.length,
|
|
969
|
-
errors: errors.length,
|
|
970
|
-
});
|
|
971
|
-
|
|
972
|
-
return { deleted, errors };
|
|
973
|
-
}
|
|
974
|
-
}
|