@openagt/skill-branches 0.1.0 → 0.1.2

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/dist/git.js DELETED
@@ -1,164 +0,0 @@
1
- /**
2
- * Running git: one `execFile`-backed runner with a per-subcommand timeout, and the two readings
3
- * of a failed invocation every caller wants — was it a timeout kill, and which line is worth
4
- * showing.
5
- */
6
- /**
7
- * A local read: the index, a ref, or objects already on disk. Kept at the budget that used to
8
- * cover everything, so a hung read still fails fast instead of holding the caller longer (#997).
9
- */
10
- export const GIT_READ_TIMEOUT_MS = 10_000;
11
- /** A local mutation. Bounded by disk, but an index write on a large repo outlives a read. */
12
- export const GIT_WRITE_TIMEOUT_MS = 30_000;
13
- /**
14
- * The network, or a whole checkout. `git worktree add` writes every tracked file and `git push`
15
- * uploads a packfile; on a large repo both routinely pass 10s, which is what #997 is about.
16
- */
17
- export const GIT_SLOW_TIMEOUT_MS = 120_000;
18
- /** Subcommands that only read. Everything unlisted is treated as a mutation. */
19
- const GIT_READ_OPS = new Set([
20
- 'branch',
21
- 'cat-file',
22
- 'diff',
23
- 'for-each-ref',
24
- 'log',
25
- 'ls-files',
26
- 'merge-base',
27
- 'remote',
28
- 'rev-list',
29
- 'rev-parse',
30
- 'show',
31
- 'show-ref',
32
- 'status',
33
- 'symbolic-ref',
34
- ]);
35
- /** Subcommands bounded by the network rather than by this machine. */
36
- const GIT_SLOW_OPS = new Set(['clone', 'fetch', 'pull', 'push', 'ls-remote']);
37
- /** `git branch` flags that only list or query; any other `branch` invocation writes a ref. */
38
- const GIT_BRANCH_READ_FLAGS = new Set(['--list', '-l', '--contains', '--no-contains', '--merged', '--no-merged', '--points-at', '--show-current', '-r', '--remotes', '-a', '--all', '-v', '-vv', '--verbose']);
39
- /** Global options whose value is the next word, so that word is not the subcommand. */
40
- const GIT_GLOBAL_VALUE_OPTIONS = new Set(['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--exec-path']);
41
- /**
42
- * The subcommand and its own words, with the leading global options dropped. A bare flag filter
43
- * would read `git -C /repo push` as the subcommand `/repo`, costing `push` its slow budget.
44
- */
45
- function gitWords(args) {
46
- let i = 0;
47
- while (i < args.length) {
48
- const arg = args[i] ?? '';
49
- if (!arg.startsWith('-'))
50
- break;
51
- // The `--opt=value` form carries its value inline; the separate form eats the next word.
52
- i += GIT_GLOBAL_VALUE_OPTIONS.has(arg) ? 2 : 1;
53
- }
54
- return args.slice(i).filter(arg => !arg.startsWith('-'));
55
- }
56
- /**
57
- * The timeout for one git invocation, chosen by subcommand (#997). One flat 10s budget once
58
- * covered every call site, so the slowest two ran under what is really a read's budget: a
59
- * SIGTERM'd `worktree add` drops an agent into the user's main checkout, a SIGTERM'd `push` may
60
- * have half-landed.
61
- */
62
- export function gitTimeoutMs(args) {
63
- const words = gitWords(args);
64
- const op = words[0] ?? '';
65
- if (GIT_SLOW_OPS.has(op))
66
- return GIT_SLOW_TIMEOUT_MS;
67
- if (op === 'worktree') {
68
- // Only `add` writes a checkout; `list` is a read, and remove/prune are ordinary mutations.
69
- if (words[1] === 'add')
70
- return GIT_SLOW_TIMEOUT_MS;
71
- return words[1] === 'list' ? GIT_READ_TIMEOUT_MS : GIT_WRITE_TIMEOUT_MS;
72
- }
73
- if (op === 'branch') {
74
- // A bare `branch` or one carrying a listing flag reads; `-D`, `-m`, or `branch <new> [start]` writes a ref.
75
- const flags = args.filter(arg => arg.startsWith('-'));
76
- const reads = words.length === 1 || flags.some(flag => GIT_BRANCH_READ_FLAGS.has(flag));
77
- return reads && !flags.some(flag => !GIT_BRANCH_READ_FLAGS.has(flag) && !flag.startsWith('--format')) ? GIT_READ_TIMEOUT_MS : GIT_WRITE_TIMEOUT_MS;
78
- }
79
- return GIT_READ_OPS.has(op) ? GIT_READ_TIMEOUT_MS : GIT_WRITE_TIMEOUT_MS;
80
- }
81
- /**
82
- * A git killed for outrunning its timeout, as opposed to one git itself rejected (#997).
83
- *
84
- * `execFile` SIGTERMs on timeout, and a killed `git push` usually writes nothing to stderr, so
85
- * without this the failure surfaces as a bare "Command failed: git push ..." that reads like a
86
- * rejected push. The `timedOut` brand is what a caller across a package boundary checks.
87
- */
88
- export class GitTimeoutError extends Error {
89
- args;
90
- timeoutMs;
91
- timedOut = true;
92
- constructor(args, timeoutMs) {
93
- super(`git ${args.join(' ')} timed out after ${timeoutMs}ms`);
94
- this.args = args;
95
- this.timeoutMs = timeoutMs;
96
- this.name = 'GitTimeoutError';
97
- }
98
- }
99
- /** True when a {@link GitRunner} rejection is a timeout kill rather than a non-zero exit. */
100
- export function isGitTimeout(err) {
101
- return err instanceof Error && err.timedOut === true;
102
- }
103
- /**
104
- * A {@link GitRunner} backed by `execFile('git', ...)`. Rejects on any git error, and with a
105
- * {@link GitTimeoutError} when the operation outran its {@link gitTimeoutMs} budget.
106
- *
107
- * The buffer is raised well past the default because a repo crawl (`git ls-files`) prints a
108
- * line per file, and a large checkout overruns it.
109
- */
110
- export function nodeGitRunner() {
111
- return async (args, cwd) => {
112
- const { execFile } = await import('node:child_process');
113
- const timeoutMs = gitTimeoutMs(args);
114
- return new Promise((resolvePromise, rejectPromise) => {
115
- execFile('git', args, { cwd, timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024 }, (err, stdout) => {
116
- if (!err)
117
- return resolvePromise(String(stdout));
118
- // execFile kills on both timeout and a maxBuffer overrun; only the latter carries ENOBUFS.
119
- const killed = err.killed === true;
120
- if (killed && err.code !== 'ENOBUFS') {
121
- return rejectPromise(new GitTimeoutError(args, timeoutMs));
122
- }
123
- rejectPromise(err);
124
- });
125
- });
126
- };
127
- }
128
- /**
129
- * Whether `cwd` sits inside a git working tree (#997). Lets a caller tell "this project cannot
130
- * host a worktree at all" from "git was there and the operation failed", which are the same
131
- * rejection out of `git worktree add` but call for opposite handling.
132
- *
133
- * Forgiving in one direction only: an unreadable / missing git reads as "no repo", which is the
134
- * conservative answer for the caller that treats a repo's failure as fatal.
135
- */
136
- export async function isGitRepo(cwd, git = nodeGitRunner()) {
137
- return git(['rev-parse', '--is-inside-work-tree'], cwd)
138
- .then(out => out.trim() === 'true')
139
- .catch(() => false);
140
- }
141
- /** The root of the checkout `cwd` is in — an agent's own, from anywhere under it. Rejects outside a repo. */
142
- export async function checkoutRoot(cwd, git = nodeGitRunner()) {
143
- return (await git(['rev-parse', '--show-toplevel'], cwd)).trim();
144
- }
145
- /**
146
- * The line of a failed git invocation worth showing: git's own `fatal:` / `error:` / `remote:`
147
- * line when there is one, else the first line, else a placeholder.
148
- */
149
- export function gitReason(err) {
150
- const message = err instanceof Error ? err.message : String(err);
151
- const lines = message.split('\n').map(line => line.trim()).filter(Boolean);
152
- return lines.find(line => /^(fatal|error|remote):/i.test(line)) ?? lines[0] ?? 'git failed';
153
- }
154
- /** Push a branch to `origin`, setting its upstream. The failure is git's own line, not a stack. */
155
- export async function pushBranch(repo, branch, git = nodeGitRunner()) {
156
- try {
157
- await git(['push', '--set-upstream', 'origin', branch], repo);
158
- return { ok: true };
159
- }
160
- catch (err) {
161
- return { ok: false, error: gitReason(err) };
162
- }
163
- }
164
- //# sourceMappingURL=git.js.map
package/dist/git.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
@@ -1,20 +0,0 @@
1
- import { type GitRunner } from '@gemstack/agent-data';
2
- /** Under `.branches/`: one file per pull request whose merge is wanted and held. Not a checkout name, so nothing lists it. */
3
- export declare const HELD_DIR = "merge-held";
4
- /** The line a held pull request's body carries, for the person who reads it; the release takes it out. */
5
- export declare const MERGE_HELD_NOTE = "**Merge held:** more work comes on this branch first; the merge is armed once it is done.";
6
- /** Put the hold on a checkout: a publish from it asked to merge arms nothing until the release. */
7
- export declare function holdMerge(checkout: string, git: GitRunner): Promise<void>;
8
- /** Whether a checkout is under a hold. */
9
- export declare function mergeHeld(checkout: string, git: GitRunner): Promise<boolean>;
10
- /** Record that a pull request's merge is wanted and held. */
11
- export declare function recordHeldMerge(repo: string, number: number): Promise<void>;
12
- /** Whether a pull request's merge is recorded as wanted and held. */
13
- export declare function heldMergeRecorded(repo: string, number: number): Promise<boolean>;
14
- /** Drop the record: the merge was armed, or the pull request is no longer open. */
15
- export declare function dropHeldMerge(repo: string, number: number): Promise<void>;
16
- /** A body with the held note at its end, once. */
17
- export declare function withHeldNote(body: string): string;
18
- /** A body without the held note. */
19
- export declare function withoutHeldNote(body: string): string;
20
- //# sourceMappingURL=merge-hold.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"merge-hold.d.ts","sourceRoot":"","sources":["../src/merge-hold.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAkBnE,8HAA8H;AAC9H,eAAO,MAAM,QAAQ,eAAe,CAAA;AAEpC,0GAA0G;AAC1G,eAAO,MAAM,eAAe,8FAA8F,CAAA;AAE1H,mGAAmG;AACnG,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/E;AAED,0CAA0C;AAC1C,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAElF;AAED,6DAA6D;AAC7D,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjF;AAED,qEAAqE;AACrE,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEtF;AAED,mFAAmF;AACnF,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/E;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED,oCAAoC;AACpC,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOpD"}
@@ -1,64 +0,0 @@
1
- import { mkdir, rm, stat, writeFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
- import { BRANCHES_DIR } from '@gemstack/agent-data';
4
- /**
5
- * A held merge: whoever started the agent has more work coming on its branch after it, so the
6
- * merge its publish asks for waits until the last of it is done. The caller puts the hold on the
7
- * checkout before the agent starts; a publish asked to merge under it opens the pull request as
8
- * always, arms nothing, and records that the merge is wanted; the caller releases it once the rest
9
- * is done. Names no command and no caller: a hold is only "not yet".
10
- *
11
- * Two files, both out of git's sight. The hold is the checkout's own, in its private git
12
- * directory, so it goes with the checkout. The record of a wanted merge is the pull request's,
13
- * under `.branches/` beside the merge watcher's logs, so it outlives the checkout: the caller
14
- * releases it after the checkout the agent published from is gone.
15
- */
16
- /** The hold's file, in the checkout's private git directory. */
17
- const HOLD_FILE = 'branches-merge-hold';
18
- /** Under `.branches/`: one file per pull request whose merge is wanted and held. Not a checkout name, so nothing lists it. */
19
- export const HELD_DIR = 'merge-held';
20
- /** The line a held pull request's body carries, for the person who reads it; the release takes it out. */
21
- export const MERGE_HELD_NOTE = '**Merge held:** more work comes on this branch first; the merge is armed once it is done.';
22
- /** Put the hold on a checkout: a publish from it asked to merge arms nothing until the release. */
23
- export async function holdMerge(checkout, git) {
24
- await writeFile(await holdPath(checkout, git), '');
25
- }
26
- /** Whether a checkout is under a hold. */
27
- export async function mergeHeld(checkout, git) {
28
- return stat(await holdPath(checkout, git)).then(() => true, () => false);
29
- }
30
- /** Record that a pull request's merge is wanted and held. */
31
- export async function recordHeldMerge(repo, number) {
32
- await mkdir(join(repo, BRANCHES_DIR, HELD_DIR), { recursive: true });
33
- await writeFile(heldPath(repo, number), '');
34
- }
35
- /** Whether a pull request's merge is recorded as wanted and held. */
36
- export async function heldMergeRecorded(repo, number) {
37
- return stat(heldPath(repo, number)).then(() => true, () => false);
38
- }
39
- /** Drop the record: the merge was armed, or the pull request is no longer open. */
40
- export async function dropHeldMerge(repo, number) {
41
- await rm(heldPath(repo, number), { force: true });
42
- }
43
- /** A body with the held note at its end, once. */
44
- export function withHeldNote(body) {
45
- if (body.split('\n').some(line => line.trim() === MERGE_HELD_NOTE))
46
- return body;
47
- return body.trim() === '' ? MERGE_HELD_NOTE : `${body.trimEnd()}\n\n${MERGE_HELD_NOTE}`;
48
- }
49
- /** A body without the held note. */
50
- export function withoutHeldNote(body) {
51
- return body
52
- .split('\n')
53
- .filter(line => line.trim() !== MERGE_HELD_NOTE)
54
- .join('\n')
55
- .replace(/\n{3,}/g, '\n\n')
56
- .trimEnd();
57
- }
58
- async function holdPath(checkout, git) {
59
- return join((await git(['rev-parse', '--absolute-git-dir'], checkout)).trim(), HOLD_FILE);
60
- }
61
- function heldPath(repo, number) {
62
- return join(repo, BRANCHES_DIR, HELD_DIR, String(number));
63
- }
64
- //# sourceMappingURL=merge-hold.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"merge-hold.js","sourceRoot":"","sources":["../src/merge-hold.ts"],"names":[],"mappings":"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"}
@@ -1,68 +0,0 @@
1
- import type { GhRunner } from './publish.js';
2
- /**
3
- * Merge on green where the repository does not allow GitHub auto-merge: a small process of this
4
- * tool's own that waits for one pull request's checks and merges it once they pass. It holds
5
- * nothing but the number: the pull request on GitHub is the whole state, so a watcher that dies
6
- * (a reboot) leaves an open request a person can merge, never a wrong merge.
7
- */
8
- /** Where a pull request's checks stand, summarised to the question the merge asks. */
9
- export type ChecksState = 'passing' | 'failing' | 'pending' | 'none';
10
- /** How long a pull request with no checks waits for its checks to attach before it counts as green. */
11
- export declare const NO_CHECKS_GRACE_MS: number;
12
- /** How often the checks are read. */
13
- export declare const WATCH_EVERY_MS = 60000;
14
- /** How long a watcher waits at most: a suite still pending after this is left to a person. */
15
- export declare const WATCH_FOR_MS: number;
16
- /** Under `.branches/`: one log per watched pull request. Not a checkout name, so nothing lists it. */
17
- export declare const WATCH_LOG_DIR = "merge-on-green";
18
- export type WatchOutcome = {
19
- outcome: 'merged';
20
- }
21
- /** Merged or closed by someone else meanwhile. */
22
- | {
23
- outcome: 'closed';
24
- state: string;
25
- } | {
26
- outcome: 'checks-failed';
27
- failed: string[];
28
- } | {
29
- outcome: 'timed-out';
30
- } | {
31
- outcome: 'failed';
32
- error: string;
33
- };
34
- interface PrView {
35
- state: string;
36
- checks: ChecksState;
37
- failed: string[];
38
- }
39
- /**
40
- * One read of the pull request: its state and its checks, GitHub Actions check runs and classic
41
- * commit statuses alike. Skipped and neutral count as passing, as GitHub's own merge box counts
42
- * them; a cancelled or timed-out check is not evidence the work is good, so it counts as failed.
43
- * A read gh cannot answer is `pending`: acting on an unreadable status must never merge.
44
- */
45
- export declare function readPr(repo: string, number: number, gh: GhRunner): Promise<PrView>;
46
- export interface WatchOptions {
47
- gh: GhRunner;
48
- now?: () => number;
49
- sleep?: (ms: number) => Promise<void>;
50
- everyMs?: number;
51
- forMs?: number;
52
- graceMs?: number;
53
- log?: (line: string) => void;
54
- }
55
- /**
56
- * Wait for the pull request's checks, then merge it (squash). Red ends the watch with the request
57
- * left open for a person; a request merged or closed meanwhile ends it too. No checks at all is
58
- * green once the grace has passed: a repository without CI has nothing to wait for.
59
- */
60
- export declare function watchAndMerge(repo: string, number: number, opts: WatchOptions): Promise<WatchOutcome>;
61
- /**
62
- * Start the watcher for one pull request as its own process, detached, so it outlives the agent
63
- * that published: `branches merge-on-green <number>` at the project root, its log under
64
- * `.branches/merge-on-green/<number>.log`. Resolves once the process exists.
65
- */
66
- export declare function spawnMergeWatch(repo: string, number: number): Promise<void>;
67
- export {};
68
- //# sourceMappingURL=merge-watch.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"merge-watch.d.ts","sourceRoot":"","sources":["../src/merge-watch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE5C;;;;;GAKG;AAEH,sFAAsF;AACtF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;AAEpE,uGAAuG;AACvG,eAAO,MAAM,kBAAkB,QAAa,CAAA;AAC5C,qCAAqC;AACrC,eAAO,MAAM,cAAc,QAAS,CAAA;AACpC,8FAA8F;AAC9F,eAAO,MAAM,YAAY,QAAkB,CAAA;AAE3C,sGAAsG;AACtG,eAAO,MAAM,aAAa,mBAAmB,CAAA;AAE7C,MAAM,MAAM,YAAY,GACpB;IAAE,OAAO,EAAE,QAAQ,CAAA;CAAE;AACvB,kDAAkD;GAChD;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,OAAO,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9C;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,GACxB;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAExC,UAAU,MAAM;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,WAAW,CAAA;IACnB,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BxF;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,QAAQ,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAsB3G;AAKD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjF"}
@@ -1,97 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { closeSync, mkdirSync, openSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import { BRANCHES_DIR } from '@gemstack/agent-data';
6
- /** How long a pull request with no checks waits for its checks to attach before it counts as green. */
7
- export const NO_CHECKS_GRACE_MS = 2 * 60_000;
8
- /** How often the checks are read. */
9
- export const WATCH_EVERY_MS = 60_000;
10
- /** How long a watcher waits at most: a suite still pending after this is left to a person. */
11
- export const WATCH_FOR_MS = 6 * 60 * 60_000;
12
- /** Under `.branches/`: one log per watched pull request. Not a checkout name, so nothing lists it. */
13
- export const WATCH_LOG_DIR = 'merge-on-green';
14
- /**
15
- * One read of the pull request: its state and its checks, GitHub Actions check runs and classic
16
- * commit statuses alike. Skipped and neutral count as passing, as GitHub's own merge box counts
17
- * them; a cancelled or timed-out check is not evidence the work is good, so it counts as failed.
18
- * A read gh cannot answer is `pending`: acting on an unreadable status must never merge.
19
- */
20
- export async function readPr(repo, number, gh) {
21
- let parsed;
22
- try {
23
- parsed = JSON.parse(await gh(['pr', 'view', String(number), '--json', 'state,statusCheckRollup'], repo));
24
- }
25
- catch {
26
- return { state: 'OPEN', checks: 'pending', failed: [] };
27
- }
28
- const state = typeof parsed.state === 'string' ? parsed.state : 'OPEN';
29
- const rollup = Array.isArray(parsed.statusCheckRollup) ? parsed.statusCheckRollup : [];
30
- if (rollup.length === 0)
31
- return { state, checks: 'none', failed: [] };
32
- const passing = /^(SUCCESS|NEUTRAL|SKIPPED)$/;
33
- let pending = false;
34
- const failed = [];
35
- for (const entry of rollup) {
36
- // A classic status has no `status` field: its `state` is both progress and verdict.
37
- const verdict = entry.conclusion ?? entry.state ?? '';
38
- const done = entry.status ? entry.status === 'COMPLETED' : verdict !== 'PENDING' && verdict !== 'EXPECTED';
39
- if (!done)
40
- pending = true;
41
- else if (!passing.test(verdict))
42
- failed.push(entry.name ?? entry.context ?? 'unnamed check');
43
- }
44
- return { state, checks: failed.length > 0 ? 'failing' : pending ? 'pending' : 'passing', failed };
45
- }
46
- /**
47
- * Wait for the pull request's checks, then merge it (squash). Red ends the watch with the request
48
- * left open for a person; a request merged or closed meanwhile ends it too. No checks at all is
49
- * green once the grace has passed: a repository without CI has nothing to wait for.
50
- */
51
- export async function watchAndMerge(repo, number, opts) {
52
- const now = opts.now ?? Date.now;
53
- const sleep = opts.sleep ?? (ms => new Promise(resolve => setTimeout(resolve, ms)));
54
- const log = opts.log ?? (() => { });
55
- const started = now();
56
- for (;;) {
57
- const pr = await readPr(repo, number, opts.gh);
58
- const elapsed = now() - started;
59
- log(`[branches] #${number}: ${pr.state}, checks ${pr.checks}`);
60
- if (pr.state !== 'OPEN')
61
- return { outcome: 'closed', state: pr.state };
62
- if (pr.checks === 'failing')
63
- return { outcome: 'checks-failed', failed: pr.failed };
64
- if (pr.checks === 'passing' || (pr.checks === 'none' && elapsed >= (opts.graceMs ?? NO_CHECKS_GRACE_MS))) {
65
- try {
66
- await opts.gh(['pr', 'merge', String(number), '--squash'], repo);
67
- return { outcome: 'merged' };
68
- }
69
- catch (err) {
70
- return { outcome: 'failed', error: err instanceof Error ? err.message : String(err) };
71
- }
72
- }
73
- if (elapsed >= (opts.forMs ?? WATCH_FOR_MS))
74
- return { outcome: 'timed-out' };
75
- await sleep(opts.everyMs ?? WATCH_EVERY_MS);
76
- }
77
- }
78
- /** This package's executable, beside `dist/`. */
79
- const BIN = fileURLToPath(new URL('../bin/branches', import.meta.url));
80
- /**
81
- * Start the watcher for one pull request as its own process, detached, so it outlives the agent
82
- * that published: `branches merge-on-green <number>` at the project root, its log under
83
- * `.branches/merge-on-green/<number>.log`. Resolves once the process exists.
84
- */
85
- export async function spawnMergeWatch(repo, number) {
86
- const dir = join(repo, BRANCHES_DIR, WATCH_LOG_DIR);
87
- mkdirSync(dir, { recursive: true });
88
- const fd = openSync(join(dir, `${number}.log`), 'a');
89
- const child = spawn(process.execPath, [BIN, 'merge-on-green', String(number)], { cwd: repo, detached: true, stdio: ['ignore', fd, fd] });
90
- closeSync(fd);
91
- child.unref();
92
- await new Promise((resolve, reject) => {
93
- child.once('spawn', resolve);
94
- child.once('error', reject);
95
- });
96
- }
97
- //# sourceMappingURL=merge-watch.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"merge-watch.js","sourceRoot":"","sources":["../src/merge-watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAanD,uGAAuG;AACvG,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAG,MAAM,CAAA;AAC5C,qCAAqC;AACrC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAA;AACpC,8FAA8F;AAC9F,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,MAAM,CAAA;AAE3C,sGAAsG;AACtG,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAA;AAgB7C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY,EAAE,MAAc,EAAE,EAAY;IAQrE,IAAI,MAA6D,CAAA;IACjE,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,yBAAyB,CAAC,EAAE,IAAI,CAAC,CAAkB,CAAA;IAC3H,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;IACzD,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAA;IACtE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAA;IACtF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;IACrE,MAAM,OAAO,GAAG,6BAA6B,CAAA;IAC7C,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,oFAAoF;QACpF,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE,CAAA;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,UAAU,CAAA;QAC1G,IAAI,CAAC,IAAI;YAAE,OAAO,GAAG,IAAI,CAAA;aACpB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,IAAI,eAAe,CAAC,CAAA;IAC9F,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,CAAA;AACnG,CAAC;AAYD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,MAAc,EAAE,IAAkB;IAClF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;IACzF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAClC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAA;IACrB,SAAS,CAAC;QACR,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,CAAA;QAC9C,MAAM,OAAO,GAAG,GAAG,EAAE,GAAG,OAAO,CAAA;QAC/B,GAAG,CAAC,eAAe,MAAM,KAAK,EAAE,CAAC,KAAK,YAAY,EAAE,CAAC,MAAM,EAAE,CAAC,CAAA;QAC9D,IAAI,EAAE,CAAC,KAAK,KAAK,MAAM;YAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,CAAA;QACtE,IAAI,EAAE,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAA;QACnF,IAAI,EAAE,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,kBAAkB,CAAC,CAAC,EAAE,CAAC;YACzG,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,CAAA;gBAChE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;YAC9B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAA;YACvF,CAAC;QACH,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC;YAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;QAC5E,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,cAAc,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAEtE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,MAAc;IAChE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,aAAa,CAAC,CAAA;IACnD,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACnC,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,GAAG,CAAC,CAAA;IACpD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;IACxI,SAAS,CAAC,EAAE,CAAC,CAAA;IACb,KAAK,CAAC,KAAK,EAAE,CAAA;IACb,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAC5B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC"}
package/dist/publish.d.ts DELETED
@@ -1,138 +0,0 @@
1
- import { type GitRunner } from '@gemstack/agent-data';
2
- /**
3
- * Publishing a checkout (#1774): the agent's own last step. Push the branch, open its pull
4
- * request with the title and body the agent wrote, and arm the merge when the agent was told
5
- * the work may land on its own. One rule before any of it: the tree is clean, since what is
6
- * published is what is committed, and nothing is committed on the agent's behalf.
7
- *
8
- * A branch that already has an open pull request gets no second one: the request is reported as
9
- * it is, and only the push and the merge arming happen.
10
- *
11
- * The merge is GitHub's auto-merge where the repository allows it. Where it does not, this tool's
12
- * own watcher waits for the checks and merges (`merge-watch.ts`); where the request is already
13
- * green, GitHub will not arm anything and the request is merged at once.
14
- *
15
- * A checkout under a hold (`merge-hold.ts`) arms nothing: the merge is recorded as wanted, the
16
- * request's body says it is held, and the caller who put the hold releases it (`releaseMerge`).
17
- *
18
- * A person publishes a finished agent's work through the same door (#1774), by branch
19
- * (`publishBranch`): the agent's checkout when one is still on the branch, under the same clean
20
- * rule; else the branch itself, pushed when this machine has it, as it is when only the remote
21
- * does. And lands its request (`mergePr`): a draft is marked ready first, then the merge is
22
- * armed exactly as `--merge` arms it.
23
- */
24
- /** A `gh` runner: the standard output of one invocation; rejects with gh's own line on failure. */
25
- export type GhRunner = (args: string[], cwd: string) => Promise<string>;
26
- /** `gh` on PATH, one minute per call: these talk to the network. */
27
- export declare function nodeGhRunner(): GhRunner;
28
- export interface PublishOptions {
29
- /** The pull request's title: one line naming what the change does. */
30
- title: string;
31
- /** The pull request's body: what changed and why. */
32
- body?: string;
33
- /** Arm the merge: the request lands on its own once its checks pass. */
34
- merge?: boolean;
35
- /** Start the merge watcher for a request (default {@link spawnMergeWatch}). For tests. */
36
- watch?: (repo: string, number: number) => Promise<void>;
37
- /** Open the request as a draft. Not with `merge`: a draft cannot be merged. */
38
- draft?: boolean;
39
- git?: GitRunner;
40
- gh?: GhRunner;
41
- }
42
- /** Why a checkout was not published. */
43
- export type PublishRefusal =
44
- /** The directory is not a git worktree root. */
45
- 'not-a-worktree'
46
- /** The checkout is on no branch. */
47
- | 'no-branch'
48
- /** The tree holds uncommitted work: commit or delete it first. */
49
- | 'dirty'
50
- /** The branch could not be pushed. */
51
- | 'push-failed'
52
- /** The pull request could not be opened. */
53
- | 'pr-failed';
54
- export type PublishOutcome = {
55
- ok: true;
56
- branch: string;
57
- pr: {
58
- number: number;
59
- url: string;
60
- };
61
- /** Whether the request was open already. */
62
- existing: boolean;
63
- /**
64
- * How the merge arming went, when asked for: GitHub's auto-merge armed, merged at once (the
65
- * request was already green), this tool's watcher started (the repository does not allow
66
- * auto-merge), or held (the checkout is under a hold; armed at its release).
67
- */
68
- merge?: MergeArming | {
69
- outcome: 'held';
70
- };
71
- } | {
72
- ok: false;
73
- reason: 'not-a-worktree';
74
- }
75
- /** The checkout is on no branch; or, published by name, neither this machine nor the remote has the branch. */
76
- | {
77
- ok: false;
78
- reason: 'no-branch';
79
- branch?: string;
80
- } | {
81
- ok: false;
82
- reason: 'dirty' | 'push-failed' | 'pr-failed';
83
- branch: string;
84
- detail?: string;
85
- };
86
- /** How arming a merge went. */
87
- export type MergeArming = {
88
- outcome: 'auto-armed' | 'merged' | 'watching';
89
- } | {
90
- outcome: 'failed';
91
- error: string;
92
- };
93
- export declare function publishCheckout(path: string, opts: PublishOptions): Promise<PublishOutcome>;
94
- /**
95
- * Publish a branch by name, from the project (#1774): the caller's form, for a person landing a
96
- * finished agent's work. The checkout under `.branches/` that is on the branch, when one is,
97
- * is published as the agent would publish it, its clean rule included. Without one, the branch
98
- * itself: pushed when this machine has it; left as it is when only `origin` has it (a branch
99
- * pushed from elsewhere, with nothing here to push). Neither: `no-branch`. No hold applies
100
- * without a checkout: a hold is a checkout's.
101
- */
102
- export declare function publishBranch(repo: string, branch: string, opts: PublishOptions): Promise<PublishOutcome>;
103
- /** What landing a request for a person did: armed as `--merge` arms, or why not. */
104
- export type MergePrOutcome = MergeArming | {
105
- outcome: 'not-open';
106
- state: string;
107
- };
108
- export interface MergePrOptions {
109
- gh?: GhRunner;
110
- /** Start the merge watcher for a request (default {@link spawnMergeWatch}). For tests. */
111
- watch?: (repo: string, number: number) => Promise<void>;
112
- }
113
- /**
114
- * Land a pull request for a person (#1774): "it is good, land it". A draft is marked ready
115
- * first, since asking for the merge is the statement that its review happened; then the merge
116
- * is armed exactly as `publish --merge` arms it. A request that is not open is `not-open` with
117
- * its state: already merged is an answer, not an action.
118
- */
119
- export declare function mergePr(repo: string, number: number, opts?: MergePrOptions): Promise<MergePrOutcome>;
120
- export type ReleaseOutcome = MergeArming | {
121
- outcome: 'not-held';
122
- } | {
123
- outcome: 'closed';
124
- state: string;
125
- };
126
- export interface ReleaseOptions {
127
- gh?: GhRunner;
128
- /** Start the merge watcher for a request (default {@link spawnMergeWatch}). For tests. */
129
- watch?: (repo: string, number: number) => Promise<void>;
130
- }
131
- /**
132
- * Release a held merge: the request's merge is armed the way `publish --merge` arms it, and then
133
- * the held note leaves its body. A request with no held merge is `not-held` (its publish never asked
134
- * to merge), one no longer open is `closed` and its record dropped. A failed arming keeps the
135
- * record and the note, so the release can be tried again and the request still says it is held.
136
- */
137
- export declare function releaseMerge(repo: string, number: number, opts?: ReleaseOptions): Promise<ReleaseOutcome>;
138
- //# sourceMappingURL=publish.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"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"}