@prestyj/cli 5.20.0 → 5.21.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 +1 -1
- package/dist/app-sidecar.js +120 -0
- package/dist/app-sidecar.js.map +1 -1
- package/dist/core/worktree.d.ts +161 -0
- package/dist/core/worktree.js +574 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/tools/subagent.d.ts +1 -0
- package/dist/tools/subagent.js +66 -2
- package/dist/tools/subagent.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export type GitLayout = {
|
|
2
|
+
worktreeRoot: string;
|
|
3
|
+
gitDir: string;
|
|
4
|
+
/** True when `.git` is a real directory (the primary checkout), false for a linked worktree. */
|
|
5
|
+
isMain: boolean;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Walk up from `startDir` to the nearest `.git`. A `.git` directory means the
|
|
9
|
+
* main worktree; a `.git` *file* (`gitdir: <path>`) means an already-linked
|
|
10
|
+
* worktree. Returns null when no repository encloses `startDir`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function gitLayout(startDir: string): Promise<GitLayout | null>;
|
|
13
|
+
/** The enclosing repo's root, but only when `startDir` is inside the MAIN worktree. */
|
|
14
|
+
export declare function findMainWorktreeRoot(startDir: string): Promise<string | null>;
|
|
15
|
+
export interface WorktreeEntry {
|
|
16
|
+
path: string;
|
|
17
|
+
branch: string | null;
|
|
18
|
+
head: string | null;
|
|
19
|
+
isBare: boolean;
|
|
20
|
+
isMain: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse `git worktree list --porcelain`. The first block is always the main
|
|
24
|
+
* worktree. Paths are canonicalised so callers can compare them by string
|
|
25
|
+
* equality (/var vs /private/var aliasing on macOS).
|
|
26
|
+
*/
|
|
27
|
+
export declare function listWorktrees(repoDir: string): Promise<WorktreeEntry[]>;
|
|
28
|
+
/** True when `git status --porcelain` reports anything (tracked or untracked). */
|
|
29
|
+
export declare function isWorkingTreeDirty(dir: string): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Normalise arbitrary text into a branch name git will accept. Returns "" when
|
|
32
|
+
* nothing usable survives, so callers can fall back to a generated name.
|
|
33
|
+
*/
|
|
34
|
+
export declare function slugifyBranch(input: string): string;
|
|
35
|
+
/** Pure: `<home>/.ezcoder/worktrees/<basename(repoRoot)>`. */
|
|
36
|
+
export declare function worktreesRootFor(repoRoot: string, homeDir?: string): string;
|
|
37
|
+
export interface CreateWorktreeOptions {
|
|
38
|
+
repoDir: string;
|
|
39
|
+
branch?: string;
|
|
40
|
+
baseRef?: string;
|
|
41
|
+
allowDirty?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface CreatedWorktree {
|
|
44
|
+
path: string;
|
|
45
|
+
branch: string;
|
|
46
|
+
baseRef: string;
|
|
47
|
+
created: true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a linked worktree on a fresh branch under `worktreesRootFor(mainRoot)`.
|
|
51
|
+
* Refuses to run against a dirty main checkout unless `allowDirty` is set — a
|
|
52
|
+
* half-committed main tree is the fastest way to lose work across worktrees.
|
|
53
|
+
*/
|
|
54
|
+
export declare function createWorktree(opts: CreateWorktreeOptions): Promise<CreatedWorktree>;
|
|
55
|
+
/**
|
|
56
|
+
* Pure: is `candidate` inside `root`?
|
|
57
|
+
*
|
|
58
|
+
* `path.relative` rather than `startsWith`: the string form says yes to
|
|
59
|
+
* `/a/worktrees-evil` for root `/a/worktrees`, and on Windows says no to a
|
|
60
|
+
* correct path that differs only in separator or drive-letter case. An empty
|
|
61
|
+
* relative path (candidate IS root) is deliberately NOT contained — removing
|
|
62
|
+
* the container itself is never what a caller means.
|
|
63
|
+
*/
|
|
64
|
+
export declare function isInsideWorktreesRoot(root: string, candidate: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The first symlink at or above `dir` (stopping at the filesystem root), or
|
|
67
|
+
* null when the whole chain is real directories.
|
|
68
|
+
*
|
|
69
|
+
* A containment check alone cannot see this: `stat` dereferences every path
|
|
70
|
+
* component, so a symlink at `~/.ezcoder/worktrees` makes every path under it
|
|
71
|
+
* look like an ordinary contained directory while the removal lands in whatever
|
|
72
|
+
* checkout it points at — the user's real repo, most likely. `lstat` sees the
|
|
73
|
+
* link itself. Every destructive path routes through here.
|
|
74
|
+
*/
|
|
75
|
+
export declare function redirectedAncestor(dir: string): Promise<string | null>;
|
|
76
|
+
export interface WorktreeStatus {
|
|
77
|
+
path: string;
|
|
78
|
+
branch: string | null;
|
|
79
|
+
/** Ref it forked from; null when unrecorded and no fallback resolved. */
|
|
80
|
+
baseRef: string | null;
|
|
81
|
+
/** Staged, modified and untracked files. Non-zero means unsaved work. */
|
|
82
|
+
dirtyFiles: number;
|
|
83
|
+
/** Commits on `branch` that `baseRef` does not contain. */
|
|
84
|
+
commitsAhead: number;
|
|
85
|
+
/** The branch is fully contained in `baseRef`. */
|
|
86
|
+
merged: boolean;
|
|
87
|
+
/** Another window is working here, so it must not be touched. */
|
|
88
|
+
busy: boolean;
|
|
89
|
+
/** Safe to remove with nothing lost. */
|
|
90
|
+
reclaimable: boolean;
|
|
91
|
+
/** Why not, in the user's words. Empty exactly when `reclaimable`. */
|
|
92
|
+
blockedBy: string[];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Describe one worktree well enough to decide its fate, from the worktree's own
|
|
96
|
+
* directory so a stale main-checkout view cannot mislead.
|
|
97
|
+
*
|
|
98
|
+
* Unknowns always count AGAINST reclaiming: a git call that fails leaves the
|
|
99
|
+
* entry blocked rather than deleted. The cost of a wrong "keep" is a stale
|
|
100
|
+
* folder; the cost of a wrong "delete" is lost work.
|
|
101
|
+
*/
|
|
102
|
+
export declare function worktreeStatus(mainRoot: string, entry: WorktreeEntry, busyPaths?: readonly string[]): Promise<WorktreeStatus>;
|
|
103
|
+
/** Status for every linked worktree of `repoDir`'s repo, main checkout excluded. */
|
|
104
|
+
export declare function worktreeStatuses(repoDir: string, busyPaths?: readonly string[]): Promise<WorktreeStatus[]>;
|
|
105
|
+
/** Worktrees that can be removed right now with nothing lost. */
|
|
106
|
+
export declare function reclaimableWorktrees(repoDir: string, busyPaths?: readonly string[]): Promise<WorktreeStatus[]>;
|
|
107
|
+
export interface WorktreeRelease {
|
|
108
|
+
/** Something was at the path when the release started. */
|
|
109
|
+
existed: boolean;
|
|
110
|
+
/** The path is free now — a `worktree add` over it would succeed. */
|
|
111
|
+
freed: boolean;
|
|
112
|
+
/** The branch is gone too. */
|
|
113
|
+
branchDeleted: boolean;
|
|
114
|
+
/** Why it is not free. Set exactly when `existed && !freed`. */
|
|
115
|
+
reason?: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Free a worktree's path AND its branch. Never throws: cleanup runs on window
|
|
119
|
+
* close and on daemon startup, where an exception would surface as a crash in
|
|
120
|
+
* an unrelated flow. Everything it could not do comes back in the result.
|
|
121
|
+
*
|
|
122
|
+
* The order below is load-bearing and `prune` is not optional. `worktree
|
|
123
|
+
* remove` needs the directory to be there; a user reclaiming disk with
|
|
124
|
+
* `rm -rf ~/.ezcoder/worktrees` leaves the tree REGISTERED BUT MISSING, and git
|
|
125
|
+
* then refuses both of the things the next run needs:
|
|
126
|
+
*
|
|
127
|
+
* $ git worktree add <path> <branch>
|
|
128
|
+
* fatal: '<path>' is a missing but already registered worktree;
|
|
129
|
+
* use 'add -f' to override, or 'prune' or 'remove' to clear
|
|
130
|
+
*
|
|
131
|
+
* and `git branch -d <branch>`, because the branch counts as checked out in
|
|
132
|
+
* that phantom. `git worktree prune` is the only thing that clears the
|
|
133
|
+
* registration, and it is a no-op when nothing is stale — so it runs
|
|
134
|
+
* unconditionally, and BEFORE the branch delete that depends on it.
|
|
135
|
+
*/
|
|
136
|
+
export declare function removeWorktree(opts: {
|
|
137
|
+
repoDir: string;
|
|
138
|
+
worktreePath: string;
|
|
139
|
+
/** Remove even with uncommitted or unmerged work. Caller must have confirmed. */
|
|
140
|
+
force?: boolean;
|
|
141
|
+
/** Delete the branch too. Default true. */
|
|
142
|
+
deleteBranch?: boolean;
|
|
143
|
+
}): Promise<WorktreeRelease>;
|
|
144
|
+
export interface SweepResult {
|
|
145
|
+
removed: WorktreeStatus[];
|
|
146
|
+
kept: {
|
|
147
|
+
status: WorktreeStatus;
|
|
148
|
+
reason: string;
|
|
149
|
+
}[];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Remove every worktree of `repoDir`'s repo that is provably free of work.
|
|
153
|
+
*
|
|
154
|
+
* The automatic entry point: window close and daemon startup. It never forces,
|
|
155
|
+
* so anything holding uncommitted or unmerged work is reported as kept, with
|
|
156
|
+
* the reason, and waits for a human. Failures are collected rather than thrown
|
|
157
|
+
* — one wedged worktree must not stop the rest of the sweep.
|
|
158
|
+
*/
|
|
159
|
+
export declare function sweepWorktrees(repoDir: string, busyPaths?: readonly string[]): Promise<SweepResult>;
|
|
160
|
+
/** True when the repo already has at least one linked worktree registered. */
|
|
161
|
+
export declare function repoHasLinkedWorktrees(mainRoot: string): Promise<boolean>;
|