@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Suleiman Shahbari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: branches
|
|
3
|
+
description: Where your work goes (a branch named agent-<name>), how to name it, what must be true before you finish, and how to push it when you are asked to.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Branch management
|
|
7
|
+
|
|
8
|
+
Your work goes on a branch named `agent-<name>`, unless whoever started you continued you on another. It stays on this machine until you are asked to push it.
|
|
9
|
+
|
|
10
|
+
## The command
|
|
11
|
+
|
|
12
|
+
`branches` is a dependency of this repository (`@openagt/skill-branches`). If `node_modules` is missing, install with the lockfile's package manager (`npm install` for `package-lock.json`). Then run `npx branches` inside your checkout, never a bare `branches`: a fresh clone has none. `status`, `name` and `push` (with no flag) are yours; the rest are the caller's.
|
|
13
|
+
|
|
14
|
+
## Where you are
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
npx branches status
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It prints JSON; `branch` is the branch you are on.
|
|
21
|
+
|
|
22
|
+
**A branch starting with `agent-`.** Its checkout is your whole workspace: read and write only there. Dependency files and skill folders are links to the user's copies: never edit them. If something you need is outside your checkout, say so and stop.
|
|
23
|
+
|
|
24
|
+
Before your first change, name the session with `[a-z0-9-]+`, starting with a letter or digit, saying what the work is, unless your branch already differs from `path`'s last segment: then it is named, keep it.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
npx branches name <name>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
It renames your branch to `agent-<name>` and prints it in `branch`: `agent-<name>-2`, `-3`, … when `<name>` was taken; a name outside `[a-z0-9-]+` is refused as `invalid-name`.
|
|
31
|
+
|
|
32
|
+
**Any other branch.** If the checkout sits under `.branches/`, you were put on this branch on purpose: stay on it, do not name it. Otherwise you are in a plain clone on someone else's branch. Before your first change, create your own and switch to it, `<name>` as above (another if it exists, locally or on origin):
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
git switch -c agent-<name>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
From then on, work as on an `agent-` branch; nothing in a plain clone is a link.
|
|
39
|
+
|
|
40
|
+
## Commit as you go
|
|
41
|
+
|
|
42
|
+
Nothing is committed for you.
|
|
43
|
+
|
|
44
|
+
## Before you finish
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
npx branches status
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
It must report `"clean": true`. `clean` is false while anything is uncommitted or untracked: commit or delete what you added; if what remains is not yours, say so and finish.
|
|
51
|
+
|
|
52
|
+
## Push
|
|
53
|
+
|
|
54
|
+
Only when your task or the person asks you to push or publish your work, and once clean:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
npx branches push
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
It pushes your branch to origin and prints it in `branch`. `clean` false is refused as `dirty`: commit or delete first.
|
|
61
|
+
|
|
62
|
+
## Merge
|
|
63
|
+
|
|
64
|
+
Only when the person asks you to merge your work into the project's main branch, and once clean:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
npx branches merge <your branch>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
It merges your branch into the main branch, in the project's own folder. A conflict changes nothing and names the files in `files`: say so and stop. Your checkout and your branch stay.
|
package/bin/branches
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runCli } from '../dist/cli.js'
|
|
3
|
+
|
|
4
|
+
runCli(process.argv.slice(2), { cwd: process.cwd(), stdout: line => console.log(line), stderr: line => console.error(line) })
|
|
5
|
+
.then(code => {
|
|
6
|
+
process.exitCode = code
|
|
7
|
+
})
|
|
8
|
+
.catch(err => {
|
|
9
|
+
console.error(err)
|
|
10
|
+
process.exitCode = 1
|
|
11
|
+
})
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the `branches` executable lives, for a caller that puts it on a spawned
|
|
3
|
+
* process's PATH (#1725); an agent itself runs it through `npx`. Beside `dist/`, so it is the
|
|
4
|
+
* same path from a workspace checkout and from an installed package.
|
|
5
|
+
*/
|
|
6
|
+
export declare const CLI_BIN_DIR: string;
|
|
7
|
+
//# sourceMappingURL=bin-dir.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin-dir.d.ts","sourceRoot":"","sources":["../src/bin-dir.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,WAAW,QAAqD,CAAA"}
|
package/dist/bin-dir.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
/**
|
|
3
|
+
* Where the `branches` executable lives, for a caller that puts it on a spawned
|
|
4
|
+
* process's PATH (#1725); an agent itself runs it through `npx`. Beside `dist/`, so it is the
|
|
5
|
+
* same path from a workspace checkout and from an installed package.
|
|
6
|
+
*/
|
|
7
|
+
export const CLI_BIN_DIR = fileURLToPath(new URL('../bin/', import.meta.url));
|
|
8
|
+
//# sourceMappingURL=bin-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin-dir.js","sourceRoot":"","sources":["../src/bin-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
import { type WorktreeDirEntry } from './worktree.js';
|
|
3
|
+
/** The filesystem the reconcile needs; `node:fs/promises` in production. */
|
|
4
|
+
export interface LinksFs {
|
|
5
|
+
/** Names under `dir`; a missing dir yields `[]`. */
|
|
6
|
+
readdir(dir: string): Promise<string[]>;
|
|
7
|
+
/** Recursive mkdir. */
|
|
8
|
+
mkdir(dir: string): Promise<void>;
|
|
9
|
+
/** Create a symlink at `path` pointing to `target`. */
|
|
10
|
+
symlink(target: string, path: string): Promise<void>;
|
|
11
|
+
/** A symlink's target, or `undefined` when `path` is missing or not a symlink. */
|
|
12
|
+
readlink(path: string): Promise<string | undefined>;
|
|
13
|
+
/** Remove one file or symlink. */
|
|
14
|
+
unlink(path: string): Promise<void>;
|
|
15
|
+
/** Whether anything (file, dir, or dangling link) sits at `path`. */
|
|
16
|
+
lexists(path: string): Promise<boolean>;
|
|
17
|
+
}
|
|
18
|
+
/** Injectable seams for {@link reconcileBranchLinks}. */
|
|
19
|
+
export interface BranchLinksDeps {
|
|
20
|
+
git?: GitRunner;
|
|
21
|
+
fs?: LinksFs;
|
|
22
|
+
/** The checkouts on disk (default {@link worktreeDirEntries}). */
|
|
23
|
+
worktrees?: (cwd: string) => Promise<WorktreeDirEntry[]>;
|
|
24
|
+
/** The branch a worktree is on, or none when the path is not a worktree root (default {@link worktreeBranch}). */
|
|
25
|
+
branchOf?: (path: string) => Promise<string | undefined>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Bring one project's `.branches/` links in line with its worktrees: one link per worktree, named
|
|
29
|
+
* as the branch the worktree is on right now. Renames are covered by the same rule — the old name
|
|
30
|
+
* stops being wanted and is dropped, the new one is created.
|
|
31
|
+
*
|
|
32
|
+
* Touches only what is provably ours: a link is created, replaced, or removed only when it points
|
|
33
|
+
* (or would point) at a sibling checkout; anything else at those paths — a user's own file, dir,
|
|
34
|
+
* or symlink — is left alone. Never throws.
|
|
35
|
+
*/
|
|
36
|
+
export declare function reconcileBranchLinks(cwd: string, deps?: BranchLinksDeps): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=branch-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-links.d.ts","sourceRoot":"","sources":["../src/branch-links.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,SAAS,EAAgB,MAAM,qBAAqB,CAAA;AAEjF,OAAO,EAAsC,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAYzF,4EAA4E;AAC5E,MAAM,WAAW,OAAO;IACtB,oDAAoD;IACpD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACvC,uBAAuB;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,uDAAuD;IACvD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACnD,kCAAkC;IAClC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,qEAAqE;IACrE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACxC;AAeD,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,EAAE,CAAC,EAAE,OAAO,CAAA;IACZ,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;IACxD,kHAAkH;IAClH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;CACzD;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCjG"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { basename, join } from 'node:path';
|
|
2
|
+
import { nodeGitRunner, BRANCHES_DIR } from '@openagt/agent-data';
|
|
3
|
+
import { isAgentBranch } from './branch-names.js';
|
|
4
|
+
import { worktreeDirEntries, worktreeBranch } from './worktree.js';
|
|
5
|
+
/** A {@link LinksFs} over `node:fs/promises`. */
|
|
6
|
+
function nodeLinksFs() {
|
|
7
|
+
const fs = () => import('node:fs/promises');
|
|
8
|
+
return {
|
|
9
|
+
readdir: dir => fs().then(f => f.readdir(dir)).catch(() => []),
|
|
10
|
+
mkdir: dir => fs().then(f => f.mkdir(dir, { recursive: true })).then(() => { }),
|
|
11
|
+
symlink: (target, path) => fs().then(f => f.symlink(target, path)),
|
|
12
|
+
readlink: path => fs().then(f => f.readlink(path)).catch(() => undefined),
|
|
13
|
+
unlink: path => fs().then(f => f.unlink(path)),
|
|
14
|
+
lexists: path => fs().then(f => f.lstat(path)).then(() => true, () => false),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Bring one project's `.branches/` links in line with its worktrees: one link per worktree, named
|
|
19
|
+
* as the branch the worktree is on right now. Renames are covered by the same rule — the old name
|
|
20
|
+
* stops being wanted and is dropped, the new one is created.
|
|
21
|
+
*
|
|
22
|
+
* Touches only what is provably ours: a link is created, replaced, or removed only when it points
|
|
23
|
+
* (or would point) at a sibling checkout; anything else at those paths — a user's own file, dir,
|
|
24
|
+
* or symlink — is left alone. Never throws.
|
|
25
|
+
*/
|
|
26
|
+
export async function reconcileBranchLinks(cwd, deps = {}) {
|
|
27
|
+
const git = deps.git ?? nodeGitRunner();
|
|
28
|
+
const fs = deps.fs ?? nodeLinksFs();
|
|
29
|
+
const worktrees = deps.worktrees ?? worktreeDirEntries;
|
|
30
|
+
// A `.branches/` directory that is not a worktree root reads as no branch (#1654): otherwise
|
|
31
|
+
// it reads as the enclosing repo's, and a link named after the user's own branch appears.
|
|
32
|
+
const branchOf = deps.branchOf ?? ((path) => worktreeBranch(path, git));
|
|
33
|
+
const linksDir = join(cwd, BRANCHES_DIR);
|
|
34
|
+
/** Link name -> relative target, derived from what is actually checked out. */
|
|
35
|
+
const wanted = new Map();
|
|
36
|
+
for (const entry of await worktrees(cwd).catch(() => [])) {
|
|
37
|
+
const branch = await branchOf(entry.path).catch(() => undefined);
|
|
38
|
+
// A detached worktree has no name to link; a slashed name cannot be a
|
|
39
|
+
// link name at all — both simply get no link.
|
|
40
|
+
if (!branch || branch.includes('/'))
|
|
41
|
+
continue;
|
|
42
|
+
// The link is a sibling of the checkout. A dir already carrying the branch's name needs none.
|
|
43
|
+
const target = basename(entry.path);
|
|
44
|
+
if (branch === target)
|
|
45
|
+
continue;
|
|
46
|
+
wanted.set(branch, target);
|
|
47
|
+
}
|
|
48
|
+
// Drop our stale links: entries that are symlinks to a sibling checkout but no longer wanted,
|
|
49
|
+
// or wanted with a different target (a reused branch name now belongs to a newer worktree).
|
|
50
|
+
for (const name of await fs.readdir(linksDir)) {
|
|
51
|
+
const path = join(linksDir, name);
|
|
52
|
+
const target = await fs.readlink(path);
|
|
53
|
+
if (target === undefined || !isAgentBranch(target))
|
|
54
|
+
continue; // not ours to touch
|
|
55
|
+
if (wanted.get(name) === target)
|
|
56
|
+
continue;
|
|
57
|
+
await fs.unlink(path).catch(() => { });
|
|
58
|
+
}
|
|
59
|
+
for (const [name, target] of wanted) {
|
|
60
|
+
const path = join(linksDir, name);
|
|
61
|
+
if (await fs.lexists(path))
|
|
62
|
+
continue; // ours-and-current, or someone else's — either way, stay
|
|
63
|
+
await fs.mkdir(linksDir);
|
|
64
|
+
await fs.symlink(target, path).catch(() => { });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=branch-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-links.js","sourceRoot":"","sources":["../src/branch-links.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The naming rules for everything the package mints in git, and the layout they imply under
|
|
3
|
+
* `.branches/` (the directory is `@openagt/agent-data`'s convention).
|
|
4
|
+
* Pure: no node imports, so browser-safe code can name branches too.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* An agent id is path-safe — no separators or traversal, only our own charset — and is not `data`:
|
|
8
|
+
* `agent-data` is the shared data branch of `@openagt/agent-data`, checked out beside the agent
|
|
9
|
+
* checkouts as `.branches/agent-data`, and an agent of that id would be indistinguishable from it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSafeAgentId(id: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* What every branch the package mints is named under. Slash-free on purpose: a `/` in a ref name
|
|
14
|
+
* never resolves as a cloud session's revision (anthropics/claude-code#87235), and it is what
|
|
15
|
+
* lets a checkout directory be named exactly as its branch.
|
|
16
|
+
*/
|
|
17
|
+
export declare const AGENT_BRANCH_PREFIX = "agent-";
|
|
18
|
+
/**
|
|
19
|
+
* The branch a checkout is created on: `agent-<agent id>`. The agent id exists before the session
|
|
20
|
+
* name does, so the branch is created from the id and renamed once the agent picks a name. It is
|
|
21
|
+
* also the checkout directory's name under `.branches/`, so the listing reads as branch names.
|
|
22
|
+
*/
|
|
23
|
+
export declare function agentBranchName(agentId: string): string;
|
|
24
|
+
/** The inverse of {@link agentBranchName}: the agent id a checkout directory's name carries. */
|
|
25
|
+
export declare function agentIdFromWorktreeDir(name: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Whether a branch is one the package minted for an agent: the branch a checkout was created on,
|
|
28
|
+
* or the session-named `agent-<name>` it was renamed to. The only branches the package ever
|
|
29
|
+
* renames or deletes; a branch of the user's own is out of reach by name alone, and so is the
|
|
30
|
+
* data branch, `agent-data`, which carries the prefix without being anyone's.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isAgentBranch(name: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* The session name a checkout's branch carries: `agent-<session name>` minus the prefix. The name
|
|
35
|
+
* is read off the branch and never recorded beside it — a checkout has one branch, and that
|
|
36
|
+
* branch is the name. Undefined for a branch the package did not mint, and while the agent has
|
|
37
|
+
* not named its session: the branch is still the one the checkout was created on, which the
|
|
38
|
+
* agent id names — a caller without an id has no such branch to rule out.
|
|
39
|
+
*/
|
|
40
|
+
export declare function sessionNameOf(branch: string | undefined, agentId: string | undefined): string | undefined;
|
|
41
|
+
//# sourceMappingURL=branch-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-names.d.ts","sourceRoot":"","sources":["../src/branch-names.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,WAAW,CAAA;AAE3C;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,gGAAgG;AAChG,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGzG"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DATA_BRANCH } from '@openagt/agent-data/names';
|
|
2
|
+
/**
|
|
3
|
+
* The naming rules for everything the package mints in git, and the layout they imply under
|
|
4
|
+
* `.branches/` (the directory is `@openagt/agent-data`'s convention).
|
|
5
|
+
* Pure: no node imports, so browser-safe code can name branches too.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* An agent id is path-safe — no separators or traversal, only our own charset — and is not `data`:
|
|
9
|
+
* `agent-data` is the shared data branch of `@openagt/agent-data`, checked out beside the agent
|
|
10
|
+
* checkouts as `.branches/agent-data`, and an agent of that id would be indistinguishable from it.
|
|
11
|
+
*/
|
|
12
|
+
export function isSafeAgentId(id) {
|
|
13
|
+
return /^[A-Za-z0-9_-]+$/.test(id) && agentBranchName(id) !== DATA_BRANCH;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* What every branch the package mints is named under. Slash-free on purpose: a `/` in a ref name
|
|
17
|
+
* never resolves as a cloud session's revision (anthropics/claude-code#87235), and it is what
|
|
18
|
+
* lets a checkout directory be named exactly as its branch.
|
|
19
|
+
*/
|
|
20
|
+
export const AGENT_BRANCH_PREFIX = 'agent-';
|
|
21
|
+
/**
|
|
22
|
+
* The branch a checkout is created on: `agent-<agent id>`. The agent id exists before the session
|
|
23
|
+
* name does, so the branch is created from the id and renamed once the agent picks a name. It is
|
|
24
|
+
* also the checkout directory's name under `.branches/`, so the listing reads as branch names.
|
|
25
|
+
*/
|
|
26
|
+
export function agentBranchName(agentId) {
|
|
27
|
+
return `${AGENT_BRANCH_PREFIX}${agentId}`;
|
|
28
|
+
}
|
|
29
|
+
/** The inverse of {@link agentBranchName}: the agent id a checkout directory's name carries. */
|
|
30
|
+
export function agentIdFromWorktreeDir(name) {
|
|
31
|
+
return name.slice(AGENT_BRANCH_PREFIX.length);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether a branch is one the package minted for an agent: the branch a checkout was created on,
|
|
35
|
+
* or the session-named `agent-<name>` it was renamed to. The only branches the package ever
|
|
36
|
+
* renames or deletes; a branch of the user's own is out of reach by name alone, and so is the
|
|
37
|
+
* data branch, `agent-data`, which carries the prefix without being anyone's.
|
|
38
|
+
*/
|
|
39
|
+
export function isAgentBranch(name) {
|
|
40
|
+
return name.startsWith(AGENT_BRANCH_PREFIX) && name !== DATA_BRANCH;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The session name a checkout's branch carries: `agent-<session name>` minus the prefix. The name
|
|
44
|
+
* is read off the branch and never recorded beside it — a checkout has one branch, and that
|
|
45
|
+
* branch is the name. Undefined for a branch the package did not mint, and while the agent has
|
|
46
|
+
* not named its session: the branch is still the one the checkout was created on, which the
|
|
47
|
+
* agent id names — a caller without an id has no such branch to rule out.
|
|
48
|
+
*/
|
|
49
|
+
export function sessionNameOf(branch, agentId) {
|
|
50
|
+
if (!branch || !isAgentBranch(branch) || (agentId !== undefined && branch === agentBranchName(agentId)))
|
|
51
|
+
return undefined;
|
|
52
|
+
return branch.slice(AGENT_BRANCH_PREFIX.length);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=branch-names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-names.js","sourceRoot":"","sources":["../src/branch-names.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAEvD;;;;GAIG;AAEH;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,OAAO,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,CAAC,KAAK,WAAW,CAAA;AAC3E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAA;AAE3C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,GAAG,mBAAmB,GAAG,OAAO,EAAE,CAAA;AAC3C,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAA;AAC/C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,IAAI,IAAI,KAAK,WAAW,CAAA;AACrE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,MAA0B,EAAE,OAA2B;IACnF,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,KAAK,eAAe,CAAC,OAAO,CAAC,CAAC;QAAE,OAAO,SAAS,CAAA;IACzH,OAAO,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAA;AACjD,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
/**
|
|
3
|
+
* What a branch holds and where it stands (#1774): the caller's read of a finished agent's
|
|
4
|
+
* work, for the run page's handoff and the "never pushed" queue. Git facts only, read from the
|
|
5
|
+
* project's repository, so the answer is the same whether or not the agent's checkout still
|
|
6
|
+
* exists: the commits the branch has beyond the project's default branch, the files they
|
|
7
|
+
* changed, whether the remote has the tip, whether the default branch already contains it, and
|
|
8
|
+
* the uncommitted paths of the checkout that is on the branch, when one is. The pull request is
|
|
9
|
+
* the caller's own question, asked of the git host, never here.
|
|
10
|
+
*
|
|
11
|
+
* A caller that knows the commit a branch was made at names it (`from`), and the commits and the
|
|
12
|
+
* files are measured so that they are the branch's own ({@link measuredFrom}): from that commit
|
|
13
|
+
* for a branch made from another branch, which holds, beyond the default branch, that branch's
|
|
14
|
+
* work as well as its own, and for a branch the default branch already contains, which holds
|
|
15
|
+
* nothing beyond it and still did its work. A full commit id asked in place of a branch is read as
|
|
16
|
+
* a branch whose tip it is: what a branch held, for a caller that kept its last commit after the
|
|
17
|
+
* branch went.
|
|
18
|
+
*
|
|
19
|
+
* Forgiving throughout: a branch that is gone answers `exists: false` with empty lists, a
|
|
20
|
+
* project without a remote answers `hasRemote: false`, and a git read that fails reads as empty.
|
|
21
|
+
*/
|
|
22
|
+
/** One commit a branch holds beyond the base. */
|
|
23
|
+
export interface BranchCommit {
|
|
24
|
+
sha: string;
|
|
25
|
+
subject: string;
|
|
26
|
+
}
|
|
27
|
+
/** One file a branch changed against the base. */
|
|
28
|
+
export interface BranchFile {
|
|
29
|
+
path: string;
|
|
30
|
+
insertions: number;
|
|
31
|
+
deletions: number;
|
|
32
|
+
/** A binary file, where line counts mean nothing. */
|
|
33
|
+
binary: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface BranchState {
|
|
36
|
+
branch: string;
|
|
37
|
+
/** The name the agent gave its work: the branch minus this package's prefix. Absent for a branch the package did not mint, and for a checkout still on the branch it was created on. */
|
|
38
|
+
name?: string;
|
|
39
|
+
/** The branch exists in the repository. Gone: every list below is empty. */
|
|
40
|
+
exists: boolean;
|
|
41
|
+
/** What it is measured against: the commit the caller named or the default branch (the remote's, else a local `main` or `master`), whichever tells the branch's own work. Absent when none was found. */
|
|
42
|
+
base?: string;
|
|
43
|
+
/** The branch's own commits beyond the base, newest first. */
|
|
44
|
+
commits: BranchCommit[];
|
|
45
|
+
/** What it changed since it left the base. */
|
|
46
|
+
files: BranchFile[];
|
|
47
|
+
/** The repository has a remote. */
|
|
48
|
+
hasRemote: boolean;
|
|
49
|
+
/** `origin` has the branch at this same tip. */
|
|
50
|
+
pushed: boolean;
|
|
51
|
+
/** The base already contains the branch. */
|
|
52
|
+
merged: boolean;
|
|
53
|
+
/** The uncommitted paths of the checkout under `.branches/` that is on this branch; absent when no checkout is. */
|
|
54
|
+
pendingFiles?: string[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The state of each branch named, in that order. One read of the checkouts serves them all.
|
|
58
|
+
* `from` is the commit they were made at, when the caller knows it; one this machine does not
|
|
59
|
+
* have is no base, and the default branch is.
|
|
60
|
+
*/
|
|
61
|
+
export declare function readBranchStates(repo: string, branches: readonly string[], git?: GitRunner, from?: string): Promise<BranchState[]>;
|
|
62
|
+
/** Parse `git log --format=%H%x1f%s`. */
|
|
63
|
+
export declare function parseCommits(out: string): BranchCommit[];
|
|
64
|
+
/** Parse `git diff --numstat`: `<added>\t<removed>\t<path>`, `-` for a binary file. */
|
|
65
|
+
export declare function parseNumstat(out: string): BranchFile[];
|
|
66
|
+
/** The paths of `git status --porcelain`: a rename reads as its new path, a quoted path unquoted. */
|
|
67
|
+
export declare function parsePorcelain(out: string): string[];
|
|
68
|
+
//# sourceMappingURL=branch-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-state.d.ts","sourceRoot":"","sources":["../src/branch-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAKnE;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,kDAAkD;AAClD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,wLAAwL;IACxL,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,MAAM,EAAE,OAAO,CAAA;IACf,yMAAyM;IACzM,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,8CAA8C;IAC9C,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,mCAAmC;IACnC,SAAS,EAAE,OAAO,CAAA;IAClB,gDAAgD;IAChD,MAAM,EAAE,OAAO,CAAA;IACf,4CAA4C;IAC5C,MAAM,EAAE,OAAO,CAAA;IACf,mHAAmH;IACnH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAKD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,GAAE,SAA2B,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAMzJ;AAqGD,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAQxD;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAUtD;AAED,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAWpD"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { nodeGitRunner } from '@openagt/agent-data';
|
|
2
|
+
import { basename } from 'node:path';
|
|
3
|
+
import { agentIdFromWorktreeDir, sessionNameOf } from './branch-names.js';
|
|
4
|
+
import { repoHasRemote, worktreeBranch, worktreeDirEntries } from './worktree.js';
|
|
5
|
+
/** A subject can hold anything, so the fields are unit-separated rather than space-split. */
|
|
6
|
+
const SEP = String.fromCharCode(31);
|
|
7
|
+
/**
|
|
8
|
+
* The state of each branch named, in that order. One read of the checkouts serves them all.
|
|
9
|
+
* `from` is the commit they were made at, when the caller knows it; one this machine does not
|
|
10
|
+
* have is no base, and the default branch is.
|
|
11
|
+
*/
|
|
12
|
+
export async function readBranchStates(repo, branches, git = nodeGitRunner(), from) {
|
|
13
|
+
const ask = soft(git, repo);
|
|
14
|
+
const [hasRemote, base, checkouts, start] = await Promise.all([repoHasRemote(repo, git), detectBase(ask), checkoutsByBranch(repo, git), from !== undefined ? commitOf(ask, from) : undefined]);
|
|
15
|
+
const states = [];
|
|
16
|
+
for (const branch of branches)
|
|
17
|
+
states.push(await readOne(repo, branch, { git, ask, hasRemote, base, from: start, checkouts }));
|
|
18
|
+
return states;
|
|
19
|
+
}
|
|
20
|
+
/** A full commit id: what a caller asks in place of a branch that is gone. */
|
|
21
|
+
const COMMIT_ID = /^[0-9a-f]{40}$/;
|
|
22
|
+
/** The commit `rev` names, when this machine has it. */
|
|
23
|
+
async function commitOf(ask, rev) {
|
|
24
|
+
return (await ask(['rev-parse', '--verify', '--quiet', `${rev}^{commit}`])).trim() || undefined;
|
|
25
|
+
}
|
|
26
|
+
async function readOne(repo, branch, reads) {
|
|
27
|
+
const { ask, hasRemote, base } = reads;
|
|
28
|
+
const tip = (await commitOf(ask, `refs/heads/${branch}`)) ?? (COMMIT_ID.test(branch) ? await commitOf(ask, branch) : undefined);
|
|
29
|
+
// Whether it is merged is always the default branch's answer; what its own commits and files
|
|
30
|
+
// are measured from is decided below, once the tip is known.
|
|
31
|
+
const checkout = reads.checkouts.get(branch);
|
|
32
|
+
const pending = checkout ? await pendingFiles(reads.git, checkout) : {};
|
|
33
|
+
const name = sessionNameOf(branch, checkout ? agentIdFromWorktreeDir(basename(checkout)) : undefined);
|
|
34
|
+
const named = name ? { name } : {};
|
|
35
|
+
if (!tip)
|
|
36
|
+
return { branch, ...named, exists: false, commits: [], files: [], hasRemote, pushed: false, merged: false, ...pending };
|
|
37
|
+
const from = await measuredFrom(ask, tip, reads.from, base);
|
|
38
|
+
// `base..branch` is the branch's own commits; `base...branch` is the change since it left the
|
|
39
|
+
// base, whatever the base did since. Each spelling answers its own question.
|
|
40
|
+
const [commitsOut, numstatOut, remoteTip, mergedOut] = await Promise.all([
|
|
41
|
+
from ? ask(['log', '--format=%H%x1f%s', `${from}..${tip}`]) : Promise.resolve(''),
|
|
42
|
+
from ? ask(['diff', '--numstat', `${from}...${tip}`]) : Promise.resolve(''),
|
|
43
|
+
hasRemote ? ask(['rev-parse', '--verify', '--quiet', `refs/remotes/origin/${branch}`]) : Promise.resolve(''),
|
|
44
|
+
base ? ask(['branch', '--list', '--merged', base, branch]) : Promise.resolve(''),
|
|
45
|
+
]);
|
|
46
|
+
return {
|
|
47
|
+
branch,
|
|
48
|
+
...named,
|
|
49
|
+
exists: true,
|
|
50
|
+
...(from ? { base: from } : {}),
|
|
51
|
+
commits: parseCommits(commitsOut),
|
|
52
|
+
files: parseNumstat(numstatOut),
|
|
53
|
+
hasRemote,
|
|
54
|
+
pushed: remoteTip.trim() === tip,
|
|
55
|
+
merged: mergedOut.trim().length > 0,
|
|
56
|
+
...pending,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* What a branch's own commits and files are measured from. With no commit named, the default
|
|
61
|
+
* branch. With one, the default branch still, while the branch left it at or after that commit
|
|
62
|
+
* and is not in it yet: what the branch took in from the default branch since, by a merge or a
|
|
63
|
+
* rebase, is then not counted as its own. The named commit otherwise: for a branch the default
|
|
64
|
+
* branch already contains, where nothing would be left to count, and for a branch that left the
|
|
65
|
+
* default branch before that commit, which is one made from another branch.
|
|
66
|
+
*/
|
|
67
|
+
async function measuredFrom(ask, tip, from, base) {
|
|
68
|
+
if (!from || !base)
|
|
69
|
+
return from ?? base;
|
|
70
|
+
const forkBase = (await ask(['merge-base', base, tip])).trim();
|
|
71
|
+
if (!forkBase || forkBase === tip)
|
|
72
|
+
return from;
|
|
73
|
+
const forkFrom = (await ask(['merge-base', from, tip])).trim();
|
|
74
|
+
return forkFrom && (await ask(['merge-base', forkFrom, forkBase])).trim() === forkFrom ? base : from;
|
|
75
|
+
}
|
|
76
|
+
/** `git` that resolves to '' instead of rejecting, for reads where "no answer" is a fine answer. */
|
|
77
|
+
function soft(git, cwd) {
|
|
78
|
+
return args => git(args, cwd).catch(() => '');
|
|
79
|
+
}
|
|
80
|
+
/** The project's default branch: what the remote points HEAD at, else the first local conventional one. */
|
|
81
|
+
async function detectBase(ask) {
|
|
82
|
+
const head = (await ask(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'])).trim();
|
|
83
|
+
if (head)
|
|
84
|
+
return head;
|
|
85
|
+
for (const name of ['main', 'master']) {
|
|
86
|
+
if ((await ask(['rev-parse', '--verify', '--quiet', `refs/heads/${name}`])).trim())
|
|
87
|
+
return name;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
/** Each checkout under `.branches/` that git knows, by the branch it is on. */
|
|
92
|
+
async function checkoutsByBranch(repo, git) {
|
|
93
|
+
const map = new Map();
|
|
94
|
+
for (const entry of await worktreeDirEntries(repo)) {
|
|
95
|
+
const branch = await worktreeBranch(entry.path, git);
|
|
96
|
+
if (branch && !map.has(branch))
|
|
97
|
+
map.set(branch, entry.path);
|
|
98
|
+
}
|
|
99
|
+
return map;
|
|
100
|
+
}
|
|
101
|
+
/** The uncommitted paths of a checkout, as a spreadable field; absent when git could not answer. */
|
|
102
|
+
async function pendingFiles(git, checkout) {
|
|
103
|
+
const status = await git(['status', '--porcelain'], checkout).catch(() => undefined);
|
|
104
|
+
return status === undefined ? {} : { pendingFiles: parsePorcelain(status) };
|
|
105
|
+
}
|
|
106
|
+
/** Parse `git log --format=%H%x1f%s`. */
|
|
107
|
+
export function parseCommits(out) {
|
|
108
|
+
return out
|
|
109
|
+
.split('\n')
|
|
110
|
+
.filter(line => line.includes(SEP))
|
|
111
|
+
.map(line => {
|
|
112
|
+
const [sha = '', subject = ''] = line.split(SEP);
|
|
113
|
+
return { sha, subject };
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** Parse `git diff --numstat`: `<added>\t<removed>\t<path>`, `-` for a binary file. */
|
|
117
|
+
export function parseNumstat(out) {
|
|
118
|
+
const files = [];
|
|
119
|
+
for (const line of out.split('\n')) {
|
|
120
|
+
const [added, removed, ...rest] = line.split('\t');
|
|
121
|
+
const path = rest.join('\t');
|
|
122
|
+
if (!path || added === undefined || removed === undefined)
|
|
123
|
+
continue;
|
|
124
|
+
const binary = added === '-' || removed === '-';
|
|
125
|
+
files.push({ path, insertions: binary ? 0 : Number(added) || 0, deletions: binary ? 0 : Number(removed) || 0, binary });
|
|
126
|
+
}
|
|
127
|
+
return files;
|
|
128
|
+
}
|
|
129
|
+
/** The paths of `git status --porcelain`: a rename reads as its new path, a quoted path unquoted. */
|
|
130
|
+
export function parsePorcelain(out) {
|
|
131
|
+
const paths = [];
|
|
132
|
+
for (const line of out.split('\n')) {
|
|
133
|
+
if (line.length < 4)
|
|
134
|
+
continue;
|
|
135
|
+
let path = line.slice(3);
|
|
136
|
+
const arrow = path.indexOf(' -> ');
|
|
137
|
+
if (arrow !== -1)
|
|
138
|
+
path = path.slice(arrow + 4);
|
|
139
|
+
if (path.length >= 2 && path.startsWith('"') && path.endsWith('"'))
|
|
140
|
+
path = path.slice(1, -1);
|
|
141
|
+
if (path)
|
|
142
|
+
paths.push(path);
|
|
143
|
+
}
|
|
144
|
+
return paths;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=branch-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-state.js","sourceRoot":"","sources":["../src/branch-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
import { type AddedWorktree } from './worktree.js';
|
|
3
|
+
import { type SkillLink } from './skill-links.js';
|
|
4
|
+
/**
|
|
5
|
+
* A checkout as an agent gets it (#1725): the worktree, `.branches/` hidden from git, the parent's
|
|
6
|
+
* dependency trees linked in, the skill linked in where the agent's harness looks for it (#1739),
|
|
7
|
+
* and the `.branches/` links brought up to date — one sequence, whichever surface asks for it (a
|
|
8
|
+
* scheduler starting a run, the command line).
|
|
9
|
+
*/
|
|
10
|
+
/** Other skills to link into the checkout beside this package's own: see `SkillLink`. */
|
|
11
|
+
export interface CheckoutSkills {
|
|
12
|
+
skills?: readonly SkillLink[];
|
|
13
|
+
}
|
|
14
|
+
/** A new agent's checkout, on a fresh `agent-<id>` branch from `base` or origin's default branch. */
|
|
15
|
+
export declare function createCheckout(repo: string, opts: {
|
|
16
|
+
agentId: string;
|
|
17
|
+
base?: string;
|
|
18
|
+
} & CheckoutSkills, git?: GitRunner): Promise<AddedWorktree>;
|
|
19
|
+
/** A continued agent's checkout, back on the branch its work is on; a branch gone everywhere starts again from `base`, or origin's default branch. */
|
|
20
|
+
export declare function attachCheckout(repo: string, opts: {
|
|
21
|
+
agentId: string;
|
|
22
|
+
branch: string;
|
|
23
|
+
base?: string;
|
|
24
|
+
} & CheckoutSkills, git?: GitRunner): Promise<AddedWorktree>;
|
|
25
|
+
//# sourceMappingURL=checkout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAgC,MAAM,qBAAqB,CAAA;AAEjG,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAG/E,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAG5D;;;;;GAKG;AAEH,yFAAyF;AACzF,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;CAC9B;AAED,qGAAqG;AACrG,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,EACzD,GAAG,GAAE,SAA2B,GAC/B,OAAO,CAAC,aAAa,CAAC,CAIxB;AAED,sJAAsJ;AACtJ,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,EACzE,GAAG,GAAE,SAA2B,GAC/B,OAAO,CAAC,aAAa,CAAC,CAIxB"}
|