@thanaen/worktree-cleanup 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -31
- package/dist/{cleanup-BQ4ci49S.mjs → cleanup-DeusOTMj.mjs} +87 -10
- package/dist/cleanup-DeusOTMj.mjs.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/main.mjs +2 -2
- package/package.json +1 -1
- package/dist/cleanup-BQ4ci49S.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,60 +1,100 @@
|
|
|
1
1
|
# worktree-cleanup
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
and already merged into a trusted base branch before it can be offered for
|
|
6
|
-
deletion.
|
|
3
|
+
Clean up stale Git worktrees left behind by coding agents and other tooling,
|
|
4
|
+
without putting uncommitted work at risk.
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
`worktree-cleanup` first shows exactly what it found and why each directory is
|
|
7
|
+
removable or skipped. Nothing is deleted until you confirm the plan.
|
|
10
8
|
|
|
11
9
|
## Install
|
|
12
10
|
|
|
11
|
+
Requires Git and Node.js 22 or newer.
|
|
12
|
+
|
|
13
13
|
```bash
|
|
14
|
-
|
|
14
|
+
npm install --global @thanaen/worktree-cleanup
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Also available with pnpm or Bun:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add --global @thanaen/worktree-cleanup
|
|
21
|
+
bun add --global @thanaen/worktree-cleanup
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
Run the command from your project directory:
|
|
18
27
|
|
|
19
28
|
```bash
|
|
20
|
-
# Inspect smart defaults below the current directory
|
|
21
29
|
worktree-cleanup
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The shorter `worktree-clean` command does the same thing.
|
|
33
|
+
|
|
34
|
+
By default, the CLI looks for these directories in the current directory:
|
|
22
35
|
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
- `worktrees`
|
|
37
|
+
- `.claude/worktrees`
|
|
38
|
+
- `.codex/worktrees`
|
|
25
39
|
|
|
26
|
-
|
|
40
|
+
Only immediate child directories are inspected. If you keep worktrees
|
|
41
|
+
somewhere else, select that directory explicitly:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
27
44
|
worktree-cleanup --dir ../agent-worktrees
|
|
45
|
+
```
|
|
28
46
|
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
Using `--dir` disables the smart defaults. To approve the displayed plan
|
|
48
|
+
without an interactive prompt, pass `-y` or `--yes`:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
worktree-cleanup --dir ../agent-worktrees --yes
|
|
31
52
|
```
|
|
32
53
|
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
In a non-interactive environment, `--yes` is required.
|
|
55
|
+
|
|
56
|
+
## What is safe to remove?
|
|
57
|
+
|
|
58
|
+
A worktree is offered for removal only when every check passes:
|
|
59
|
+
|
|
60
|
+
- Git recognizes it as a registered worktree.
|
|
61
|
+
- It is neither the repository's main worktree nor the worktree running the
|
|
62
|
+
command.
|
|
63
|
+
- It is unlocked.
|
|
64
|
+
- It has no tracked changes or untracked files.
|
|
65
|
+
- Its `HEAD` is already an ancestor of the detected base branch, or its
|
|
66
|
+
attached branch can be merged without changing the base branch's content.
|
|
67
|
+
|
|
68
|
+
The base branch is detected from `origin/HEAD`, then local `main`, local
|
|
69
|
+
`master`, and finally the main worktree's current branch. If the CLI cannot
|
|
70
|
+
prove that a directory is safe, it skips it and explains why.
|
|
35
71
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
interactive terminal, `--yes` is required.
|
|
72
|
+
Candidates are checked again immediately before removal. The CLI uses
|
|
73
|
+
`git worktree remove` without `--force` and never deletes branches.
|
|
39
74
|
|
|
40
|
-
|
|
75
|
+
The content-equivalence check recognizes branches integrated through rebase or
|
|
76
|
+
squash merge. It uses `git merge-tree`, does not modify either worktree, and is
|
|
77
|
+
only available with Git 2.38 or newer. Detached worktrees and repositories with
|
|
78
|
+
custom merge drivers keep the stricter ancestry-only rule. Older Git versions
|
|
79
|
+
remain supported but also keep that conservative behavior.
|
|
41
80
|
|
|
42
|
-
A
|
|
81
|
+
### A note about remote branches
|
|
43
82
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
The CLI does not contact GitHub, and a deleted remote branch alone does not
|
|
84
|
+
prove that its work was merged. Run `git fetch --prune` first if you want the
|
|
85
|
+
decision to use the latest remote refs. Squash-merged or rebased branches may
|
|
86
|
+
still be skipped when Git cannot prove that merging them would leave the base
|
|
87
|
+
unchanged. This conservative behavior avoids discarding work based on a guess.
|
|
48
88
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
89
|
+
If a repository has been moved, Git may still hold worktree paths from its old
|
|
90
|
+
location. In that case, repair the metadata with `git worktree repair` before
|
|
91
|
+
running the cleanup again.
|
|
52
92
|
|
|
53
93
|
## Development
|
|
54
94
|
|
|
55
|
-
|
|
56
|
-
`specs/001-core-cleanup/`. Effect's official
|
|
57
|
-
|
|
95
|
+
The project is built with Effect v4 and follows the GitHub Spec Kit workflow.
|
|
96
|
+
The feature contract lives in `specs/001-core-cleanup/`. Effect's official
|
|
97
|
+
source is vendored under `repos/` as a read-only reference using a Git subtree.
|
|
58
98
|
|
|
59
99
|
```bash
|
|
60
100
|
pnpm install
|
|
@@ -254,6 +254,81 @@ const skipped = (candidate, reason, detail, context) => ({
|
|
|
254
254
|
...context?.worktree === void 0 ? {} : { worktree: context.worktree },
|
|
255
255
|
...context?.baseRef === void 0 ? {} : { baseRef: context.baseRef }
|
|
256
256
|
});
|
|
257
|
+
const integrated = (evidence) => ({
|
|
258
|
+
status: "integrated",
|
|
259
|
+
evidence
|
|
260
|
+
});
|
|
261
|
+
const notIntegrated = (detail) => ({
|
|
262
|
+
status: "not-integrated",
|
|
263
|
+
detail
|
|
264
|
+
});
|
|
265
|
+
const integrationGitError = (detail) => ({
|
|
266
|
+
status: "git-error",
|
|
267
|
+
detail
|
|
268
|
+
});
|
|
269
|
+
const assessIntegration = Effect.fn("assessIntegration")(function* (repositoryPath, worktree, baseRef) {
|
|
270
|
+
const git = yield* Git;
|
|
271
|
+
const baseCommitResult = yield* git.run(repositoryPath, [
|
|
272
|
+
"rev-parse",
|
|
273
|
+
"--verify",
|
|
274
|
+
"--end-of-options",
|
|
275
|
+
`${baseRef}^{commit}`
|
|
276
|
+
]);
|
|
277
|
+
if (baseCommitResult.exitCode !== 0) return integrationGitError(trim(baseCommitResult.stderr));
|
|
278
|
+
const baseCommit = trim(baseCommitResult.stdout);
|
|
279
|
+
const ancestor = yield* git.run(repositoryPath, [
|
|
280
|
+
"merge-base",
|
|
281
|
+
"--is-ancestor",
|
|
282
|
+
worktree.head,
|
|
283
|
+
baseCommit
|
|
284
|
+
]);
|
|
285
|
+
if (ancestor.exitCode === 0) return integrated("ancestor");
|
|
286
|
+
if (ancestor.exitCode !== 1) return integrationGitError(trim(ancestor.stderr));
|
|
287
|
+
const baseDetail = `base: ${baseRef}`;
|
|
288
|
+
if (worktree.detached || worktree.branch === void 0) return notIntegrated(`${baseDetail}; content-equivalence proof requires an attached branch`);
|
|
289
|
+
const customDrivers = yield* git.run(repositoryPath, [
|
|
290
|
+
"config",
|
|
291
|
+
"--get-regexp",
|
|
292
|
+
"^merge\\..*\\.driver$"
|
|
293
|
+
]);
|
|
294
|
+
if (customDrivers.exitCode === 0) return notIntegrated(`${baseDetail}; content-equivalence proof is disabled when custom merge drivers are configured`);
|
|
295
|
+
if (customDrivers.exitCode !== 1) return integrationGitError(trim(customDrivers.stderr));
|
|
296
|
+
const defaultDriver = yield* git.run(repositoryPath, [
|
|
297
|
+
"config",
|
|
298
|
+
"--get-all",
|
|
299
|
+
"merge.default"
|
|
300
|
+
]);
|
|
301
|
+
if (defaultDriver.exitCode === 0) return notIntegrated(`${baseDetail}; content-equivalence proof is disabled when a default merge driver is configured`);
|
|
302
|
+
if (defaultDriver.exitCode !== 1) return integrationGitError(trim(defaultDriver.stderr));
|
|
303
|
+
const baseTreeResult = yield* git.run(repositoryPath, [
|
|
304
|
+
"rev-parse",
|
|
305
|
+
"--verify",
|
|
306
|
+
"--end-of-options",
|
|
307
|
+
`${baseCommit}^{tree}`
|
|
308
|
+
]);
|
|
309
|
+
if (baseTreeResult.exitCode !== 0) return integrationGitError(trim(baseTreeResult.stderr));
|
|
310
|
+
const mergeTree = yield* git.run(repositoryPath, [
|
|
311
|
+
"merge-tree",
|
|
312
|
+
"--write-tree",
|
|
313
|
+
baseCommit,
|
|
314
|
+
worktree.head
|
|
315
|
+
]);
|
|
316
|
+
if (mergeTree.exitCode === 1) return notIntegrated(`${baseDetail}; simulated merge has conflicts`);
|
|
317
|
+
if (mergeTree.exitCode !== 0) {
|
|
318
|
+
if (mergeTree.exitCode === 129) return integrationGitError("content-equivalence proof requires Git 2.38 or newer");
|
|
319
|
+
const message = trim(mergeTree.stderr);
|
|
320
|
+
return integrationGitError(message.length === 0 ? "content-equivalence proof requires Git 2.38 or newer" : message);
|
|
321
|
+
}
|
|
322
|
+
if (trim(mergeTree.stdout) !== trim(baseTreeResult.stdout)) return notIntegrated(`${baseDetail}; branch still contributes content not present in the base`);
|
|
323
|
+
const branchHead = yield* git.run(repositoryPath, [
|
|
324
|
+
"rev-parse",
|
|
325
|
+
"--verify",
|
|
326
|
+
"--end-of-options",
|
|
327
|
+
`${worktree.branch}^{commit}`
|
|
328
|
+
]);
|
|
329
|
+
if (branchHead.exitCode !== 0 || trim(branchHead.stdout) !== worktree.head) return notIntegrated(`${baseDetail}; attached branch no longer points at the assessed HEAD`);
|
|
330
|
+
return integrated("content-equivalent");
|
|
331
|
+
});
|
|
257
332
|
const assessCandidate = Effect.fn("assessCandidate")(function* (candidate, currentWorktree) {
|
|
258
333
|
const git = yield* Git;
|
|
259
334
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -310,17 +385,15 @@ const assessCandidate = Effect.fn("assessCandidate")(function* (candidate, curre
|
|
|
310
385
|
if (status.stdout.length > 0) return skipped(candidate, "dirty", void 0, context);
|
|
311
386
|
const baseRef = yield* detectBaseRef(repositoryPath, canonicalMain);
|
|
312
387
|
if (baseRef === void 0) return skipped(candidate, "base-ref-unknown", void 0, context);
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
]);
|
|
319
|
-
if (merged.exitCode === 1) return skipped(candidate, "not-merged", `base: ${baseRef}`, {
|
|
388
|
+
const integration = yield* assessIntegration(repositoryPath, {
|
|
389
|
+
...worktree,
|
|
390
|
+
head: worktree.head
|
|
391
|
+
}, baseRef);
|
|
392
|
+
if (integration.status === "not-integrated") return skipped(candidate, "not-merged", integration.detail, {
|
|
320
393
|
...context,
|
|
321
394
|
baseRef
|
|
322
395
|
});
|
|
323
|
-
if (
|
|
396
|
+
if (integration.status === "git-error") return skipped(candidate, "git-error", integration.detail, {
|
|
324
397
|
...context,
|
|
325
398
|
baseRef
|
|
326
399
|
});
|
|
@@ -329,6 +402,7 @@ const assessCandidate = Effect.fn("assessCandidate")(function* (candidate, curre
|
|
|
329
402
|
repositoryPath,
|
|
330
403
|
worktree,
|
|
331
404
|
baseRef,
|
|
405
|
+
integrationEvidence: integration.evidence,
|
|
332
406
|
status: "removable"
|
|
333
407
|
};
|
|
334
408
|
});
|
|
@@ -340,7 +414,10 @@ const renderPlan = Effect.fn("renderPlan")(function* (roots, assessments) {
|
|
|
340
414
|
const skippedItems = assessments.filter((assessment) => assessment.status === "skipped");
|
|
341
415
|
yield* Console.log(`Removable (${removable.length}):`);
|
|
342
416
|
if (removable.length === 0) yield* Console.log(" - none");
|
|
343
|
-
for (const assessment of removable)
|
|
417
|
+
for (const assessment of removable) {
|
|
418
|
+
const evidence = assessment.integrationEvidence ?? "unknown";
|
|
419
|
+
yield* Console.log(` REMOVE ${assessment.candidate.path} [${assessment.baseRef}; ${evidence}]`);
|
|
420
|
+
}
|
|
344
421
|
yield* Console.log(`Skipped (${skippedItems.length}):`);
|
|
345
422
|
if (skippedItems.length === 0) yield* Console.log(" - none");
|
|
346
423
|
for (const assessment of skippedItems) {
|
|
@@ -421,4 +498,4 @@ const runCleanup = Effect.fn("runCleanup")(function* (options) {
|
|
|
421
498
|
//#endregion
|
|
422
499
|
export { skipReasons as a, skipReasonLabel as i, Git as n, parseWorktreePorcelain as r, runCleanup as t };
|
|
423
500
|
|
|
424
|
-
//# sourceMappingURL=cleanup-
|
|
501
|
+
//# sourceMappingURL=cleanup-DeusOTMj.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup-DeusOTMj.mjs","names":[],"sources":["../src/domain.ts","../src/errors.ts","../src/git.ts","../src/cleanup.ts"],"sourcesContent":["export const skipReasons = [\n \"not-registered\",\n \"symlink\",\n \"main-worktree\",\n \"current-worktree\",\n \"locked\",\n \"dirty\",\n \"base-ref-unknown\",\n \"not-merged\",\n \"git-error\",\n \"outside-root\"\n] as const\n\nexport type SkipReason = (typeof skipReasons)[number]\n\nexport type IntegrationEvidence = \"ancestor\" | \"content-equivalent\"\n\nexport interface TargetRoot {\n readonly path: string\n readonly source: \"explicit\" | \"worktrees\" | \"claude\" | \"codex\"\n}\n\nexport interface RegisteredWorktree {\n readonly path: string\n readonly head?: string\n readonly branch?: string\n readonly detached: boolean\n readonly lockedReason?: string\n readonly prunableReason?: string\n readonly isMain: boolean\n}\n\nexport interface Candidate {\n readonly path: string\n readonly root: TargetRoot\n readonly structuralSkip?: {\n readonly reason: \"symlink\" | \"outside-root\"\n readonly detail: string\n }\n}\n\nexport interface Assessment {\n readonly candidate: Candidate\n readonly repositoryPath?: string\n readonly worktree?: RegisteredWorktree\n readonly baseRef?: string\n readonly integrationEvidence?: IntegrationEvidence\n readonly status: \"removable\" | \"skipped\"\n readonly reason?: SkipReason\n readonly detail?: string\n}\n\nexport interface CleanupResult {\n readonly removed: ReadonlyArray<string>\n readonly revalidationSkipped: ReadonlyArray<Assessment>\n readonly failures: ReadonlyArray<{\n readonly path: string\n readonly message: string\n }>\n}\n\ninterface MutableWorktree {\n path?: string\n head?: string\n branch?: string\n detached: boolean\n lockedReason?: string\n prunableReason?: string\n}\n\nconst finishRecord = (\n records: Array<Omit<RegisteredWorktree, \"isMain\">>,\n current: MutableWorktree\n): void => {\n if (current.path === undefined) return\n records.push({\n path: current.path,\n ...(current.head === undefined ? {} : { head: current.head }),\n ...(current.branch === undefined ? {} : { branch: current.branch }),\n detached: current.detached,\n ...(current.lockedReason === undefined ? {} : { lockedReason: current.lockedReason }),\n ...(current.prunableReason === undefined ? {} : { prunableReason: current.prunableReason })\n })\n}\n\n/** Parse `git worktree list --porcelain -z` without interpreting path bytes as shell text. */\nexport const parseWorktreePorcelain = (input: string): ReadonlyArray<RegisteredWorktree> => {\n const records: Array<Omit<RegisteredWorktree, \"isMain\">> = []\n let current: MutableWorktree = { detached: false }\n\n for (const field of input.split(\"\\0\")) {\n if (field.length === 0) {\n finishRecord(records, current)\n current = { detached: false }\n continue\n }\n\n const separator = field.indexOf(\" \")\n const key = separator === -1 ? field : field.slice(0, separator)\n const value = separator === -1 ? \"\" : field.slice(separator + 1)\n\n if (key === \"worktree\") {\n if (current.path !== undefined) {\n finishRecord(records, current)\n current = { detached: false }\n }\n current.path = value\n } else if (key === \"HEAD\") {\n current.head = value\n } else if (key === \"branch\") {\n current.branch = value\n } else if (key === \"detached\") {\n current.detached = true\n } else if (key === \"locked\") {\n current.lockedReason = value.length === 0 ? \"locked\" : value\n } else if (key === \"prunable\") {\n current.prunableReason = value.length === 0 ? \"prunable\" : value\n }\n }\n\n finishRecord(records, current)\n return records.map((record, index) => ({ ...record, isMain: index === 0 }))\n}\n\nexport const skipReasonLabel: Readonly<Record<SkipReason, string>> = {\n \"not-registered\": \"not a registered Git worktree\",\n symlink: \"symlinked directories are never removed\",\n \"main-worktree\": \"repository main worktree\",\n \"current-worktree\": \"worktree running this command\",\n locked: \"worktree is locked\",\n dirty: \"worktree has tracked or untracked changes\",\n \"base-ref-unknown\": \"could not determine a trusted base branch\",\n \"not-merged\": \"HEAD is not integrated into the base branch\",\n \"git-error\": \"Git state could not be proven\",\n \"outside-root\": \"canonical path is outside the selected root\"\n}\n","import { Schema } from \"effect\"\n\nexport class GitExecutionError extends Schema.TaggedError<GitExecutionError>()(\n \"GitExecutionError\",\n {\n operation: Schema.String,\n message: Schema.String,\n cause: Schema.Defect()\n }\n) {}\n\nexport class InputError extends Schema.TaggedError<InputError>()(\"InputError\", {\n message: Schema.String,\n exitCode: Schema.Int\n}) {}\n\nexport class DiscoveryError extends Schema.TaggedError<DiscoveryError>()(\"DiscoveryError\", {\n path: Schema.String,\n message: Schema.String,\n cause: Schema.Defect()\n}) {}\n\nexport type AppError = GitExecutionError | InputError | DiscoveryError\n","import { Context, Effect, Layer, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\n\nimport { GitExecutionError } from \"./errors.js\"\n\nexport interface GitResult {\n readonly exitCode: number\n readonly stdout: string\n readonly stderr: string\n}\n\nexport interface GitService {\n readonly run: (\n cwd: string,\n args: ReadonlyArray<string>\n ) => Effect.Effect<GitResult, GitExecutionError>\n}\n\nexport class Git extends Context.Service<Git, GitService>()(\"@thanaen/worktree-cleanup/Git\") {\n static readonly layer = Layer.effect(\n Git,\n Effect.gen(function* () {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\n const run = Effect.fn(\"Git.run\")(function* (cwd: string, args: ReadonlyArray<string>) {\n const operation = `git ${args.join(\" \")}`\n const handle = yield* spawner.spawn(ChildProcess.make(\"git\", [...args], { cwd })).pipe(\n Effect.mapError(\n (cause) =>\n new GitExecutionError({\n operation,\n message: `Could not start Git in ${cwd}`,\n cause\n })\n )\n )\n\n const [stdout, stderr, exitCode] = yield* Effect.all(\n [\n Stream.mkString(Stream.decodeText(handle.stdout)),\n Stream.mkString(Stream.decodeText(handle.stderr)),\n handle.exitCode\n ] as const,\n { concurrency: \"unbounded\" }\n ).pipe(\n Effect.mapError(\n (cause) =>\n new GitExecutionError({\n operation,\n message: `Git execution failed in ${cwd}`,\n cause\n })\n )\n )\n\n return {\n stdout,\n stderr,\n exitCode: Number(exitCode)\n }\n }, Effect.scoped)\n\n return Git.of({ run })\n })\n )\n}\n","import { Console, Effect, FileSystem, Option, Path } from \"effect\"\nimport { Prompt } from \"effect/unstable/cli\"\n\nimport type {\n Assessment,\n Candidate,\n CleanupResult,\n IntegrationEvidence,\n RegisteredWorktree,\n TargetRoot\n} from \"./domain.js\"\nimport { parseWorktreePorcelain, skipReasonLabel } from \"./domain.js\"\nimport { DiscoveryError, InputError } from \"./errors.js\"\nimport { Git } from \"./git.js\"\n\nconst smartRoots = [\n { relative: \"worktrees\", source: \"worktrees\" },\n { relative: \".claude/worktrees\", source: \"claude\" },\n { relative: \".codex/worktrees\", source: \"codex\" }\n] as const\n\nconst trim = (value: string): string => value.trim()\n\nconst pathKey = (pathService: Path.Path, value: string): string => {\n const normalized = pathService.normalize(value)\n return pathService.sep === \"\\\\\" ? normalized.toLowerCase() : normalized\n}\n\nconst discoveryFailure = (path: string, message: string) => (cause: unknown) =>\n new DiscoveryError({ path, message, cause })\n\nexport const discoverRoots = Effect.fn(\"discoverRoots\")(function* (\n cwd: string,\n explicitDirectory: Option.Option<string>\n) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n\n if (Option.isSome(explicitDirectory)) {\n const requested = path.resolve(cwd, explicitDirectory.value)\n const exists = yield* fs\n .exists(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect --dir\")))\n if (!exists) {\n return yield* new InputError({\n message: `--dir does not exist: ${requested}`,\n exitCode: 2\n })\n }\n const info = yield* fs\n .stat(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect --dir\")))\n if (info.type !== \"Directory\") {\n return yield* new InputError({\n message: `--dir is not a directory: ${requested}`,\n exitCode: 2\n })\n }\n const canonical = yield* fs\n .realPath(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not resolve --dir\")))\n return [{ path: canonical, source: \"explicit\" }] satisfies ReadonlyArray<TargetRoot>\n }\n\n const roots: Array<TargetRoot> = []\n for (const entry of smartRoots) {\n const requested = path.resolve(cwd, entry.relative)\n const exists = yield* fs\n .exists(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect smart default\")))\n if (!exists) continue\n const info = yield* fs\n .stat(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect smart default\")))\n if (info.type !== \"Directory\") continue\n const canonical = yield* fs\n .realPath(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not resolve smart default\")))\n roots.push({ path: canonical, source: entry.source })\n }\n\n return roots\n})\n\nexport const enumerateCandidates = Effect.fn(\"enumerateCandidates\")(function* (\n roots: ReadonlyArray<TargetRoot>\n) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const candidates: Array<Candidate> = []\n\n for (const root of roots) {\n const names = yield* fs\n .readDirectory(root.path)\n .pipe(Effect.mapError(discoveryFailure(root.path, \"Could not list worktree root\")))\n\n for (const name of names.toSorted()) {\n const requested = path.resolve(root.path, name)\n const info = yield* fs\n .stat(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect candidate\")))\n if (info.type !== \"Directory\") continue\n\n const canonical = yield* fs\n .realPath(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not resolve candidate\")))\n const normalizedRequested = path.normalize(requested)\n\n if (canonical !== normalizedRequested) {\n candidates.push({\n path: normalizedRequested,\n root,\n structuralSkip: {\n reason: path.dirname(canonical) === root.path ? \"symlink\" : \"outside-root\",\n detail: `resolves to ${canonical}`\n }\n })\n continue\n }\n if (path.dirname(canonical) !== root.path) {\n candidates.push({\n path: canonical,\n root,\n structuralSkip: {\n reason: \"outside-root\",\n detail: `canonical parent is ${path.dirname(canonical)}`\n }\n })\n continue\n }\n candidates.push({ path: canonical, root })\n }\n }\n\n const unique = new Map<string, Candidate>()\n for (const candidate of candidates) unique.set(candidate.path, candidate)\n return [...unique.values()]\n})\n\nconst findCurrentWorktree = Effect.fn(\"findCurrentWorktree\")(function* (cwd: string) {\n const git = yield* Git\n const fs = yield* FileSystem.FileSystem\n const result = yield* git.run(cwd, [\"rev-parse\", \"--show-toplevel\"])\n if (result.exitCode !== 0) return undefined\n return yield* fs\n .realPath(trim(result.stdout))\n .pipe(Effect.orElseSucceed(() => trim(result.stdout)))\n})\n\nconst detectBaseRef = Effect.fn(\"detectBaseRef\")(function* (\n repositoryPath: string,\n mainWorktree: RegisteredWorktree\n) {\n const git = yield* Git\n const originHead = yield* git.run(repositoryPath, [\n \"symbolic-ref\",\n \"--quiet\",\n \"refs/remotes/origin/HEAD\"\n ])\n if (originHead.exitCode === 0 && trim(originHead.stdout).length > 0)\n return trim(originHead.stdout)\n\n for (const ref of [\"refs/heads/main\", \"refs/heads/master\"]) {\n const exists = yield* git.run(repositoryPath, [\"show-ref\", \"--verify\", \"--quiet\", ref])\n if (exists.exitCode === 0) return ref\n }\n\n if (mainWorktree.branch !== undefined) {\n const exists = yield* git.run(repositoryPath, [\n \"show-ref\",\n \"--verify\",\n \"--quiet\",\n mainWorktree.branch\n ])\n if (exists.exitCode === 0) return mainWorktree.branch\n }\n return undefined\n})\n\nconst skipped = (\n candidate: Candidate,\n reason: Assessment[\"reason\"],\n detail?: string,\n context?: {\n readonly repositoryPath?: string\n readonly worktree?: RegisteredWorktree\n readonly baseRef?: string\n }\n): Assessment => ({\n candidate,\n status: \"skipped\",\n ...(reason === undefined ? {} : { reason }),\n ...(detail === undefined ? {} : { detail }),\n ...(context?.repositoryPath === undefined ? {} : { repositoryPath: context.repositoryPath }),\n ...(context?.worktree === undefined ? {} : { worktree: context.worktree }),\n ...(context?.baseRef === undefined ? {} : { baseRef: context.baseRef })\n})\n\ntype IntegrationAssessment =\n | { readonly status: \"integrated\"; readonly evidence: IntegrationEvidence }\n | { readonly status: \"not-integrated\"; readonly detail: string }\n | { readonly status: \"git-error\"; readonly detail: string }\n\nconst integrated = (evidence: IntegrationEvidence): IntegrationAssessment => ({\n status: \"integrated\",\n evidence\n})\n\nconst notIntegrated = (detail: string): IntegrationAssessment => ({\n status: \"not-integrated\",\n detail\n})\n\nconst integrationGitError = (detail: string): IntegrationAssessment => ({\n status: \"git-error\",\n detail\n})\n\nconst assessIntegration = Effect.fn(\"assessIntegration\")(function* (\n repositoryPath: string,\n worktree: RegisteredWorktree & { readonly head: string },\n baseRef: string\n) {\n const git = yield* Git\n const baseCommitResult = yield* git.run(repositoryPath, [\n \"rev-parse\",\n \"--verify\",\n \"--end-of-options\",\n `${baseRef}^{commit}`\n ])\n if (baseCommitResult.exitCode !== 0) {\n return integrationGitError(trim(baseCommitResult.stderr))\n }\n const baseCommit = trim(baseCommitResult.stdout)\n\n const ancestor = yield* git.run(repositoryPath, [\n \"merge-base\",\n \"--is-ancestor\",\n worktree.head,\n baseCommit\n ])\n if (ancestor.exitCode === 0) return integrated(\"ancestor\")\n if (ancestor.exitCode !== 1) {\n return integrationGitError(trim(ancestor.stderr))\n }\n\n const baseDetail = `base: ${baseRef}`\n if (worktree.detached || worktree.branch === undefined) {\n return notIntegrated(`${baseDetail}; content-equivalence proof requires an attached branch`)\n }\n\n const customDrivers = yield* git.run(repositoryPath, [\n \"config\",\n \"--get-regexp\",\n \"^merge\\\\..*\\\\.driver$\"\n ])\n if (customDrivers.exitCode === 0) {\n return notIntegrated(\n `${baseDetail}; content-equivalence proof is disabled when custom merge drivers are configured`\n )\n }\n if (customDrivers.exitCode !== 1) {\n return integrationGitError(trim(customDrivers.stderr))\n }\n\n const defaultDriver = yield* git.run(repositoryPath, [\"config\", \"--get-all\", \"merge.default\"])\n if (defaultDriver.exitCode === 0) {\n return notIntegrated(\n `${baseDetail}; content-equivalence proof is disabled when a default merge driver is configured`\n )\n }\n if (defaultDriver.exitCode !== 1) {\n return integrationGitError(trim(defaultDriver.stderr))\n }\n\n const baseTreeResult = yield* git.run(repositoryPath, [\n \"rev-parse\",\n \"--verify\",\n \"--end-of-options\",\n `${baseCommit}^{tree}`\n ])\n if (baseTreeResult.exitCode !== 0) {\n return integrationGitError(trim(baseTreeResult.stderr))\n }\n\n const mergeTree = yield* git.run(repositoryPath, [\n \"merge-tree\",\n \"--write-tree\",\n baseCommit,\n worktree.head\n ])\n if (mergeTree.exitCode === 1) {\n return notIntegrated(`${baseDetail}; simulated merge has conflicts`)\n }\n if (mergeTree.exitCode !== 0) {\n if (mergeTree.exitCode === 129) {\n return integrationGitError(\"content-equivalence proof requires Git 2.38 or newer\")\n }\n const message = trim(mergeTree.stderr)\n return integrationGitError(\n message.length === 0 ? \"content-equivalence proof requires Git 2.38 or newer\" : message\n )\n }\n if (trim(mergeTree.stdout) !== trim(baseTreeResult.stdout)) {\n return notIntegrated(`${baseDetail}; branch still contributes content not present in the base`)\n }\n\n const branchHead = yield* git.run(repositoryPath, [\n \"rev-parse\",\n \"--verify\",\n \"--end-of-options\",\n `${worktree.branch}^{commit}`\n ])\n if (branchHead.exitCode !== 0 || trim(branchHead.stdout) !== worktree.head) {\n return notIntegrated(`${baseDetail}; attached branch no longer points at the assessed HEAD`)\n }\n\n return integrated(\"content-equivalent\")\n})\n\nexport const assessCandidate = Effect.fn(\"assessCandidate\")(function* (\n candidate: Candidate,\n currentWorktree: string | undefined\n) {\n const git = yield* Git\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n\n if (candidate.structuralSkip !== undefined) {\n return skipped(candidate, candidate.structuralSkip.reason, candidate.structuralSkip.detail)\n }\n\n const repository = yield* git.run(candidate.path, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--git-common-dir\"\n ])\n if (repository.exitCode !== 0) {\n return skipped(candidate, \"not-registered\", trim(repository.stderr))\n }\n\n const listResult = yield* git.run(candidate.path, [\"worktree\", \"list\", \"--porcelain\", \"-z\"])\n if (listResult.exitCode !== 0) {\n return skipped(candidate, \"git-error\", trim(listResult.stderr))\n }\n const worktrees = parseWorktreePorcelain(listResult.stdout)\n const mainWorktree = worktrees[0]\n if (mainWorktree === undefined) {\n return skipped(candidate, \"git-error\", \"Git returned an empty worktree list\")\n }\n\n const topLevel = yield* git.run(candidate.path, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--show-toplevel\"\n ])\n if (topLevel.exitCode !== 0) {\n return skipped(candidate, \"git-error\", trim(topLevel.stderr))\n }\n\n // Git for Windows can spell the same path using either its long form or an\n // 8.3 component (for example `runneradmin` versus `RUNNER~1`). Match the\n // path reported by Git from inside the candidate against Git's own list.\n const gitCandidatePath = pathKey(path, trim(topLevel.stdout))\n const registered = worktrees.find((entry) => pathKey(path, entry.path) === gitCandidatePath)\n if (registered === undefined) {\n return skipped(candidate, \"not-registered\")\n }\n const worktree = { ...registered, path: candidate.path }\n const canonicalMain = {\n ...mainWorktree,\n path: yield* fs\n .realPath(mainWorktree.path)\n .pipe(Effect.orElseSucceed(() => path.normalize(mainWorktree.path)))\n }\n const repositoryPath = canonicalMain.path\n const context = { repositoryPath, worktree }\n\n if (worktree.isMain) return skipped(candidate, \"main-worktree\", undefined, context)\n if (currentWorktree === candidate.path) {\n return skipped(candidate, \"current-worktree\", undefined, context)\n }\n if (worktree.lockedReason !== undefined) {\n return skipped(candidate, \"locked\", worktree.lockedReason, context)\n }\n if (worktree.head === undefined) {\n return skipped(candidate, \"git-error\", \"Git did not report a HEAD\", context)\n }\n\n const status = yield* git.run(candidate.path, [\"status\", \"--porcelain\", \"--untracked-files=all\"])\n if (status.exitCode !== 0) return skipped(candidate, \"git-error\", trim(status.stderr), context)\n if (status.stdout.length > 0) return skipped(candidate, \"dirty\", undefined, context)\n\n const baseRef = yield* detectBaseRef(repositoryPath, canonicalMain)\n if (baseRef === undefined) return skipped(candidate, \"base-ref-unknown\", undefined, context)\n\n const integration = yield* assessIntegration(\n repositoryPath,\n { ...worktree, head: worktree.head },\n baseRef\n )\n if (integration.status === \"not-integrated\") {\n return skipped(candidate, \"not-merged\", integration.detail, { ...context, baseRef })\n }\n if (integration.status === \"git-error\") {\n return skipped(candidate, \"git-error\", integration.detail, { ...context, baseRef })\n }\n\n return {\n candidate,\n repositoryPath,\n worktree,\n baseRef,\n integrationEvidence: integration.evidence,\n status: \"removable\" as const\n }\n})\n\nconst renderPlan = Effect.fn(\"renderPlan\")(function* (\n roots: ReadonlyArray<TargetRoot>,\n assessments: ReadonlyArray<Assessment>\n) {\n yield* Console.log(\"Worktree cleanup plan\")\n yield* Console.log(\"Roots:\")\n for (const root of roots) yield* Console.log(` - ${root.path} (${root.source})`)\n\n const removable = assessments.filter((assessment) => assessment.status === \"removable\")\n const skippedItems = assessments.filter((assessment) => assessment.status === \"skipped\")\n\n yield* Console.log(`Removable (${removable.length}):`)\n if (removable.length === 0) yield* Console.log(\" - none\")\n for (const assessment of removable) {\n const evidence = assessment.integrationEvidence ?? \"unknown\"\n yield* Console.log(` REMOVE ${assessment.candidate.path} [${assessment.baseRef}; ${evidence}]`)\n }\n\n yield* Console.log(`Skipped (${skippedItems.length}):`)\n if (skippedItems.length === 0) yield* Console.log(\" - none\")\n for (const assessment of skippedItems) {\n const reason = assessment.reason === undefined ? \"unknown\" : skipReasonLabel[assessment.reason]\n const detail =\n assessment.detail === undefined || assessment.detail.length === 0\n ? \"\"\n : `: ${assessment.detail}`\n yield* Console.log(` skip ${assessment.candidate.path} — ${reason}${detail}`)\n }\n})\n\nexport interface CleanupOptions {\n readonly cwd: string\n readonly directory: Option.Option<string>\n readonly yes: boolean\n readonly interactive: boolean\n}\n\nexport const runCleanup = Effect.fn(\"runCleanup\")(function* (options: CleanupOptions) {\n const git = yield* Git\n const roots = yield* discoverRoots(options.cwd, options.directory)\n if (roots.length === 0) {\n yield* Console.log(\n \"No worktree roots found (checked worktrees, .claude/worktrees, .codex/worktrees).\"\n )\n return {\n removed: [],\n revalidationSkipped: [],\n failures: []\n } satisfies CleanupResult\n }\n\n const candidates = yield* enumerateCandidates(roots)\n const currentWorktree = yield* findCurrentWorktree(options.cwd)\n const assessments = yield* Effect.forEach(\n candidates,\n (candidate) => assessCandidate(candidate, currentWorktree),\n { concurrency: 4 }\n )\n yield* renderPlan(roots, assessments)\n\n const removable = assessments.filter(\n (\n assessment\n ): assessment is Assessment & {\n readonly repositoryPath: string\n readonly status: \"removable\"\n } => assessment.status === \"removable\" && assessment.repositoryPath !== undefined\n )\n if (removable.length === 0) {\n yield* Console.log(\"Nothing to remove.\")\n return {\n removed: [],\n revalidationSkipped: [],\n failures: []\n } satisfies CleanupResult\n }\n\n if (!options.yes && !options.interactive) {\n return yield* new InputError({\n message: \"Refusing to delete without an interactive terminal. Pass --yes to approve.\",\n exitCode: 2\n })\n }\n\n const confirmed = options.yes\n ? true\n : yield* Prompt.run(\n Prompt.confirm({\n message: `Remove ${removable.length} stale worktree${removable.length === 1 ? \"\" : \"s\"}?`,\n initial: false\n })\n ).pipe(Effect.orElseSucceed(() => false))\n\n if (!confirmed) {\n yield* Console.log(\"Cleanup cancelled; nothing was removed.\")\n return {\n removed: [],\n revalidationSkipped: [],\n failures: []\n } satisfies CleanupResult\n }\n\n const removed: Array<string> = []\n const revalidationSkipped: Array<Assessment> = []\n const failures: Array<{ path: string; message: string }> = []\n\n for (const planned of removable) {\n const revalidated = yield* assessCandidate(planned.candidate, currentWorktree)\n if (revalidated.status !== \"removable\" || revalidated.repositoryPath === undefined) {\n revalidationSkipped.push(revalidated)\n continue\n }\n\n const removal = yield* git.run(revalidated.repositoryPath, [\n \"worktree\",\n \"remove\",\n \"--\",\n revalidated.candidate.path\n ])\n if (removal.exitCode === 0) {\n removed.push(revalidated.candidate.path)\n } else {\n failures.push({\n path: revalidated.candidate.path,\n message: trim(removal.stderr) || `git exited with ${removal.exitCode}`\n })\n }\n }\n\n yield* Console.log(\n `Cleanup complete: ${removed.length} removed, ${revalidationSkipped.length} skipped after revalidation, ${failures.length} failed.`\n )\n for (const failure of failures) yield* Console.error(`Failed ${failure.path}: ${failure.message}`)\n\n if (failures.length > 0) {\n process.exitCode = 1\n }\n return { removed, revalidationSkipped, failures } satisfies CleanupResult\n})\n"],"mappings":";;;;;AAAA,MAAa,cAAc;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AA2DA,MAAM,gBACJ,SACA,YACS;CACT,IAAI,QAAQ,SAAS,KAAA,GAAW;CAChC,QAAQ,KAAK;EACX,MAAM,QAAQ;EACd,GAAI,QAAQ,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,QAAQ,KAAK;EAC3D,GAAI,QAAQ,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,QAAQ,OAAO;EACjE,UAAU,QAAQ;EAClB,GAAI,QAAQ,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,QAAQ,aAAa;EACnF,GAAI,QAAQ,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,gBAAgB,QAAQ,eAAe;CAC3F,CAAC;AACH;;AAGA,MAAa,0BAA0B,UAAqD;CAC1F,MAAM,UAAqD,CAAC;CAC5D,IAAI,UAA2B,EAAE,UAAU,MAAM;CAEjD,KAAK,MAAM,SAAS,MAAM,MAAM,IAAI,GAAG;EACrC,IAAI,MAAM,WAAW,GAAG;GACtB,aAAa,SAAS,OAAO;GAC7B,UAAU,EAAE,UAAU,MAAM;GAC5B;EACF;EAEA,MAAM,YAAY,MAAM,QAAQ,GAAG;EACnC,MAAM,MAAM,cAAc,KAAK,QAAQ,MAAM,MAAM,GAAG,SAAS;EAC/D,MAAM,QAAQ,cAAc,KAAK,KAAK,MAAM,MAAM,YAAY,CAAC;EAE/D,IAAI,QAAQ,YAAY;GACtB,IAAI,QAAQ,SAAS,KAAA,GAAW;IAC9B,aAAa,SAAS,OAAO;IAC7B,UAAU,EAAE,UAAU,MAAM;GAC9B;GACA,QAAQ,OAAO;EACjB,OAAO,IAAI,QAAQ,QACjB,QAAQ,OAAO;OACV,IAAI,QAAQ,UACjB,QAAQ,SAAS;OACZ,IAAI,QAAQ,YACjB,QAAQ,WAAW;OACd,IAAI,QAAQ,UACjB,QAAQ,eAAe,MAAM,WAAW,IAAI,WAAW;OAClD,IAAI,QAAQ,YACjB,QAAQ,iBAAiB,MAAM,WAAW,IAAI,aAAa;CAE/D;CAEA,aAAa,SAAS,OAAO;CAC7B,OAAO,QAAQ,KAAK,QAAQ,WAAW;EAAE,GAAG;EAAQ,QAAQ,UAAU;CAAE,EAAE;AAC5E;AAEA,MAAa,kBAAwD;CACnE,kBAAkB;CAClB,SAAS;CACT,iBAAiB;CACjB,oBAAoB;CACpB,QAAQ;CACR,OAAO;CACP,oBAAoB;CACpB,cAAc;CACd,aAAa;CACb,gBAAgB;AAClB;;;ACrIA,IAAa,oBAAb,cAAuC,OAAO,YAA+B,CAAC,CAC5E,qBACA;CACE,WAAW,OAAO;CAClB,SAAS,OAAO;CAChB,OAAO,OAAO,OAAO;AACvB,CACF,CAAC,CAAC,CAAC;AAEH,IAAa,aAAb,cAAgC,OAAO,YAAwB,CAAC,CAAC,cAAc;CAC7E,SAAS,OAAO;CAChB,UAAU,OAAO;AACnB,CAAC,CAAC,CAAC,CAAC;AAEJ,IAAa,iBAAb,cAAoC,OAAO,YAA4B,CAAC,CAAC,kBAAkB;CACzF,MAAM,OAAO;CACb,SAAS,OAAO;CAChB,OAAO,OAAO,OAAO;AACvB,CAAC,CAAC,CAAC,CAAC;;;ACFJ,IAAa,MAAb,MAAa,YAAY,QAAQ,QAAyB,CAAC,CAAC,+BAA+B,CAAC,CAAC;CAC3F,OAAgB,QAAQ,MAAM,OAC5B,KACA,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,oBAAoB;EAE3C,MAAM,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,WAAW,KAAa,MAA6B;GACpF,MAAM,YAAY,OAAO,KAAK,KAAK,GAAG;GACtC,MAAM,SAAS,OAAO,QAAQ,MAAM,aAAa,KAAK,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAChF,OAAO,UACJ,UACC,IAAI,kBAAkB;IACpB;IACA,SAAS,0BAA0B;IACnC;GACF,CAAC,CACL,CACF;GAEA,MAAM,CAAC,QAAQ,QAAQ,YAAY,OAAO,OAAO,IAC/C;IACE,OAAO,SAAS,OAAO,WAAW,OAAO,MAAM,CAAC;IAChD,OAAO,SAAS,OAAO,WAAW,OAAO,MAAM,CAAC;IAChD,OAAO;GACT,GACA,EAAE,aAAa,YAAY,CAC7B,CAAC,CAAC,KACA,OAAO,UACJ,UACC,IAAI,kBAAkB;IACpB;IACA,SAAS,2BAA2B;IACpC;GACF,CAAC,CACL,CACF;GAEA,OAAO;IACL;IACA;IACA,UAAU,OAAO,QAAQ;GAC3B;EACF,GAAG,OAAO,MAAM;EAEhB,OAAO,IAAI,GAAG,EAAE,IAAI,CAAC;CACvB,CAAC,CACH;AACF;;;AClDA,MAAM,aAAa;CACjB;EAAE,UAAU;EAAa,QAAQ;CAAY;CAC7C;EAAE,UAAU;EAAqB,QAAQ;CAAS;CAClD;EAAE,UAAU;EAAoB,QAAQ;CAAQ;AAClD;AAEA,MAAM,QAAQ,UAA0B,MAAM,KAAK;AAEnD,MAAM,WAAW,aAAwB,UAA0B;CACjE,MAAM,aAAa,YAAY,UAAU,KAAK;CAC9C,OAAO,YAAY,QAAQ,OAAO,WAAW,YAAY,IAAI;AAC/D;AAEA,MAAM,oBAAoB,MAAc,aAAqB,UAC3D,IAAI,eAAe;CAAE;CAAM;CAAS;AAAM,CAAC;AAE7C,MAAa,gBAAgB,OAAO,GAAG,eAAe,CAAC,CAAC,WACtD,KACA,mBACA;CACA,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CAEzB,IAAI,OAAO,OAAO,iBAAiB,GAAG;EACpC,MAAM,YAAY,KAAK,QAAQ,KAAK,kBAAkB,KAAK;EAI3D,IAAI,EAAC,OAHiB,GACnB,OAAO,SAAS,CAAC,CACjB,KAAK,OAAO,SAAS,iBAAiB,WAAW,yBAAyB,CAAC,CAAC,IAE7E,OAAO,OAAO,IAAI,WAAW;GAC3B,SAAS,yBAAyB;GAClC,UAAU;EACZ,CAAC;EAKH,KAAI,OAHgB,GACjB,KAAK,SAAS,CAAC,CACf,KAAK,OAAO,SAAS,iBAAiB,WAAW,yBAAyB,CAAC,CAAC,EAAA,CACtE,SAAS,aAChB,OAAO,OAAO,IAAI,WAAW;GAC3B,SAAS,6BAA6B;GACtC,UAAU;EACZ,CAAC;EAKH,OAAO,CAAC;GAAE,MAAM,OAHS,GACtB,SAAS,SAAS,CAAC,CACnB,KAAK,OAAO,SAAS,iBAAiB,WAAW,yBAAyB,CAAC,CAAC;GACpD,QAAQ;EAAW,CAAC;CACjD;CAEA,MAAM,QAA2B,CAAC;CAClC,KAAK,MAAM,SAAS,YAAY;EAC9B,MAAM,YAAY,KAAK,QAAQ,KAAK,MAAM,QAAQ;EAIlD,IAAI,EAAC,OAHiB,GACnB,OAAO,SAAS,CAAC,CACjB,KAAK,OAAO,SAAS,iBAAiB,WAAW,iCAAiC,CAAC,CAAC,IAC1E;EAIb,KAAI,OAHgB,GACjB,KAAK,SAAS,CAAC,CACf,KAAK,OAAO,SAAS,iBAAiB,WAAW,iCAAiC,CAAC,CAAC,EAAA,CAC9E,SAAS,aAAa;EAC/B,MAAM,YAAY,OAAO,GACtB,SAAS,SAAS,CAAC,CACnB,KAAK,OAAO,SAAS,iBAAiB,WAAW,iCAAiC,CAAC,CAAC;EACvF,MAAM,KAAK;GAAE,MAAM;GAAW,QAAQ,MAAM;EAAO,CAAC;CACtD;CAEA,OAAO;AACT,CAAC;AAED,MAAa,sBAAsB,OAAO,GAAG,qBAAqB,CAAC,CAAC,WAClE,OACA;CACA,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,aAA+B,CAAC;CAEtC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,OAAO,GAClB,cAAc,KAAK,IAAI,CAAC,CACxB,KAAK,OAAO,SAAS,iBAAiB,KAAK,MAAM,8BAA8B,CAAC,CAAC;EAEpF,KAAK,MAAM,QAAQ,MAAM,SAAS,GAAG;GACnC,MAAM,YAAY,KAAK,QAAQ,KAAK,MAAM,IAAI;GAI9C,KAAI,OAHgB,GACjB,KAAK,SAAS,CAAC,CACf,KAAK,OAAO,SAAS,iBAAiB,WAAW,6BAA6B,CAAC,CAAC,EAAA,CAC1E,SAAS,aAAa;GAE/B,MAAM,YAAY,OAAO,GACtB,SAAS,SAAS,CAAC,CACnB,KAAK,OAAO,SAAS,iBAAiB,WAAW,6BAA6B,CAAC,CAAC;GACnF,MAAM,sBAAsB,KAAK,UAAU,SAAS;GAEpD,IAAI,cAAc,qBAAqB;IACrC,WAAW,KAAK;KACd,MAAM;KACN;KACA,gBAAgB;MACd,QAAQ,KAAK,QAAQ,SAAS,MAAM,KAAK,OAAO,YAAY;MAC5D,QAAQ,eAAe;KACzB;IACF,CAAC;IACD;GACF;GACA,IAAI,KAAK,QAAQ,SAAS,MAAM,KAAK,MAAM;IACzC,WAAW,KAAK;KACd,MAAM;KACN;KACA,gBAAgB;MACd,QAAQ;MACR,QAAQ,uBAAuB,KAAK,QAAQ,SAAS;KACvD;IACF,CAAC;IACD;GACF;GACA,WAAW,KAAK;IAAE,MAAM;IAAW;GAAK,CAAC;EAC3C;CACF;CAEA,MAAM,yBAAS,IAAI,IAAuB;CAC1C,KAAK,MAAM,aAAa,YAAY,OAAO,IAAI,UAAU,MAAM,SAAS;CACxE,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC;AAC5B,CAAC;AAED,MAAM,sBAAsB,OAAO,GAAG,qBAAqB,CAAC,CAAC,WAAW,KAAa;CACnF,MAAM,MAAM,OAAO;CACnB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,SAAS,OAAO,IAAI,IAAI,KAAK,CAAC,aAAa,iBAAiB,CAAC;CACnE,IAAI,OAAO,aAAa,GAAG,OAAO,KAAA;CAClC,OAAO,OAAO,GACX,SAAS,KAAK,OAAO,MAAM,CAAC,CAAC,CAC7B,KAAK,OAAO,oBAAoB,KAAK,OAAO,MAAM,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,gBAAgB,OAAO,GAAG,eAAe,CAAC,CAAC,WAC/C,gBACA,cACA;CACA,MAAM,MAAM,OAAO;CACnB,MAAM,aAAa,OAAO,IAAI,IAAI,gBAAgB;EAChD;EACA;EACA;CACF,CAAC;CACD,IAAI,WAAW,aAAa,KAAK,KAAK,WAAW,MAAM,CAAC,CAAC,SAAS,GAChE,OAAO,KAAK,WAAW,MAAM;CAE/B,KAAK,MAAM,OAAO,CAAC,mBAAmB,mBAAmB,GAEvD,KAAI,OADkB,IAAI,IAAI,gBAAgB;EAAC;EAAY;EAAY;EAAW;CAAG,CAAC,EAAA,CAC3E,aAAa,GAAG,OAAO;CAGpC,IAAI,aAAa,WAAW,KAAA,GAOtB;OAAA,OANkB,IAAI,IAAI,gBAAgB;GAC5C;GACA;GACA;GACA,aAAa;EACf,CAAC,EAAA,CACU,aAAa,GAAG,OAAO,aAAa;CAAA;AAGnD,CAAC;AAED,MAAM,WACJ,WACA,QACA,QACA,aAKgB;CAChB;CACA,QAAQ;CACR,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;CACzC,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;CACzC,GAAI,SAAS,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,gBAAgB,QAAQ,eAAe;CAC1F,GAAI,SAAS,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,QAAQ,SAAS;CACxE,GAAI,SAAS,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,QAAQ,QAAQ;AACvE;AAOA,MAAM,cAAc,cAA0D;CAC5E,QAAQ;CACR;AACF;AAEA,MAAM,iBAAiB,YAA2C;CAChE,QAAQ;CACR;AACF;AAEA,MAAM,uBAAuB,YAA2C;CACtE,QAAQ;CACR;AACF;AAEA,MAAM,oBAAoB,OAAO,GAAG,mBAAmB,CAAC,CAAC,WACvD,gBACA,UACA,SACA;CACA,MAAM,MAAM,OAAO;CACnB,MAAM,mBAAmB,OAAO,IAAI,IAAI,gBAAgB;EACtD;EACA;EACA;EACA,GAAG,QAAQ;CACb,CAAC;CACD,IAAI,iBAAiB,aAAa,GAChC,OAAO,oBAAoB,KAAK,iBAAiB,MAAM,CAAC;CAE1D,MAAM,aAAa,KAAK,iBAAiB,MAAM;CAE/C,MAAM,WAAW,OAAO,IAAI,IAAI,gBAAgB;EAC9C;EACA;EACA,SAAS;EACT;CACF,CAAC;CACD,IAAI,SAAS,aAAa,GAAG,OAAO,WAAW,UAAU;CACzD,IAAI,SAAS,aAAa,GACxB,OAAO,oBAAoB,KAAK,SAAS,MAAM,CAAC;CAGlD,MAAM,aAAa,SAAS;CAC5B,IAAI,SAAS,YAAY,SAAS,WAAW,KAAA,GAC3C,OAAO,cAAc,GAAG,WAAW,wDAAwD;CAG7F,MAAM,gBAAgB,OAAO,IAAI,IAAI,gBAAgB;EACnD;EACA;EACA;CACF,CAAC;CACD,IAAI,cAAc,aAAa,GAC7B,OAAO,cACL,GAAG,WAAW,iFAChB;CAEF,IAAI,cAAc,aAAa,GAC7B,OAAO,oBAAoB,KAAK,cAAc,MAAM,CAAC;CAGvD,MAAM,gBAAgB,OAAO,IAAI,IAAI,gBAAgB;EAAC;EAAU;EAAa;CAAe,CAAC;CAC7F,IAAI,cAAc,aAAa,GAC7B,OAAO,cACL,GAAG,WAAW,kFAChB;CAEF,IAAI,cAAc,aAAa,GAC7B,OAAO,oBAAoB,KAAK,cAAc,MAAM,CAAC;CAGvD,MAAM,iBAAiB,OAAO,IAAI,IAAI,gBAAgB;EACpD;EACA;EACA;EACA,GAAG,WAAW;CAChB,CAAC;CACD,IAAI,eAAe,aAAa,GAC9B,OAAO,oBAAoB,KAAK,eAAe,MAAM,CAAC;CAGxD,MAAM,YAAY,OAAO,IAAI,IAAI,gBAAgB;EAC/C;EACA;EACA;EACA,SAAS;CACX,CAAC;CACD,IAAI,UAAU,aAAa,GACzB,OAAO,cAAc,GAAG,WAAW,gCAAgC;CAErE,IAAI,UAAU,aAAa,GAAG;EAC5B,IAAI,UAAU,aAAa,KACzB,OAAO,oBAAoB,sDAAsD;EAEnF,MAAM,UAAU,KAAK,UAAU,MAAM;EACrC,OAAO,oBACL,QAAQ,WAAW,IAAI,yDAAyD,OAClF;CACF;CACA,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,eAAe,MAAM,GACvD,OAAO,cAAc,GAAG,WAAW,2DAA2D;CAGhG,MAAM,aAAa,OAAO,IAAI,IAAI,gBAAgB;EAChD;EACA;EACA;EACA,GAAG,SAAS,OAAO;CACrB,CAAC;CACD,IAAI,WAAW,aAAa,KAAK,KAAK,WAAW,MAAM,MAAM,SAAS,MACpE,OAAO,cAAc,GAAG,WAAW,wDAAwD;CAG7F,OAAO,WAAW,oBAAoB;AACxC,CAAC;AAED,MAAa,kBAAkB,OAAO,GAAG,iBAAiB,CAAC,CAAC,WAC1D,WACA,iBACA;CACA,MAAM,MAAM,OAAO;CACnB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CAEzB,IAAI,UAAU,mBAAmB,KAAA,GAC/B,OAAO,QAAQ,WAAW,UAAU,eAAe,QAAQ,UAAU,eAAe,MAAM;CAG5F,MAAM,aAAa,OAAO,IAAI,IAAI,UAAU,MAAM;EAChD;EACA;EACA;CACF,CAAC;CACD,IAAI,WAAW,aAAa,GAC1B,OAAO,QAAQ,WAAW,kBAAkB,KAAK,WAAW,MAAM,CAAC;CAGrE,MAAM,aAAa,OAAO,IAAI,IAAI,UAAU,MAAM;EAAC;EAAY;EAAQ;EAAe;CAAI,CAAC;CAC3F,IAAI,WAAW,aAAa,GAC1B,OAAO,QAAQ,WAAW,aAAa,KAAK,WAAW,MAAM,CAAC;CAEhE,MAAM,YAAY,uBAAuB,WAAW,MAAM;CAC1D,MAAM,eAAe,UAAU;CAC/B,IAAI,iBAAiB,KAAA,GACnB,OAAO,QAAQ,WAAW,aAAa,qCAAqC;CAG9E,MAAM,WAAW,OAAO,IAAI,IAAI,UAAU,MAAM;EAC9C;EACA;EACA;CACF,CAAC;CACD,IAAI,SAAS,aAAa,GACxB,OAAO,QAAQ,WAAW,aAAa,KAAK,SAAS,MAAM,CAAC;CAM9D,MAAM,mBAAmB,QAAQ,MAAM,KAAK,SAAS,MAAM,CAAC;CAC5D,MAAM,aAAa,UAAU,MAAM,UAAU,QAAQ,MAAM,MAAM,IAAI,MAAM,gBAAgB;CAC3F,IAAI,eAAe,KAAA,GACjB,OAAO,QAAQ,WAAW,gBAAgB;CAE5C,MAAM,WAAW;EAAE,GAAG;EAAY,MAAM,UAAU;CAAK;CACvD,MAAM,gBAAgB;EACpB,GAAG;EACH,MAAM,OAAO,GACV,SAAS,aAAa,IAAI,CAAC,CAC3B,KAAK,OAAO,oBAAoB,KAAK,UAAU,aAAa,IAAI,CAAC,CAAC;CACvE;CACA,MAAM,iBAAiB,cAAc;CACrC,MAAM,UAAU;EAAE;EAAgB;CAAS;CAE3C,IAAI,SAAS,QAAQ,OAAO,QAAQ,WAAW,iBAAiB,KAAA,GAAW,OAAO;CAClF,IAAI,oBAAoB,UAAU,MAChC,OAAO,QAAQ,WAAW,oBAAoB,KAAA,GAAW,OAAO;CAElE,IAAI,SAAS,iBAAiB,KAAA,GAC5B,OAAO,QAAQ,WAAW,UAAU,SAAS,cAAc,OAAO;CAEpE,IAAI,SAAS,SAAS,KAAA,GACpB,OAAO,QAAQ,WAAW,aAAa,6BAA6B,OAAO;CAG7E,MAAM,SAAS,OAAO,IAAI,IAAI,UAAU,MAAM;EAAC;EAAU;EAAe;CAAuB,CAAC;CAChG,IAAI,OAAO,aAAa,GAAG,OAAO,QAAQ,WAAW,aAAa,KAAK,OAAO,MAAM,GAAG,OAAO;CAC9F,IAAI,OAAO,OAAO,SAAS,GAAG,OAAO,QAAQ,WAAW,SAAS,KAAA,GAAW,OAAO;CAEnF,MAAM,UAAU,OAAO,cAAc,gBAAgB,aAAa;CAClE,IAAI,YAAY,KAAA,GAAW,OAAO,QAAQ,WAAW,oBAAoB,KAAA,GAAW,OAAO;CAE3F,MAAM,cAAc,OAAO,kBACzB,gBACA;EAAE,GAAG;EAAU,MAAM,SAAS;CAAK,GACnC,OACF;CACA,IAAI,YAAY,WAAW,kBACzB,OAAO,QAAQ,WAAW,cAAc,YAAY,QAAQ;EAAE,GAAG;EAAS;CAAQ,CAAC;CAErF,IAAI,YAAY,WAAW,aACzB,OAAO,QAAQ,WAAW,aAAa,YAAY,QAAQ;EAAE,GAAG;EAAS;CAAQ,CAAC;CAGpF,OAAO;EACL;EACA;EACA;EACA;EACA,qBAAqB,YAAY;EACjC,QAAQ;CACV;AACF,CAAC;AAED,MAAM,aAAa,OAAO,GAAG,YAAY,CAAC,CAAC,WACzC,OACA,aACA;CACA,OAAO,QAAQ,IAAI,uBAAuB;CAC1C,OAAO,QAAQ,IAAI,QAAQ;CAC3B,KAAK,MAAM,QAAQ,OAAO,OAAO,QAAQ,IAAI,OAAO,KAAK,KAAK,IAAI,KAAK,OAAO,EAAE;CAEhF,MAAM,YAAY,YAAY,QAAQ,eAAe,WAAW,WAAW,WAAW;CACtF,MAAM,eAAe,YAAY,QAAQ,eAAe,WAAW,WAAW,SAAS;CAEvF,OAAO,QAAQ,IAAI,cAAc,UAAU,OAAO,GAAG;CACrD,IAAI,UAAU,WAAW,GAAG,OAAO,QAAQ,IAAI,UAAU;CACzD,KAAK,MAAM,cAAc,WAAW;EAClC,MAAM,WAAW,WAAW,uBAAuB;EACnD,OAAO,QAAQ,IAAI,YAAY,WAAW,UAAU,KAAK,IAAI,WAAW,QAAQ,IAAI,SAAS,EAAE;CACjG;CAEA,OAAO,QAAQ,IAAI,YAAY,aAAa,OAAO,GAAG;CACtD,IAAI,aAAa,WAAW,GAAG,OAAO,QAAQ,IAAI,UAAU;CAC5D,KAAK,MAAM,cAAc,cAAc;EACrC,MAAM,SAAS,WAAW,WAAW,KAAA,IAAY,YAAY,gBAAgB,WAAW;EACxF,MAAM,SACJ,WAAW,WAAW,KAAA,KAAa,WAAW,OAAO,WAAW,IAC5D,KACA,KAAK,WAAW;EACtB,OAAO,QAAQ,IAAI,UAAU,WAAW,UAAU,KAAK,KAAK,SAAS,QAAQ;CAC/E;AACF,CAAC;AASD,MAAa,aAAa,OAAO,GAAG,YAAY,CAAC,CAAC,WAAW,SAAyB;CACpF,MAAM,MAAM,OAAO;CACnB,MAAM,QAAQ,OAAO,cAAc,QAAQ,KAAK,QAAQ,SAAS;CACjE,IAAI,MAAM,WAAW,GAAG;EACtB,OAAO,QAAQ,IACb,mFACF;EACA,OAAO;GACL,SAAS,CAAC;GACV,qBAAqB,CAAC;GACtB,UAAU,CAAC;EACb;CACF;CAEA,MAAM,aAAa,OAAO,oBAAoB,KAAK;CACnD,MAAM,kBAAkB,OAAO,oBAAoB,QAAQ,GAAG;CAC9D,MAAM,cAAc,OAAO,OAAO,QAChC,aACC,cAAc,gBAAgB,WAAW,eAAe,GACzD,EAAE,aAAa,EAAE,CACnB;CACA,OAAO,WAAW,OAAO,WAAW;CAEpC,MAAM,YAAY,YAAY,QAE1B,eAIG,WAAW,WAAW,eAAe,WAAW,mBAAmB,KAAA,CAC1E;CACA,IAAI,UAAU,WAAW,GAAG;EAC1B,OAAO,QAAQ,IAAI,oBAAoB;EACvC,OAAO;GACL,SAAS,CAAC;GACV,qBAAqB,CAAC;GACtB,UAAU,CAAC;EACb;CACF;CAEA,IAAI,CAAC,QAAQ,OAAO,CAAC,QAAQ,aAC3B,OAAO,OAAO,IAAI,WAAW;EAC3B,SAAS;EACT,UAAU;CACZ,CAAC;CAYH,IAAI,EATc,QAAQ,MACtB,OACA,OAAO,OAAO,IACZ,OAAO,QAAQ;EACb,SAAS,UAAU,UAAU,OAAO,iBAAiB,UAAU,WAAW,IAAI,KAAK,IAAI;EACvF,SAAS;CACX,CAAC,CACH,CAAC,CAAC,KAAK,OAAO,oBAAoB,KAAK,CAAC,IAE5B;EACd,OAAO,QAAQ,IAAI,yCAAyC;EAC5D,OAAO;GACL,SAAS,CAAC;GACV,qBAAqB,CAAC;GACtB,UAAU,CAAC;EACb;CACF;CAEA,MAAM,UAAyB,CAAC;CAChC,MAAM,sBAAyC,CAAC;CAChD,MAAM,WAAqD,CAAC;CAE5D,KAAK,MAAM,WAAW,WAAW;EAC/B,MAAM,cAAc,OAAO,gBAAgB,QAAQ,WAAW,eAAe;EAC7E,IAAI,YAAY,WAAW,eAAe,YAAY,mBAAmB,KAAA,GAAW;GAClF,oBAAoB,KAAK,WAAW;GACpC;EACF;EAEA,MAAM,UAAU,OAAO,IAAI,IAAI,YAAY,gBAAgB;GACzD;GACA;GACA;GACA,YAAY,UAAU;EACxB,CAAC;EACD,IAAI,QAAQ,aAAa,GACvB,QAAQ,KAAK,YAAY,UAAU,IAAI;OAEvC,SAAS,KAAK;GACZ,MAAM,YAAY,UAAU;GAC5B,SAAS,KAAK,QAAQ,MAAM,KAAK,mBAAmB,QAAQ;EAC9D,CAAC;CAEL;CAEA,OAAO,QAAQ,IACb,qBAAqB,QAAQ,OAAO,YAAY,oBAAoB,OAAO,+BAA+B,SAAS,OAAO,SAC5H;CACA,KAAK,MAAM,WAAW,UAAU,OAAO,QAAQ,MAAM,UAAU,QAAQ,KAAK,IAAI,QAAQ,SAAS;CAEjG,IAAI,SAAS,SAAS,GACpB,QAAQ,WAAW;CAErB,OAAO;EAAE;EAAS;EAAqB;CAAS;AAClD,CAAC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -24,6 +24,7 @@ declare class DiscoveryError extends DiscoveryError_base {}
|
|
|
24
24
|
//#region src/domain.d.ts
|
|
25
25
|
declare const skipReasons: readonly ["not-registered", "symlink", "main-worktree", "current-worktree", "locked", "dirty", "base-ref-unknown", "not-merged", "git-error", "outside-root"];
|
|
26
26
|
type SkipReason = (typeof skipReasons)[number];
|
|
27
|
+
type IntegrationEvidence = "ancestor" | "content-equivalent";
|
|
27
28
|
interface TargetRoot {
|
|
28
29
|
readonly path: string;
|
|
29
30
|
readonly source: "explicit" | "worktrees" | "claude" | "codex";
|
|
@@ -50,6 +51,7 @@ interface Assessment {
|
|
|
50
51
|
readonly repositoryPath?: string;
|
|
51
52
|
readonly worktree?: RegisteredWorktree;
|
|
52
53
|
readonly baseRef?: string;
|
|
54
|
+
readonly integrationEvidence?: IntegrationEvidence;
|
|
53
55
|
readonly status: "removable" | "skipped";
|
|
54
56
|
readonly reason?: SkipReason;
|
|
55
57
|
readonly detail?: string;
|
|
@@ -96,5 +98,5 @@ declare const runCleanup: (options: CleanupOptions) => Effect.Effect<{
|
|
|
96
98
|
}[];
|
|
97
99
|
}, DiscoveryError | GitExecutionError | InputError, Git | Prompt.Environment>;
|
|
98
100
|
//#endregion
|
|
99
|
-
export { type Assessment, type Candidate, type CleanupOptions, type CleanupResult, Git, type GitResult, type GitService, type RegisteredWorktree, type SkipReason, type TargetRoot, parseWorktreePorcelain, runCleanup, skipReasonLabel, skipReasons };
|
|
101
|
+
export { type Assessment, type Candidate, type CleanupOptions, type CleanupResult, Git, type GitResult, type GitService, type IntegrationEvidence, type RegisteredWorktree, type SkipReason, type TargetRoot, parseWorktreePorcelain, runCleanup, skipReasonLabel, skipReasons };
|
|
100
102
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/errors.ts","../src/domain.ts","../src/git.ts","../src/cleanup.ts"],"mappings":";;;;;;;;;;cAEa,0BAA0B;;;;;cAS1B,mBAAmB;;;;;;cAKnB,uBAAuB;;;cChBvB;KAaD,qBAAqB;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/errors.ts","../src/domain.ts","../src/git.ts","../src/cleanup.ts"],"mappings":";;;;;;;;;;cAEa,0BAA0B;;;;;cAS1B,mBAAmB;;;;;;cAKnB,uBAAuB;;;cChBvB;KAaD,qBAAqB;KAErB;UAEK;WACN;WACA;;UAGM;WACN;WACA;WACA;WACA;WACA;WACA;WACA;;UAGM;WACN;WACA,MAAM;WACN;aACE;aACA;;;UAII;WACN,WAAW;WACX;WACA,WAAW;WACX;WACA,sBAAsB;WACtB;WACA,SAAS;WACT;;UAGM;WACN,SAAS;WACT,qBAAqB,cAAc;WACnC,UAAU;aACR;aACA;;;;cA6BA,yBAAsB,kBAAoB,cAAc;cAsCxD,iBAAiB,SAAS,OAAO;;;UCvH7B;WACN;WACA;WACA;;UAGM;WACN,MACP,aACA,MAAM,0BACH,OAAO,OAAO,WAAW;;;cAGnB,YAAY;kBACP,OAAK,MAAA,MAAA,YAAA,oBAAA;;;;UC6aN;WACN;WACA,WAAW,OAAO;WAClB;WACA;;cAGE,aAAU,SAAA,mBAAA,OAAA;;;;IAmES;IAAiB;;GAkC/C,iBAAA,oBAAA,YAAA,MAAA,OAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as skipReasons, i as skipReasonLabel, n as Git, r as parseWorktreePorcelain, t as runCleanup } from "./cleanup-
|
|
2
|
+
import { a as skipReasons, i as skipReasonLabel, n as Git, r as parseWorktreePorcelain, t as runCleanup } from "./cleanup-DeusOTMj.mjs";
|
|
3
3
|
export { Git, parseWorktreePorcelain, runCleanup, skipReasonLabel, skipReasons };
|
package/dist/main.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as Git, t as runCleanup } from "./cleanup-
|
|
2
|
+
import { n as Git, t as runCleanup } from "./cleanup-DeusOTMj.mjs";
|
|
3
3
|
import { Console, Effect, Layer } from "effect";
|
|
4
4
|
import { Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
6
6
|
//#region package.json
|
|
7
|
-
var version = "0.
|
|
7
|
+
var version = "0.2.0";
|
|
8
8
|
//#endregion
|
|
9
9
|
//#region src/cli.ts
|
|
10
10
|
const directory = Flag.string("dir").pipe(Flag.withDescription("Inspect exactly this worktree root and ignore smart defaults"), Flag.optional);
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup-BQ4ci49S.mjs","names":[],"sources":["../src/domain.ts","../src/errors.ts","../src/git.ts","../src/cleanup.ts"],"sourcesContent":["export const skipReasons = [\n \"not-registered\",\n \"symlink\",\n \"main-worktree\",\n \"current-worktree\",\n \"locked\",\n \"dirty\",\n \"base-ref-unknown\",\n \"not-merged\",\n \"git-error\",\n \"outside-root\"\n] as const\n\nexport type SkipReason = (typeof skipReasons)[number]\n\nexport interface TargetRoot {\n readonly path: string\n readonly source: \"explicit\" | \"worktrees\" | \"claude\" | \"codex\"\n}\n\nexport interface RegisteredWorktree {\n readonly path: string\n readonly head?: string\n readonly branch?: string\n readonly detached: boolean\n readonly lockedReason?: string\n readonly prunableReason?: string\n readonly isMain: boolean\n}\n\nexport interface Candidate {\n readonly path: string\n readonly root: TargetRoot\n readonly structuralSkip?: {\n readonly reason: \"symlink\" | \"outside-root\"\n readonly detail: string\n }\n}\n\nexport interface Assessment {\n readonly candidate: Candidate\n readonly repositoryPath?: string\n readonly worktree?: RegisteredWorktree\n readonly baseRef?: string\n readonly status: \"removable\" | \"skipped\"\n readonly reason?: SkipReason\n readonly detail?: string\n}\n\nexport interface CleanupResult {\n readonly removed: ReadonlyArray<string>\n readonly revalidationSkipped: ReadonlyArray<Assessment>\n readonly failures: ReadonlyArray<{\n readonly path: string\n readonly message: string\n }>\n}\n\ninterface MutableWorktree {\n path?: string\n head?: string\n branch?: string\n detached: boolean\n lockedReason?: string\n prunableReason?: string\n}\n\nconst finishRecord = (\n records: Array<Omit<RegisteredWorktree, \"isMain\">>,\n current: MutableWorktree\n): void => {\n if (current.path === undefined) return\n records.push({\n path: current.path,\n ...(current.head === undefined ? {} : { head: current.head }),\n ...(current.branch === undefined ? {} : { branch: current.branch }),\n detached: current.detached,\n ...(current.lockedReason === undefined ? {} : { lockedReason: current.lockedReason }),\n ...(current.prunableReason === undefined ? {} : { prunableReason: current.prunableReason })\n })\n}\n\n/** Parse `git worktree list --porcelain -z` without interpreting path bytes as shell text. */\nexport const parseWorktreePorcelain = (input: string): ReadonlyArray<RegisteredWorktree> => {\n const records: Array<Omit<RegisteredWorktree, \"isMain\">> = []\n let current: MutableWorktree = { detached: false }\n\n for (const field of input.split(\"\\0\")) {\n if (field.length === 0) {\n finishRecord(records, current)\n current = { detached: false }\n continue\n }\n\n const separator = field.indexOf(\" \")\n const key = separator === -1 ? field : field.slice(0, separator)\n const value = separator === -1 ? \"\" : field.slice(separator + 1)\n\n if (key === \"worktree\") {\n if (current.path !== undefined) {\n finishRecord(records, current)\n current = { detached: false }\n }\n current.path = value\n } else if (key === \"HEAD\") {\n current.head = value\n } else if (key === \"branch\") {\n current.branch = value\n } else if (key === \"detached\") {\n current.detached = true\n } else if (key === \"locked\") {\n current.lockedReason = value.length === 0 ? \"locked\" : value\n } else if (key === \"prunable\") {\n current.prunableReason = value.length === 0 ? \"prunable\" : value\n }\n }\n\n finishRecord(records, current)\n return records.map((record, index) => ({ ...record, isMain: index === 0 }))\n}\n\nexport const skipReasonLabel: Readonly<Record<SkipReason, string>> = {\n \"not-registered\": \"not a registered Git worktree\",\n symlink: \"symlinked directories are never removed\",\n \"main-worktree\": \"repository main worktree\",\n \"current-worktree\": \"worktree running this command\",\n locked: \"worktree is locked\",\n dirty: \"worktree has tracked or untracked changes\",\n \"base-ref-unknown\": \"could not determine a trusted base branch\",\n \"not-merged\": \"HEAD is not integrated into the base branch\",\n \"git-error\": \"Git state could not be proven\",\n \"outside-root\": \"canonical path is outside the selected root\"\n}\n","import { Schema } from \"effect\"\n\nexport class GitExecutionError extends Schema.TaggedError<GitExecutionError>()(\n \"GitExecutionError\",\n {\n operation: Schema.String,\n message: Schema.String,\n cause: Schema.Defect()\n }\n) {}\n\nexport class InputError extends Schema.TaggedError<InputError>()(\"InputError\", {\n message: Schema.String,\n exitCode: Schema.Int\n}) {}\n\nexport class DiscoveryError extends Schema.TaggedError<DiscoveryError>()(\"DiscoveryError\", {\n path: Schema.String,\n message: Schema.String,\n cause: Schema.Defect()\n}) {}\n\nexport type AppError = GitExecutionError | InputError | DiscoveryError\n","import { Context, Effect, Layer, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\n\nimport { GitExecutionError } from \"./errors.js\"\n\nexport interface GitResult {\n readonly exitCode: number\n readonly stdout: string\n readonly stderr: string\n}\n\nexport interface GitService {\n readonly run: (\n cwd: string,\n args: ReadonlyArray<string>\n ) => Effect.Effect<GitResult, GitExecutionError>\n}\n\nexport class Git extends Context.Service<Git, GitService>()(\"@thanaen/worktree-cleanup/Git\") {\n static readonly layer = Layer.effect(\n Git,\n Effect.gen(function* () {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\n const run = Effect.fn(\"Git.run\")(function* (cwd: string, args: ReadonlyArray<string>) {\n const operation = `git ${args.join(\" \")}`\n const handle = yield* spawner.spawn(ChildProcess.make(\"git\", [...args], { cwd })).pipe(\n Effect.mapError(\n (cause) =>\n new GitExecutionError({\n operation,\n message: `Could not start Git in ${cwd}`,\n cause\n })\n )\n )\n\n const [stdout, stderr, exitCode] = yield* Effect.all(\n [\n Stream.mkString(Stream.decodeText(handle.stdout)),\n Stream.mkString(Stream.decodeText(handle.stderr)),\n handle.exitCode\n ] as const,\n { concurrency: \"unbounded\" }\n ).pipe(\n Effect.mapError(\n (cause) =>\n new GitExecutionError({\n operation,\n message: `Git execution failed in ${cwd}`,\n cause\n })\n )\n )\n\n return {\n stdout,\n stderr,\n exitCode: Number(exitCode)\n }\n }, Effect.scoped)\n\n return Git.of({ run })\n })\n )\n}\n","import { Console, Effect, FileSystem, Option, Path } from \"effect\"\nimport { Prompt } from \"effect/unstable/cli\"\n\nimport type {\n Assessment,\n Candidate,\n CleanupResult,\n RegisteredWorktree,\n TargetRoot\n} from \"./domain.js\"\nimport { parseWorktreePorcelain, skipReasonLabel } from \"./domain.js\"\nimport { DiscoveryError, InputError } from \"./errors.js\"\nimport { Git } from \"./git.js\"\n\nconst smartRoots = [\n { relative: \"worktrees\", source: \"worktrees\" },\n { relative: \".claude/worktrees\", source: \"claude\" },\n { relative: \".codex/worktrees\", source: \"codex\" }\n] as const\n\nconst trim = (value: string): string => value.trim()\n\nconst pathKey = (pathService: Path.Path, value: string): string => {\n const normalized = pathService.normalize(value)\n return pathService.sep === \"\\\\\" ? normalized.toLowerCase() : normalized\n}\n\nconst discoveryFailure = (path: string, message: string) => (cause: unknown) =>\n new DiscoveryError({ path, message, cause })\n\nexport const discoverRoots = Effect.fn(\"discoverRoots\")(function* (\n cwd: string,\n explicitDirectory: Option.Option<string>\n) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n\n if (Option.isSome(explicitDirectory)) {\n const requested = path.resolve(cwd, explicitDirectory.value)\n const exists = yield* fs\n .exists(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect --dir\")))\n if (!exists) {\n return yield* new InputError({\n message: `--dir does not exist: ${requested}`,\n exitCode: 2\n })\n }\n const info = yield* fs\n .stat(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect --dir\")))\n if (info.type !== \"Directory\") {\n return yield* new InputError({\n message: `--dir is not a directory: ${requested}`,\n exitCode: 2\n })\n }\n const canonical = yield* fs\n .realPath(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not resolve --dir\")))\n return [{ path: canonical, source: \"explicit\" }] satisfies ReadonlyArray<TargetRoot>\n }\n\n const roots: Array<TargetRoot> = []\n for (const entry of smartRoots) {\n const requested = path.resolve(cwd, entry.relative)\n const exists = yield* fs\n .exists(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect smart default\")))\n if (!exists) continue\n const info = yield* fs\n .stat(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect smart default\")))\n if (info.type !== \"Directory\") continue\n const canonical = yield* fs\n .realPath(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not resolve smart default\")))\n roots.push({ path: canonical, source: entry.source })\n }\n\n return roots\n})\n\nexport const enumerateCandidates = Effect.fn(\"enumerateCandidates\")(function* (\n roots: ReadonlyArray<TargetRoot>\n) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const candidates: Array<Candidate> = []\n\n for (const root of roots) {\n const names = yield* fs\n .readDirectory(root.path)\n .pipe(Effect.mapError(discoveryFailure(root.path, \"Could not list worktree root\")))\n\n for (const name of names.toSorted()) {\n const requested = path.resolve(root.path, name)\n const info = yield* fs\n .stat(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not inspect candidate\")))\n if (info.type !== \"Directory\") continue\n\n const canonical = yield* fs\n .realPath(requested)\n .pipe(Effect.mapError(discoveryFailure(requested, \"Could not resolve candidate\")))\n const normalizedRequested = path.normalize(requested)\n\n if (canonical !== normalizedRequested) {\n candidates.push({\n path: normalizedRequested,\n root,\n structuralSkip: {\n reason: path.dirname(canonical) === root.path ? \"symlink\" : \"outside-root\",\n detail: `resolves to ${canonical}`\n }\n })\n continue\n }\n if (path.dirname(canonical) !== root.path) {\n candidates.push({\n path: canonical,\n root,\n structuralSkip: {\n reason: \"outside-root\",\n detail: `canonical parent is ${path.dirname(canonical)}`\n }\n })\n continue\n }\n candidates.push({ path: canonical, root })\n }\n }\n\n const unique = new Map<string, Candidate>()\n for (const candidate of candidates) unique.set(candidate.path, candidate)\n return [...unique.values()]\n})\n\nconst findCurrentWorktree = Effect.fn(\"findCurrentWorktree\")(function* (cwd: string) {\n const git = yield* Git\n const fs = yield* FileSystem.FileSystem\n const result = yield* git.run(cwd, [\"rev-parse\", \"--show-toplevel\"])\n if (result.exitCode !== 0) return undefined\n return yield* fs\n .realPath(trim(result.stdout))\n .pipe(Effect.orElseSucceed(() => trim(result.stdout)))\n})\n\nconst detectBaseRef = Effect.fn(\"detectBaseRef\")(function* (\n repositoryPath: string,\n mainWorktree: RegisteredWorktree\n) {\n const git = yield* Git\n const originHead = yield* git.run(repositoryPath, [\n \"symbolic-ref\",\n \"--quiet\",\n \"refs/remotes/origin/HEAD\"\n ])\n if (originHead.exitCode === 0 && trim(originHead.stdout).length > 0)\n return trim(originHead.stdout)\n\n for (const ref of [\"refs/heads/main\", \"refs/heads/master\"]) {\n const exists = yield* git.run(repositoryPath, [\"show-ref\", \"--verify\", \"--quiet\", ref])\n if (exists.exitCode === 0) return ref\n }\n\n if (mainWorktree.branch !== undefined) {\n const exists = yield* git.run(repositoryPath, [\n \"show-ref\",\n \"--verify\",\n \"--quiet\",\n mainWorktree.branch\n ])\n if (exists.exitCode === 0) return mainWorktree.branch\n }\n return undefined\n})\n\nconst skipped = (\n candidate: Candidate,\n reason: Assessment[\"reason\"],\n detail?: string,\n context?: {\n readonly repositoryPath?: string\n readonly worktree?: RegisteredWorktree\n readonly baseRef?: string\n }\n): Assessment => ({\n candidate,\n status: \"skipped\",\n ...(reason === undefined ? {} : { reason }),\n ...(detail === undefined ? {} : { detail }),\n ...(context?.repositoryPath === undefined ? {} : { repositoryPath: context.repositoryPath }),\n ...(context?.worktree === undefined ? {} : { worktree: context.worktree }),\n ...(context?.baseRef === undefined ? {} : { baseRef: context.baseRef })\n})\n\nexport const assessCandidate = Effect.fn(\"assessCandidate\")(function* (\n candidate: Candidate,\n currentWorktree: string | undefined\n) {\n const git = yield* Git\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n\n if (candidate.structuralSkip !== undefined) {\n return skipped(candidate, candidate.structuralSkip.reason, candidate.structuralSkip.detail)\n }\n\n const repository = yield* git.run(candidate.path, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--git-common-dir\"\n ])\n if (repository.exitCode !== 0) {\n return skipped(candidate, \"not-registered\", trim(repository.stderr))\n }\n\n const listResult = yield* git.run(candidate.path, [\"worktree\", \"list\", \"--porcelain\", \"-z\"])\n if (listResult.exitCode !== 0) {\n return skipped(candidate, \"git-error\", trim(listResult.stderr))\n }\n const worktrees = parseWorktreePorcelain(listResult.stdout)\n const mainWorktree = worktrees[0]\n if (mainWorktree === undefined) {\n return skipped(candidate, \"git-error\", \"Git returned an empty worktree list\")\n }\n\n const topLevel = yield* git.run(candidate.path, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--show-toplevel\"\n ])\n if (topLevel.exitCode !== 0) {\n return skipped(candidate, \"git-error\", trim(topLevel.stderr))\n }\n\n // Git for Windows can spell the same path using either its long form or an\n // 8.3 component (for example `runneradmin` versus `RUNNER~1`). Match the\n // path reported by Git from inside the candidate against Git's own list.\n const gitCandidatePath = pathKey(path, trim(topLevel.stdout))\n const registered = worktrees.find((entry) => pathKey(path, entry.path) === gitCandidatePath)\n if (registered === undefined) {\n return skipped(candidate, \"not-registered\")\n }\n const worktree = { ...registered, path: candidate.path }\n const canonicalMain = {\n ...mainWorktree,\n path: yield* fs\n .realPath(mainWorktree.path)\n .pipe(Effect.orElseSucceed(() => path.normalize(mainWorktree.path)))\n }\n const repositoryPath = canonicalMain.path\n const context = { repositoryPath, worktree }\n\n if (worktree.isMain) return skipped(candidate, \"main-worktree\", undefined, context)\n if (currentWorktree === candidate.path) {\n return skipped(candidate, \"current-worktree\", undefined, context)\n }\n if (worktree.lockedReason !== undefined) {\n return skipped(candidate, \"locked\", worktree.lockedReason, context)\n }\n if (worktree.head === undefined) {\n return skipped(candidate, \"git-error\", \"Git did not report a HEAD\", context)\n }\n\n const status = yield* git.run(candidate.path, [\"status\", \"--porcelain\", \"--untracked-files=all\"])\n if (status.exitCode !== 0) return skipped(candidate, \"git-error\", trim(status.stderr), context)\n if (status.stdout.length > 0) return skipped(candidate, \"dirty\", undefined, context)\n\n const baseRef = yield* detectBaseRef(repositoryPath, canonicalMain)\n if (baseRef === undefined) return skipped(candidate, \"base-ref-unknown\", undefined, context)\n\n const merged = yield* git.run(repositoryPath, [\n \"merge-base\",\n \"--is-ancestor\",\n worktree.head,\n baseRef\n ])\n if (merged.exitCode === 1) {\n return skipped(candidate, \"not-merged\", `base: ${baseRef}`, { ...context, baseRef })\n }\n if (merged.exitCode !== 0) {\n return skipped(candidate, \"git-error\", trim(merged.stderr), { ...context, baseRef })\n }\n\n return {\n candidate,\n repositoryPath,\n worktree,\n baseRef,\n status: \"removable\" as const\n }\n})\n\nconst renderPlan = Effect.fn(\"renderPlan\")(function* (\n roots: ReadonlyArray<TargetRoot>,\n assessments: ReadonlyArray<Assessment>\n) {\n yield* Console.log(\"Worktree cleanup plan\")\n yield* Console.log(\"Roots:\")\n for (const root of roots) yield* Console.log(` - ${root.path} (${root.source})`)\n\n const removable = assessments.filter((assessment) => assessment.status === \"removable\")\n const skippedItems = assessments.filter((assessment) => assessment.status === \"skipped\")\n\n yield* Console.log(`Removable (${removable.length}):`)\n if (removable.length === 0) yield* Console.log(\" - none\")\n for (const assessment of removable) {\n yield* Console.log(` REMOVE ${assessment.candidate.path} [${assessment.baseRef}]`)\n }\n\n yield* Console.log(`Skipped (${skippedItems.length}):`)\n if (skippedItems.length === 0) yield* Console.log(\" - none\")\n for (const assessment of skippedItems) {\n const reason = assessment.reason === undefined ? \"unknown\" : skipReasonLabel[assessment.reason]\n const detail =\n assessment.detail === undefined || assessment.detail.length === 0\n ? \"\"\n : `: ${assessment.detail}`\n yield* Console.log(` skip ${assessment.candidate.path} — ${reason}${detail}`)\n }\n})\n\nexport interface CleanupOptions {\n readonly cwd: string\n readonly directory: Option.Option<string>\n readonly yes: boolean\n readonly interactive: boolean\n}\n\nexport const runCleanup = Effect.fn(\"runCleanup\")(function* (options: CleanupOptions) {\n const git = yield* Git\n const roots = yield* discoverRoots(options.cwd, options.directory)\n if (roots.length === 0) {\n yield* Console.log(\n \"No worktree roots found (checked worktrees, .claude/worktrees, .codex/worktrees).\"\n )\n return {\n removed: [],\n revalidationSkipped: [],\n failures: []\n } satisfies CleanupResult\n }\n\n const candidates = yield* enumerateCandidates(roots)\n const currentWorktree = yield* findCurrentWorktree(options.cwd)\n const assessments = yield* Effect.forEach(\n candidates,\n (candidate) => assessCandidate(candidate, currentWorktree),\n { concurrency: 4 }\n )\n yield* renderPlan(roots, assessments)\n\n const removable = assessments.filter(\n (\n assessment\n ): assessment is Assessment & {\n readonly repositoryPath: string\n readonly status: \"removable\"\n } => assessment.status === \"removable\" && assessment.repositoryPath !== undefined\n )\n if (removable.length === 0) {\n yield* Console.log(\"Nothing to remove.\")\n return {\n removed: [],\n revalidationSkipped: [],\n failures: []\n } satisfies CleanupResult\n }\n\n if (!options.yes && !options.interactive) {\n return yield* new InputError({\n message: \"Refusing to delete without an interactive terminal. Pass --yes to approve.\",\n exitCode: 2\n })\n }\n\n const confirmed = options.yes\n ? true\n : yield* Prompt.run(\n Prompt.confirm({\n message: `Remove ${removable.length} stale worktree${removable.length === 1 ? \"\" : \"s\"}?`,\n initial: false\n })\n ).pipe(Effect.orElseSucceed(() => false))\n\n if (!confirmed) {\n yield* Console.log(\"Cleanup cancelled; nothing was removed.\")\n return {\n removed: [],\n revalidationSkipped: [],\n failures: []\n } satisfies CleanupResult\n }\n\n const removed: Array<string> = []\n const revalidationSkipped: Array<Assessment> = []\n const failures: Array<{ path: string; message: string }> = []\n\n for (const planned of removable) {\n const revalidated = yield* assessCandidate(planned.candidate, currentWorktree)\n if (revalidated.status !== \"removable\" || revalidated.repositoryPath === undefined) {\n revalidationSkipped.push(revalidated)\n continue\n }\n\n const removal = yield* git.run(revalidated.repositoryPath, [\n \"worktree\",\n \"remove\",\n \"--\",\n revalidated.candidate.path\n ])\n if (removal.exitCode === 0) {\n removed.push(revalidated.candidate.path)\n } else {\n failures.push({\n path: revalidated.candidate.path,\n message: trim(removal.stderr) || `git exited with ${removal.exitCode}`\n })\n }\n }\n\n yield* Console.log(\n `Cleanup complete: ${removed.length} removed, ${revalidationSkipped.length} skipped after revalidation, ${failures.length} failed.`\n )\n for (const failure of failures) yield* Console.error(`Failed ${failure.path}: ${failure.message}`)\n\n if (failures.length > 0) {\n process.exitCode = 1\n }\n return { removed, revalidationSkipped, failures } satisfies CleanupResult\n})\n"],"mappings":"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"}
|