@openagt/skill-branches 0.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 +21 -0
- package/SKILL.md +70 -0
- package/bin/branches +11 -0
- package/dist/bin-dir.d.ts +7 -0
- package/dist/bin-dir.d.ts.map +1 -0
- package/dist/bin-dir.js +8 -0
- package/dist/bin-dir.js.map +1 -0
- package/dist/branch-links.d.ts +37 -0
- package/dist/branch-links.d.ts.map +1 -0
- package/dist/branch-links.js +67 -0
- package/dist/branch-links.js.map +1 -0
- package/dist/branch-names.d.ts +41 -0
- package/dist/branch-names.d.ts.map +1 -0
- package/dist/branch-names.js +54 -0
- package/dist/branch-names.js.map +1 -0
- package/dist/branch-state.d.ts +68 -0
- package/dist/branch-state.d.ts.map +1 -0
- package/dist/branch-state.js +146 -0
- package/dist/branch-state.js.map +1 -0
- package/dist/checkout.d.ts +25 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +37 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli.d.ts +36 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +310 -0
- package/dist/cli.js.map +1 -0
- package/dist/command-link.d.ts +20 -0
- package/dist/command-link.d.ts.map +1 -0
- package/dist/command-link.js +37 -0
- package/dist/command-link.js.map +1 -0
- package/dist/file-branch.d.ts +150 -0
- package/dist/file-branch.d.ts.map +1 -0
- package/dist/file-branch.js +371 -0
- package/dist/file-branch.js.map +1 -0
- package/dist/git-exclude.d.ts +17 -0
- package/dist/git-exclude.d.ts.map +1 -0
- package/dist/git-exclude.js +30 -0
- package/dist/git-exclude.js.map +1 -0
- package/dist/git.d.ts +73 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +164 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/merge-hold.d.ts +20 -0
- package/dist/merge-hold.d.ts.map +1 -0
- package/dist/merge-hold.js +64 -0
- package/dist/merge-hold.js.map +1 -0
- package/dist/merge-watch.d.ts +68 -0
- package/dist/merge-watch.d.ts.map +1 -0
- package/dist/merge-watch.js +97 -0
- package/dist/merge-watch.js.map +1 -0
- package/dist/merge.d.ts +70 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +55 -0
- package/dist/merge.js.map +1 -0
- package/dist/publish.d.ts +138 -0
- package/dist/publish.d.ts.map +1 -0
- package/dist/publish.js +215 -0
- package/dist/publish.js.map +1 -0
- package/dist/push.d.ts +53 -0
- package/dist/push.d.ts.map +1 -0
- package/dist/push.js +41 -0
- package/dist/push.js.map +1 -0
- package/dist/reclaim.d.ts +74 -0
- package/dist/reclaim.d.ts.map +1 -0
- package/dist/reclaim.js +87 -0
- package/dist/reclaim.js.map +1 -0
- package/dist/skill-links.d.ts +43 -0
- package/dist/skill-links.d.ts.map +1 -0
- package/dist/skill-links.js +50 -0
- package/dist/skill-links.js.map +1 -0
- package/dist/worktree-deps.d.ts +30 -0
- package/dist/worktree-deps.d.ts.map +1 -0
- package/dist/worktree-deps.js +128 -0
- package/dist/worktree-deps.js.map +1 -0
- package/dist/worktree.d.ts +220 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +397 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +53 -0
package/dist/reclaim.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { nodeGitRunner, originDefaultBranch } from '@openagt/agent-data';
|
|
2
|
+
import { isAgentBranch } from './branch-names.js';
|
|
3
|
+
import { currentBranch, deleteBranch, isWorktreeRoot, pruneWorktrees, removeWorktree, worktreeClean, } from './worktree.js';
|
|
4
|
+
/**
|
|
5
|
+
* Remove one checkout under the rule above. Throws only for a git failure past the decision
|
|
6
|
+
* (the removal itself); every refusal is an outcome.
|
|
7
|
+
*/
|
|
8
|
+
export async function reclaimWorktree(repo, path, opts) {
|
|
9
|
+
const git = opts.git ?? nodeGitRunner();
|
|
10
|
+
// Before any git runs in it (#1654): a directory under `.branches/` that git does not know as
|
|
11
|
+
// a worktree root makes every command below act on the enclosing repo — the user's checkout,
|
|
12
|
+
// the user's branch. Nothing is pushed or deleted through it; it is left where it is.
|
|
13
|
+
if (!(await isWorktreeRoot(path, git)))
|
|
14
|
+
return { ok: false, reason: 'not-a-worktree' };
|
|
15
|
+
const branch = await currentBranch(path, git);
|
|
16
|
+
if (!branch)
|
|
17
|
+
return { ok: false, reason: 'no-branch' };
|
|
18
|
+
// Every way out below needs a clean tree: `removeWorktree` forces past a dirty one, so a dirty
|
|
19
|
+
// tree is kept (#1638), and so is a tree git cannot read. Asked once, for all of them.
|
|
20
|
+
if (!(await worktreeClean(path, git).catch(() => false)))
|
|
21
|
+
return { ok: false, reason: 'dirty', branch };
|
|
22
|
+
// Whether the checkout's branch goes with it (#1650): only when it holds no commit of its own —
|
|
23
|
+
// an agent that committed nothing — and only a branch minted for an agent: a leftover checkout
|
|
24
|
+
// can sit on the user's own branch (one was found on `main`), and deleting that is not this
|
|
25
|
+
// code's call even when it holds nothing — git's refusal to delete a checked-out branch must
|
|
26
|
+
// never be the guard.
|
|
27
|
+
const emptyBranch = isAgentBranch(branch) && (await branchHoldsNothing(repo, branch, opts.from, git));
|
|
28
|
+
// The birth branch (#1657) is judged before anything is deleted: the containment reads both refs.
|
|
29
|
+
// Only a branch the package minted: the rule that guards the checkout's own branch guards this one.
|
|
30
|
+
const birthBranchGoes = opts.birthBranch !== undefined && opts.birthBranch !== branch && isAgentBranch(opts.birthBranch) && (await isAncestor(repo, `refs/heads/${opts.birthBranch}`, `refs/heads/${branch}`, git));
|
|
31
|
+
await opts.beforeRemove?.();
|
|
32
|
+
await removeWorktree(repo, path, git);
|
|
33
|
+
await pruneWorktrees(repo, git);
|
|
34
|
+
// After the checkout: git refuses to delete a branch a worktree still has checked out.
|
|
35
|
+
// Only a branch that actually went is named: git refuses to delete a branch another worktree
|
|
36
|
+
// has checked out, and the caller must not report that one as gone.
|
|
37
|
+
const deleted = [];
|
|
38
|
+
if (emptyBranch && (await deleteBranch(repo, branch, git)))
|
|
39
|
+
deleted.push(branch);
|
|
40
|
+
if (birthBranchGoes && opts.birthBranch && (await deleteBranch(repo, opts.birthBranch, git)))
|
|
41
|
+
deleted.push(opts.birthBranch);
|
|
42
|
+
return deleted.length ? { ok: true, branchesDeleted: deleted } : { ok: true };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Discard one checkout (#1774): it goes whatever it holds, uncommitted work included. A person's
|
|
46
|
+
* call, for a run they are throwing away, where the rule above would keep the checkout. The
|
|
47
|
+
* branch and its commits stay: deleting a branch that may carry an open pull request is git's
|
|
48
|
+
* business, never this tool's on the way out of a checkout. Only the not-a-worktree guard
|
|
49
|
+
* applies: a directory git does not know as a worktree is left alone.
|
|
50
|
+
*/
|
|
51
|
+
export async function discardWorktree(repo, path, opts = {}) {
|
|
52
|
+
const git = opts.git ?? nodeGitRunner();
|
|
53
|
+
if (!(await isWorktreeRoot(path, git)))
|
|
54
|
+
return { ok: false, reason: 'not-a-worktree' };
|
|
55
|
+
await opts.beforeRemove?.();
|
|
56
|
+
await git(['worktree', 'remove', '--force', path], repo);
|
|
57
|
+
await pruneWorktrees(repo, git);
|
|
58
|
+
return { ok: true };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Whether a branch holds nothing of its own (#1650). Either answer is enough: it has no commit
|
|
62
|
+
* past the commit it was made at, when the caller names that commit and this machine has it; or
|
|
63
|
+
* origin's default branch already has everything on it, read from the local remote-tracking ref
|
|
64
|
+
* and never fetched. The first is the only one a project with no remote can give, and the only
|
|
65
|
+
* one for a branch made from another branch nobody published. The second is the only one for a
|
|
66
|
+
* caller that names no commit, and for a branch whose work was merged.
|
|
67
|
+
*
|
|
68
|
+
* Not "the tip is on the remote under another name", the earlier test: a branch another agent was
|
|
69
|
+
* started from has its tip inside that agent's branch once that one is pushed, and it is still
|
|
70
|
+
* the only branch that work is its own on.
|
|
71
|
+
*/
|
|
72
|
+
async function branchHoldsNothing(repo, branch, from, git) {
|
|
73
|
+
const tip = `refs/heads/${branch}`;
|
|
74
|
+
if (from !== undefined) {
|
|
75
|
+
// The commit itself, never the caller's words handed on to git; one this machine lacks proves nothing.
|
|
76
|
+
const start = await git(['rev-parse', '--verify', '--quiet', '--end-of-options', `${from}^{commit}`], repo).then(out => out.trim(), () => '');
|
|
77
|
+
if (start !== '' && (await isAncestor(repo, tip, start, git)))
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const main = await originDefaultBranch(repo, git);
|
|
81
|
+
return main !== undefined && (await isAncestor(repo, tip, `refs/remotes/${main}`, git));
|
|
82
|
+
}
|
|
83
|
+
/** Whether `inner` is `outer` or an ancestor of it — everything on it is on `outer` too. False on any doubt. */
|
|
84
|
+
async function isAncestor(cwd, inner, outer, git) {
|
|
85
|
+
return git(['merge-base', '--is-ancestor', inner, outer], cwd).then(() => true, () => false);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=reclaim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reclaim.js","sourceRoot":"","sources":["../src/reclaim.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
import { type LinkFs } from './worktree-deps.js';
|
|
3
|
+
/**
|
|
4
|
+
* The skill, where the agent's harness looks for one (#1739). A harness discovers skills under a
|
|
5
|
+
* directory of its own at the checkout root — `.claude/skills/<name>/SKILL.md` for Claude Code,
|
|
6
|
+
* `.agents/skills/<name>/SKILL.md` for Codex — and a checkout under `.branches/` is its own
|
|
7
|
+
* repository root to it, so nothing the project keeps in those directories reaches the agent.
|
|
8
|
+
* Every checkout the package creates therefore gets one symlink per harness, named as the skill
|
|
9
|
+
* and pointing at this package, whose `SKILL.md` is the skill. One mechanism for every harness,
|
|
10
|
+
* and the same text for every agent; a new harness is one more entry in {@link HARNESS_SKILL_DIRS}.
|
|
11
|
+
*/
|
|
12
|
+
/** The skill's name: the `name` in `SKILL.md`'s front matter, and the directory a harness lists it under. */
|
|
13
|
+
export declare const SKILL_NAME = "branches";
|
|
14
|
+
/** Where each harness looks for skills, relative to the checkout root. */
|
|
15
|
+
export declare const HARNESS_SKILL_DIRS: readonly ['.claude/skills', '.agents/skills'];
|
|
16
|
+
/**
|
|
17
|
+
* This package's directory — the one holding `SKILL.md`. Beside `dist/`, so it is the same path
|
|
18
|
+
* from a workspace checkout and from an installed package.
|
|
19
|
+
*/
|
|
20
|
+
export declare const SKILL_DIR: string;
|
|
21
|
+
/**
|
|
22
|
+
* A skill to link into a checkout: its name (the directory a harness lists it under) and the
|
|
23
|
+
* directory holding its `SKILL.md`.
|
|
24
|
+
*
|
|
25
|
+
* A caller names the skills it ships itself and wants in every checkout it makes, skills that
|
|
26
|
+
* live with the caller and not in the repository. A skill a project uses
|
|
27
|
+
* is the project's own tracked `.claude/skills/<name>`, in every worktree by itself.
|
|
28
|
+
*/
|
|
29
|
+
export interface SkillLink {
|
|
30
|
+
name: string;
|
|
31
|
+
dir: string;
|
|
32
|
+
}
|
|
33
|
+
/** This package's own skill, as a {@link SkillLink}. */
|
|
34
|
+
export declare const OWN_SKILL: SkillLink;
|
|
35
|
+
/**
|
|
36
|
+
* Link the skill into `checkout` for every harness — this package's own, plus whatever `skills`
|
|
37
|
+
* the caller names (see {@link SkillLink}) — and hide the links from the project's git
|
|
38
|
+
* through the repository's exclude file: a symlink at the checkout root would otherwise ride any
|
|
39
|
+
* sweeping `git add -A` onto the agent's branch. Best-effort: an entry already at a link's path is
|
|
40
|
+
* left alone, and a link that cannot be made is a worse run, not a failed one.
|
|
41
|
+
*/
|
|
42
|
+
export declare function linkSkill(repo: string, checkout: string, fs?: LinkFs, git?: GitRunner, skills?: readonly SkillLink[]): Promise<void>;
|
|
43
|
+
//# sourceMappingURL=skill-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-links.d.ts","sourceRoot":"","sources":["../src/skill-links.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,SAAS,EAAkB,MAAM,qBAAqB,CAAA;AACnF,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAE5D;;;;;;;;GAQG;AAEH,6GAA6G;AAC7G,eAAO,MAAM,UAAU,aAAa,CAAA;AAEpC,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,YAAI,gBAAgB,EAAE,gBAAgB,CAAU,CAAA;AAE/E;;;GAGG;AACH,eAAO,MAAM,SAAS,QAAsD,CAAA;AAE5E;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,wDAAwD;AACxD,eAAO,MAAM,SAAS,EAAE,SAAgD,CAAA;AAExE;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,EAAE,GAAE,MAAqB,EACzB,GAAG,GAAE,SAA2B,EAChC,MAAM,GAAE,SAAS,SAAS,EAAO,GAChC,OAAO,CAAC,IAAI,CAAC,CAef"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { nodeGitRunner, excludeFromGit } from '@openagt/agent-data';
|
|
4
|
+
import { nodeLinkFs } from './worktree-deps.js';
|
|
5
|
+
/**
|
|
6
|
+
* The skill, where the agent's harness looks for one (#1739). A harness discovers skills under a
|
|
7
|
+
* directory of its own at the checkout root — `.claude/skills/<name>/SKILL.md` for Claude Code,
|
|
8
|
+
* `.agents/skills/<name>/SKILL.md` for Codex — and a checkout under `.branches/` is its own
|
|
9
|
+
* repository root to it, so nothing the project keeps in those directories reaches the agent.
|
|
10
|
+
* Every checkout the package creates therefore gets one symlink per harness, named as the skill
|
|
11
|
+
* and pointing at this package, whose `SKILL.md` is the skill. One mechanism for every harness,
|
|
12
|
+
* and the same text for every agent; a new harness is one more entry in {@link HARNESS_SKILL_DIRS}.
|
|
13
|
+
*/
|
|
14
|
+
/** The skill's name: the `name` in `SKILL.md`'s front matter, and the directory a harness lists it under. */
|
|
15
|
+
export const SKILL_NAME = 'branches';
|
|
16
|
+
/** Where each harness looks for skills, relative to the checkout root. */
|
|
17
|
+
export const HARNESS_SKILL_DIRS = ['.claude/skills', '.agents/skills'];
|
|
18
|
+
/**
|
|
19
|
+
* This package's directory — the one holding `SKILL.md`. Beside `dist/`, so it is the same path
|
|
20
|
+
* from a workspace checkout and from an installed package.
|
|
21
|
+
*/
|
|
22
|
+
export const SKILL_DIR = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
23
|
+
/** This package's own skill, as a {@link SkillLink}. */
|
|
24
|
+
export const OWN_SKILL = { name: SKILL_NAME, dir: SKILL_DIR };
|
|
25
|
+
/**
|
|
26
|
+
* Link the skill into `checkout` for every harness — this package's own, plus whatever `skills`
|
|
27
|
+
* the caller names (see {@link SkillLink}) — and hide the links from the project's git
|
|
28
|
+
* through the repository's exclude file: a symlink at the checkout root would otherwise ride any
|
|
29
|
+
* sweeping `git add -A` onto the agent's branch. Best-effort: an entry already at a link's path is
|
|
30
|
+
* left alone, and a link that cannot be made is a worse run, not a failed one.
|
|
31
|
+
*/
|
|
32
|
+
export async function linkSkill(repo, checkout, fs = nodeLinkFs(), git = nodeGitRunner(), skills = []) {
|
|
33
|
+
for (const skill of [OWN_SKILL, ...skills]) {
|
|
34
|
+
for (const dir of HARNESS_SKILL_DIRS) {
|
|
35
|
+
const rel = `${dir}/${skill.name}`;
|
|
36
|
+
await excludeFromGit(repo, `/${rel}`, undefined, git).catch(() => { });
|
|
37
|
+
try {
|
|
38
|
+
const path = join(checkout, rel);
|
|
39
|
+
if (await fs.entryExists(path))
|
|
40
|
+
continue;
|
|
41
|
+
await fs.mkdir(join(checkout, dir));
|
|
42
|
+
await fs.symlinkDir(skill.dir, path);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// A filesystem that refuses the link: the agent still starts, without the skill.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=skill-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-links.js","sourceRoot":"","sources":["../src/skill-links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,aAAa,EAAkB,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACnF,OAAO,EAAE,UAAU,EAAe,MAAM,oBAAoB,CAAA;AAE5D;;;;;;;;GAQG;AAEH,6GAA6G;AAC7G,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAA;AAEpC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,gBAAgB,EAAE,gBAAgB,CAAU,CAAA;AAE/E;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;AAe5E,wDAAwD;AACxD,MAAM,CAAC,MAAM,SAAS,GAAc,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,CAAA;AAExE;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAY,EACZ,QAAgB,EAChB,EAAE,GAAW,UAAU,EAAE,EACzB,GAAG,GAAc,aAAa,EAAE,EAChC,MAAM,GAAyB,EAAE;IAEjC,KAAK,MAAM,KAAK,IAAI,CAAC,SAAS,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;YACrC,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAA;YAClC,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACrE,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;gBAChC,IAAI,MAAM,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC;oBAAE,SAAQ;gBACxC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAA;gBACnC,MAAM,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,iFAAiF;YACnF,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** The filesystem this module needs. Injectable so the scan is testable. */
|
|
2
|
+
export interface LinkFs {
|
|
3
|
+
/** Entry names in a directory. A missing/unreadable dir yields `[]`. */
|
|
4
|
+
readdir(path: string): Promise<string[]>;
|
|
5
|
+
/** True when `path` is a directory (following symlinks). Any error reads as `false`. */
|
|
6
|
+
isDirectory(path: string): Promise<boolean>;
|
|
7
|
+
/** True when anything exists at `path`, symlinks included (no link following). */
|
|
8
|
+
entryExists(path: string): Promise<boolean>;
|
|
9
|
+
/** Recursive. */
|
|
10
|
+
mkdir(path: string): Promise<void>;
|
|
11
|
+
/** Create a directory symlink at `path` pointing to `target`. */
|
|
12
|
+
symlinkDir(target: string, path: string): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/** The `node:fs/promises` implementation of {@link LinkFs}. */
|
|
15
|
+
export declare function nodeLinkFs(): LinkFs;
|
|
16
|
+
/**
|
|
17
|
+
* Every `node_modules` directory in `repo`, as repo-relative paths, down to
|
|
18
|
+
* {@link MAX_DEPTH}. Sorted, so the linking order (and any log of it) is stable.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findDependencyDirs(repo: string, fs?: LinkFs): Promise<string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Mirror `repo`'s dependency trees into `worktree` at the same relative paths: a real
|
|
23
|
+
* directory per tree, holding a link per entry (the package manager's private state
|
|
24
|
+
* left out, see above). Returns the trees mirrored. Best-effort throughout: a worktree
|
|
25
|
+
* with no deps is a worse run, not a failed one, so a link that cannot be made is
|
|
26
|
+
* skipped rather than thrown. A tree already present is left alone (the agent may
|
|
27
|
+
* have installed already).
|
|
28
|
+
*/
|
|
29
|
+
export declare function linkDependencies(repo: string, worktree: string, fs?: LinkFs): Promise<string[]>;
|
|
30
|
+
//# sourceMappingURL=worktree-deps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree-deps.d.ts","sourceRoot":"","sources":["../src/worktree-deps.ts"],"names":[],"mappings":"AAkDA,4EAA4E;AAC5E,MAAM,WAAW,MAAM;IACrB,wEAAwE;IACxE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACxC,wFAAwF;IACxF,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,kFAAkF;IAClF,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,iBAAiB;IACjB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,iEAAiE;IACjE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACxD;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,IAAI,MAAM,CAyBnC;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAE,MAAqB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAanG;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAE,MAAqB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBnH"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { join, relative } from 'node:path';
|
|
2
|
+
/**
|
|
3
|
+
* Give a fresh worktree a dependency tree (#736). `node_modules` is gitignored, so
|
|
4
|
+
* `git worktree add` hands the agent an empty one and every command in it fails.
|
|
5
|
+
*
|
|
6
|
+
* Three ways to fix that: copy the tree (correct, but gigabytes per agent), install
|
|
7
|
+
* into each worktree (correct, but real latency on every start), or link the
|
|
8
|
+
* parent checkout's trees in (instant, no extra disk, one store shared by N runs).
|
|
9
|
+
* We link — but each *entry* of a dependency directory, into a real directory of
|
|
10
|
+
* the worktree's own, never the directory itself (#1262). A directory link made the
|
|
11
|
+
* parent's tree the worktree's modules directory in the package manager's eyes:
|
|
12
|
+
* an agent that changed a dependency and ran `pnpm install` had pnpm resolve
|
|
13
|
+
* through the link and rewrite — or, under `CI=true`, purge outright — the parent
|
|
14
|
+
* checkout's real `node_modules`, and every later agent died at boot. With a real
|
|
15
|
+
* directory holding links, an install in the worktree writes into the worktree.
|
|
16
|
+
*
|
|
17
|
+
* The package manager's own state (`.pnpm`, `.modules.yaml`, and the like — every
|
|
18
|
+
* dot-entry but `.bin`) is deliberately not linked: it is what tells the package
|
|
19
|
+
* manager "this tree is mine, installed here", and the worktree's is not. The
|
|
20
|
+
* packages still resolve without it — a package entry in a pnpm tree is a relative
|
|
21
|
+
* link into `.pnpm`, and a link to that link resolves where the target lives, in
|
|
22
|
+
* the parent checkout. `.bin` is linked because an agent runs the project's tools.
|
|
23
|
+
* A scope directory is mirrored one level down the same way, a real directory
|
|
24
|
+
* holding a link per package, so a new scoped package lands in the worktree too.
|
|
25
|
+
*/
|
|
26
|
+
/** The dependency directory mirrored into a worktree. */
|
|
27
|
+
const NODE_MODULES = 'node_modules';
|
|
28
|
+
/** How deep below the repo root a `node_modules` is looked for (root = 0). Covers a
|
|
29
|
+
* pnpm/npm workspace's `packages/<pkg>/node_modules` without walking the world. */
|
|
30
|
+
const MAX_DEPTH = 2;
|
|
31
|
+
/** Directory names never descended into while scanning for dependency trees. */
|
|
32
|
+
const SKIP = new Set([NODE_MODULES, '.git', 'dist', 'build', 'coverage']);
|
|
33
|
+
/** The one dot-entry of a dependency directory that is linked: the project's executables. */
|
|
34
|
+
const BIN = '.bin';
|
|
35
|
+
/** The package manager's private state in a dependency directory: never linked (see above). */
|
|
36
|
+
const isPrivate = (name) => name.startsWith('.') && name !== BIN;
|
|
37
|
+
/**
|
|
38
|
+
* A scope (`@acme`) holds packages rather than being one. Linked whole, an install of a new
|
|
39
|
+
* `@acme/pkg` in the worktree would go through the link into the parent's tree, so a scope is a
|
|
40
|
+
* real directory of the worktree's own too, holding a link per package.
|
|
41
|
+
*/
|
|
42
|
+
const isScope = (name) => name.startsWith('@');
|
|
43
|
+
/** The `node:fs/promises` implementation of {@link LinkFs}. */
|
|
44
|
+
export function nodeLinkFs() {
|
|
45
|
+
return {
|
|
46
|
+
async readdir(path) {
|
|
47
|
+
const { readdir } = await import('node:fs/promises');
|
|
48
|
+
return readdir(path).catch(() => []);
|
|
49
|
+
},
|
|
50
|
+
async isDirectory(path) {
|
|
51
|
+
const { stat } = await import('node:fs/promises');
|
|
52
|
+
return stat(path).then(s => s.isDirectory(), () => false);
|
|
53
|
+
},
|
|
54
|
+
async entryExists(path) {
|
|
55
|
+
const { lstat } = await import('node:fs/promises');
|
|
56
|
+
return lstat(path).then(() => true, () => false);
|
|
57
|
+
},
|
|
58
|
+
async mkdir(path) {
|
|
59
|
+
const { mkdir } = await import('node:fs/promises');
|
|
60
|
+
await mkdir(path, { recursive: true });
|
|
61
|
+
},
|
|
62
|
+
async symlinkDir(target, path) {
|
|
63
|
+
const { symlink } = await import('node:fs/promises');
|
|
64
|
+
// 'junction' is the only directory-link type Windows grants without elevation;
|
|
65
|
+
// it is ignored on POSIX.
|
|
66
|
+
await symlink(target, path, process.platform === 'win32' ? 'junction' : 'dir');
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Every `node_modules` directory in `repo`, as repo-relative paths, down to
|
|
72
|
+
* {@link MAX_DEPTH}. Sorted, so the linking order (and any log of it) is stable.
|
|
73
|
+
*/
|
|
74
|
+
export async function findDependencyDirs(repo, fs = nodeLinkFs()) {
|
|
75
|
+
const found = [];
|
|
76
|
+
const walk = async (dir, depth) => {
|
|
77
|
+
if (await fs.isDirectory(join(dir, NODE_MODULES)))
|
|
78
|
+
found.push(relative(repo, join(dir, NODE_MODULES)));
|
|
79
|
+
if (depth >= MAX_DEPTH)
|
|
80
|
+
return;
|
|
81
|
+
for (const name of await fs.readdir(dir)) {
|
|
82
|
+
if (name.startsWith('.') || SKIP.has(name))
|
|
83
|
+
continue;
|
|
84
|
+
const child = join(dir, name);
|
|
85
|
+
if (await fs.isDirectory(child))
|
|
86
|
+
await walk(child, depth + 1);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
await walk(repo, 0);
|
|
90
|
+
return found.sort();
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Mirror `repo`'s dependency trees into `worktree` at the same relative paths: a real
|
|
94
|
+
* directory per tree, holding a link per entry (the package manager's private state
|
|
95
|
+
* left out, see above). Returns the trees mirrored. Best-effort throughout: a worktree
|
|
96
|
+
* with no deps is a worse run, not a failed one, so a link that cannot be made is
|
|
97
|
+
* skipped rather than thrown. A tree already present is left alone (the agent may
|
|
98
|
+
* have installed already).
|
|
99
|
+
*/
|
|
100
|
+
export async function linkDependencies(repo, worktree, fs = nodeLinkFs()) {
|
|
101
|
+
const linked = [];
|
|
102
|
+
for (const rel of await findDependencyDirs(repo, fs)) {
|
|
103
|
+
const source = join(repo, rel);
|
|
104
|
+
const dir = join(worktree, rel);
|
|
105
|
+
try {
|
|
106
|
+
if (await fs.entryExists(dir))
|
|
107
|
+
continue;
|
|
108
|
+
await fs.mkdir(dir);
|
|
109
|
+
for (const name of await fs.readdir(source)) {
|
|
110
|
+
if (isPrivate(name))
|
|
111
|
+
continue;
|
|
112
|
+
if (isScope(name) && (await fs.isDirectory(join(source, name)))) {
|
|
113
|
+
await fs.mkdir(join(dir, name));
|
|
114
|
+
for (const pkg of await fs.readdir(join(source, name)))
|
|
115
|
+
await fs.symlinkDir(join(source, name, pkg), join(dir, name, pkg)).catch(() => { });
|
|
116
|
+
}
|
|
117
|
+
else
|
|
118
|
+
await fs.symlinkDir(join(source, name), join(dir, name)).catch(() => { });
|
|
119
|
+
}
|
|
120
|
+
linked.push(rel);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Raced, or a filesystem that refuses the directory: the agent still starts.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return linked;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=worktree-deps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree-deps.js","sourceRoot":"","sources":["../src/worktree-deps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
/**
|
|
3
|
+
* Git-worktree lifecycle for concurrent agents (#453/#735): give each agent its own
|
|
4
|
+
* checkout so N agents on one repo never fight over the working tree. Pure plumbing
|
|
5
|
+
* over the {@link GitRunner} seam: this module only knows how to add, list, name,
|
|
6
|
+
* remove, and prune worktrees, and to read what a retention decision needs.
|
|
7
|
+
*/
|
|
8
|
+
/** The path an agent's worktree gets (#1580): `<repo>/.branches/<agent branch>`. */
|
|
9
|
+
export declare function worktreePath(repo: string, agentId: string): string;
|
|
10
|
+
/** One checkout on disk: where it is, and whose it is. */
|
|
11
|
+
export interface WorktreeDirEntry {
|
|
12
|
+
path: string;
|
|
13
|
+
agentId: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Lists the names of the *directories* under `path` — a symlink is not one, whatever it points
|
|
17
|
+
* to. A missing or unreadable directory yields `[]`.
|
|
18
|
+
*/
|
|
19
|
+
export type DirReader = (path: string) => Promise<string[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Every checkout directory on disk (#1580): a directory under `.branches/` named as an agent
|
|
22
|
+
* branch. Only directories count — the same place holds the rename links (#1589), symlinks named
|
|
23
|
+
* as agent branches too, which are views, not checkouts. Forgiving: a missing root yields nothing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function worktreeDirEntries(repo: string, readdir?: DirReader): Promise<WorktreeDirEntry[]>;
|
|
26
|
+
/**
|
|
27
|
+
* The agent ids that have a worktree directory (#737/#1580). Forgiving — a project that never ran
|
|
28
|
+
* concurrently has no such dir and yields `[]`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function listWorktreeDirs(repo: string, readdir?: DirReader): Promise<string[]>;
|
|
31
|
+
/** One entry parsed from `git worktree list --porcelain`. */
|
|
32
|
+
export interface WorktreeInfo {
|
|
33
|
+
/** Absolute worktree path (the main checkout included). */
|
|
34
|
+
path: string;
|
|
35
|
+
/** The checked-out commit. */
|
|
36
|
+
head: string;
|
|
37
|
+
/** The checked-out branch (short name), or absent when detached. */
|
|
38
|
+
branch?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Inputs to {@link addWorktree}. The caller owns branch naming (#736). */
|
|
41
|
+
export interface AddWorktreeOptions {
|
|
42
|
+
agentId: string;
|
|
43
|
+
/** The branch to create for the agent. */
|
|
44
|
+
branch: string;
|
|
45
|
+
/** Base ref to branch from; defaults to origin's default branch ({@link freshStart}). */
|
|
46
|
+
base?: string;
|
|
47
|
+
}
|
|
48
|
+
/** The worktree {@link addWorktree} created. */
|
|
49
|
+
export interface AddedWorktree {
|
|
50
|
+
path: string;
|
|
51
|
+
branch: string;
|
|
52
|
+
/** The branch was gone everywhere and was made again, from the base or origin's default branch: nothing it held before is on it. */
|
|
53
|
+
again?: true;
|
|
54
|
+
}
|
|
55
|
+
/** How long a new agent branch waits for origin's default branch to be fetched: Claude Code's own cap. */
|
|
56
|
+
export declare const FRESH_START_FETCH_MS = 5000;
|
|
57
|
+
/**
|
|
58
|
+
* Create a worktree for an agent on a fresh branch: `git worktree add --no-track -b <branch>
|
|
59
|
+
* <path> <base>`, the base origin's default branch unless the caller names one. No upstream:
|
|
60
|
+
* tracking writes the repository's shared `.git/config`, whose lock a running agent's own git
|
|
61
|
+
* command in another checkout may hold. Git makes the leaf dir (and any missing parents) itself. The
|
|
62
|
+
* `agentId` is validated as path-safe first so a caller can never traverse out of
|
|
63
|
+
* `.branches/`. Rejects on any git failure (a caller that wants a
|
|
64
|
+
* run needs its checkout, so failure must surface, not be swallowed).
|
|
65
|
+
*/
|
|
66
|
+
export declare function addWorktree(repo: string, opts: AddWorktreeOptions, git?: GitRunner): Promise<AddedWorktree>;
|
|
67
|
+
/**
|
|
68
|
+
* Check an *existing* branch out into an agent's worktree (#762). Continuing an agent puts it back
|
|
69
|
+
* on the branch its work is already on, rather than branching again and stranding what it did
|
|
70
|
+
* last time. The branch as this clone has it; else origin's copy, as a new local branch with no
|
|
71
|
+
* upstream set, as in {@link addWorktree}; else, gone everywhere, a new branch where the agent's
|
|
72
|
+
* first one started: the base the caller names, as this clone has it or else as origin does, and
|
|
73
|
+
* with none, or one that is gone too, where a new agent branch starts ({@link freshStart}); the
|
|
74
|
+
* answer then says the branch was made again. The
|
|
75
|
+
* only branch the package deletes is one that held nothing past a commit the remote already had,
|
|
76
|
+
* so nothing of the agent's is lost. Anything git refuses — the branch checked out elsewhere, say —
|
|
77
|
+
* rejects, like {@link addWorktree}: a continued agent needs its checkout.
|
|
78
|
+
*/
|
|
79
|
+
export declare function attachWorktree(repo: string, opts: {
|
|
80
|
+
agentId: string;
|
|
81
|
+
branch: string;
|
|
82
|
+
base?: string;
|
|
83
|
+
}, git?: GitRunner): Promise<AddedWorktree>;
|
|
84
|
+
/**
|
|
85
|
+
* Every worktree registered for the repo (the main checkout included). Forgiving:
|
|
86
|
+
* a non-repo / git failure yields `[]` so a reconcile scan never throws.
|
|
87
|
+
*/
|
|
88
|
+
export declare function listWorktrees(repo: string, git?: GitRunner): Promise<WorktreeInfo[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Parse `git worktree list --porcelain`: blank-line-separated records, each with
|
|
91
|
+
* a `worktree <path>` line, a `HEAD <sha>` line, and either `branch refs/heads/...`
|
|
92
|
+
* or `detached`. Extra attributes (bare/locked/prunable) are ignored. Exported so
|
|
93
|
+
* the parsing is unit-testable without a real repo.
|
|
94
|
+
*/
|
|
95
|
+
export declare function parseWorktreeList(porcelain: string): WorktreeInfo[];
|
|
96
|
+
/**
|
|
97
|
+
* Remove an agent's worktree. Tolerant of an already-gone / never-registered path so
|
|
98
|
+
* teardown stays idempotent (a caller may run it twice).
|
|
99
|
+
*
|
|
100
|
+
* Plain removal first: it refuses a checkout git considers unclean, which after the
|
|
101
|
+
* caller's {@link worktreeClean} check means a state we did not anticipate. Falling back to
|
|
102
|
+
* `--force` keeps teardown working (an ignored build artifact must not strand a
|
|
103
|
+
* worktree forever), but it says so, because forcing past unknown state is exactly
|
|
104
|
+
* how uncommitted work got deleted in the first place.
|
|
105
|
+
*/
|
|
106
|
+
export declare function removeWorktree(repo: string, path: string, git?: GitRunner): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Delete a branch that holds nothing (#1650). `-D`, because "merged" in git's eyes is the wrong
|
|
109
|
+
* test: the caller proved the tip is a commit the remote already has, which is the stronger fact.
|
|
110
|
+
* Forgiving: the checkout is already gone by the time this runs, and a branch that would not
|
|
111
|
+
* delete is a leftover name, not lost work. Says whether it went, so a caller names only the
|
|
112
|
+
* branches that are actually gone (#1757).
|
|
113
|
+
*/
|
|
114
|
+
export declare function deleteBranch(repo: string, branch: string, git?: GitRunner): Promise<boolean>;
|
|
115
|
+
/**
|
|
116
|
+
* Whether `path` is the root of a git worktree — the main checkout's or a linked one (#1654).
|
|
117
|
+
*
|
|
118
|
+
* Git answers for any directory *inside* a repository, so a `.branches/<agent>` directory that is
|
|
119
|
+
* no longer a worktree (a checkout removed by hand, a marker written after teardown) makes every
|
|
120
|
+
* git command run in it act on the enclosing repo: the user's own checkout, on the user's own
|
|
121
|
+
* branch. The one question that tells the two apart is whether git's top level is this very
|
|
122
|
+
* directory. False on any failure, and the caller leaves the directory alone.
|
|
123
|
+
*/
|
|
124
|
+
export declare function isWorktreeRoot(path: string, git?: GitRunner): Promise<boolean>;
|
|
125
|
+
/**
|
|
126
|
+
* The branch checked out at `path` when `path` is a worktree root (#1654), else `undefined` —
|
|
127
|
+
* the read every consumer of a `.branches/<agent>` directory wants, so none of them can take the
|
|
128
|
+
* enclosing repo's branch for the run's.
|
|
129
|
+
*/
|
|
130
|
+
export declare function worktreeBranch(path: string, git?: GitRunner): Promise<string | undefined>;
|
|
131
|
+
/**
|
|
132
|
+
* The branch checked out at `path`, or `undefined` when detached / not a repo.
|
|
133
|
+
* Forgiving, like {@link listWorktrees}: callers use it to decide, not to fail.
|
|
134
|
+
*/
|
|
135
|
+
export declare function currentBranch(path: string, git?: GitRunner): Promise<string | undefined>;
|
|
136
|
+
/**
|
|
137
|
+
* The project a directory belongs to (#1725): the checkout whose `.branches/` holds the agent
|
|
138
|
+
* checkouts. From inside an agent's checkout that is two levels up, by the layout every checkout
|
|
139
|
+
* is created with; from anywhere else it is the checkout itself. Read from the layout rather than
|
|
140
|
+
* from git's common dir, so a project that is itself a linked worktree, or a submodule, answers
|
|
141
|
+
* with the directory the caller registered. Rejects outside a repo.
|
|
142
|
+
*/
|
|
143
|
+
export declare function projectRoot(cwd: string, git?: GitRunner): Promise<string>;
|
|
144
|
+
/** A session name as the agent picks it: the charset the skill asks for. */
|
|
145
|
+
export declare function isSessionName(name: string): boolean;
|
|
146
|
+
/** Why {@link nameBranch} left the branch as it was. */
|
|
147
|
+
export type NameBranchRefusal =
|
|
148
|
+
/** Not `[a-z0-9-]+`. */
|
|
149
|
+
'invalid-name'
|
|
150
|
+
/** The directory is not a git worktree root: nothing was run in it. */
|
|
151
|
+
| 'not-a-worktree'
|
|
152
|
+
/** The checkout is on no branch (detached). */
|
|
153
|
+
| 'no-branch'
|
|
154
|
+
/** The checkout is on a branch the package did not mint — the user's own. */
|
|
155
|
+
| 'not-an-agent-branch';
|
|
156
|
+
export type NameBranchOutcome = {
|
|
157
|
+
ok: true;
|
|
158
|
+
/** The name the branch ends up with: `agent-<name>`, suffixed when that was taken. */
|
|
159
|
+
branch: string;
|
|
160
|
+
} | {
|
|
161
|
+
ok: false;
|
|
162
|
+
reason: NameBranchRefusal;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Name the session (#1725): rename the checkout's branch to `agent-<name>`. A rename, not a new
|
|
166
|
+
* branch, so the branch the checkout was born on is the branch it ends on and nothing is left
|
|
167
|
+
* behind to clean up. Only a branch the package minted is ever renamed: an agent that somehow
|
|
168
|
+
* runs in the user's own checkout must not rename `main`.
|
|
169
|
+
*
|
|
170
|
+
* A taken name gets a numeric suffix (`agent-<name>-2`, `-3`, …) rather than a refusal: the agent
|
|
171
|
+
* asked for a name, and the caller reads back the one it got. Taken means any local branch or
|
|
172
|
+
* any remote-tracking branch, so the later push does not land on someone else's branch — except
|
|
173
|
+
* the checkout's own branch, pushed or not, which is why asking again for the name the checkout
|
|
174
|
+
* already carries (suffixed or not) changes nothing. Two checkouts naming the same thing at the
|
|
175
|
+
* same moment race on the rename itself; the loser reads the branches again and takes the next
|
|
176
|
+
* free suffix.
|
|
177
|
+
*/
|
|
178
|
+
export declare function nameBranch(path: string, name: string, git?: GitRunner): Promise<NameBranchOutcome>;
|
|
179
|
+
/**
|
|
180
|
+
* `git worktree prune`: drop administrative entries for worktree dirs a crash left
|
|
181
|
+
* behind. Never removes a live worktree, so it is always safe. Forgiving.
|
|
182
|
+
*/
|
|
183
|
+
export declare function pruneWorktrees(repo: string, git?: GitRunner): Promise<void>;
|
|
184
|
+
/** Runs `du`, resolving its stdout. Injectable so the size read can be tested without a real tree. */
|
|
185
|
+
export type SizeRunner = (path: string) => Promise<string>;
|
|
186
|
+
/** A {@link SizeRunner} over `du -sk`: one process, and it does not follow the symlinked deps (#736). */
|
|
187
|
+
export declare function nodeSizeRunner(): SizeRunner;
|
|
188
|
+
/**
|
|
189
|
+
* A worktree's size on disk in bytes, or undefined when it cannot be read (#798). Best-effort by
|
|
190
|
+
* design: this only ever labels a "remove this" button, so a missing number costs nothing while a
|
|
191
|
+
* throw or a hang would cost the listing it sits in. `du` is absent on Windows, which reads as
|
|
192
|
+
* unknown like any other failure.
|
|
193
|
+
*/
|
|
194
|
+
export declare function worktreeSize(path: string, size?: SizeRunner): Promise<number | undefined>;
|
|
195
|
+
/**
|
|
196
|
+
* Whether a branch is on the remote, with the local tip already there.
|
|
197
|
+
*
|
|
198
|
+
* What a caller asks to say whether a branch's work is published yet.
|
|
199
|
+
*
|
|
200
|
+
* `git rev-parse` of the remote-tracking ref, then a merge-base check: the ref existing is not
|
|
201
|
+
* enough, because a branch pushed and then committed to again has a tip the remote has never seen.
|
|
202
|
+
* Reads only local refs (no fetch), so it is cheap enough to ask on every poll — the remote ref
|
|
203
|
+
* is written by the push this is checking for, which is what makes that sound.
|
|
204
|
+
*
|
|
205
|
+
* Anything unreadable answers `false`, and so does a repo with no remote configured.
|
|
206
|
+
*/
|
|
207
|
+
export declare function branchPushed(repo: string, branch: string, git?: GitRunner): Promise<boolean>;
|
|
208
|
+
/**
|
|
209
|
+
* Whether the checkout has nothing uncommitted. A read, never a commit (#1638): the package
|
|
210
|
+
* commits nothing on an agent's behalf, so a checkout holding uncommitted work is one the caller
|
|
211
|
+
* keeps. Throws when git cannot answer, so the caller keeps the checkout rather than guessing.
|
|
212
|
+
*/
|
|
213
|
+
export declare function worktreeClean(path: string, git?: GitRunner): Promise<boolean>;
|
|
214
|
+
/**
|
|
215
|
+
* Whether the repo has any remote configured at all: with none there is nowhere to publish a
|
|
216
|
+
* branch to, and a caller offers no push. Anything unreadable answers `false`, like
|
|
217
|
+
* {@link branchPushed}.
|
|
218
|
+
*/
|
|
219
|
+
export declare function repoHasRemote(repo: string, git?: GitRunner): Promise<boolean>;
|
|
220
|
+
//# sourceMappingURL=worktree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"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"}
|