@openagt/skill-branches 0.0.2 → 0.1.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.
Files changed (46) hide show
  1. package/SKILL.md +6 -6
  2. package/dist/checkout.d.ts +6 -0
  3. package/dist/checkout.d.ts.map +1 -1
  4. package/dist/checkout.js +11 -2
  5. package/dist/checkout.js.map +1 -1
  6. package/dist/cleanup.d.ts +8 -0
  7. package/dist/cleanup.d.ts.map +1 -1
  8. package/dist/cleanup.js +19 -3
  9. package/dist/cleanup.js.map +1 -1
  10. package/dist/command-link.d.ts +37 -13
  11. package/dist/command-link.d.ts.map +1 -1
  12. package/dist/command-link.js +48 -26
  13. package/dist/command-link.js.map +1 -1
  14. package/dist/index.d.ts +4 -3
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +4 -3
  17. package/dist/index.js.map +1 -1
  18. package/dist/skill-links.d.ts +7 -1
  19. package/dist/skill-links.d.ts.map +1 -1
  20. package/dist/skill-links.js +12 -3
  21. package/dist/skill-links.js.map +1 -1
  22. package/package.json +2 -2
  23. package/dist/file-branch.d.ts +0 -150
  24. package/dist/file-branch.d.ts.map +0 -1
  25. package/dist/file-branch.js +0 -371
  26. package/dist/file-branch.js.map +0 -1
  27. package/dist/git-exclude.d.ts +0 -17
  28. package/dist/git-exclude.d.ts.map +0 -1
  29. package/dist/git-exclude.js +0 -30
  30. package/dist/git-exclude.js.map +0 -1
  31. package/dist/git.d.ts +0 -73
  32. package/dist/git.d.ts.map +0 -1
  33. package/dist/git.js +0 -164
  34. package/dist/git.js.map +0 -1
  35. package/dist/merge-hold.d.ts +0 -20
  36. package/dist/merge-hold.d.ts.map +0 -1
  37. package/dist/merge-hold.js +0 -64
  38. package/dist/merge-hold.js.map +0 -1
  39. package/dist/merge-watch.d.ts +0 -68
  40. package/dist/merge-watch.d.ts.map +0 -1
  41. package/dist/merge-watch.js +0 -97
  42. package/dist/merge-watch.js.map +0 -1
  43. package/dist/publish.d.ts +0 -138
  44. package/dist/publish.d.ts.map +0 -1
  45. package/dist/publish.js +0 -215
  46. package/dist/publish.js.map +0 -1
package/dist/git.d.ts DELETED
@@ -1,73 +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
- /** Runs `git` in `cwd`, resolving its stdout. Rejects on a non-zero exit. */
7
- export type GitRunner = (args: string[], cwd: string) => Promise<string>;
8
- /**
9
- * A local read: the index, a ref, or objects already on disk. Kept at the budget that used to
10
- * cover everything, so a hung read still fails fast instead of holding the caller longer (#997).
11
- */
12
- export declare const GIT_READ_TIMEOUT_MS = 10000;
13
- /** A local mutation. Bounded by disk, but an index write on a large repo outlives a read. */
14
- export declare const GIT_WRITE_TIMEOUT_MS = 30000;
15
- /**
16
- * The network, or a whole checkout. `git worktree add` writes every tracked file and `git push`
17
- * uploads a packfile; on a large repo both routinely pass 10s, which is what #997 is about.
18
- */
19
- export declare const GIT_SLOW_TIMEOUT_MS = 120000;
20
- /**
21
- * The timeout for one git invocation, chosen by subcommand (#997). One flat 10s budget once
22
- * covered every call site, so the slowest two ran under what is really a read's budget: a
23
- * SIGTERM'd `worktree add` drops an agent into the user's main checkout, a SIGTERM'd `push` may
24
- * have half-landed.
25
- */
26
- export declare function gitTimeoutMs(args: string[]): number;
27
- /**
28
- * A git killed for outrunning its timeout, as opposed to one git itself rejected (#997).
29
- *
30
- * `execFile` SIGTERMs on timeout, and a killed `git push` usually writes nothing to stderr, so
31
- * without this the failure surfaces as a bare "Command failed: git push ..." that reads like a
32
- * rejected push. The `timedOut` brand is what a caller across a package boundary checks.
33
- */
34
- export declare class GitTimeoutError extends Error {
35
- readonly args: string[];
36
- readonly timeoutMs: number;
37
- readonly timedOut = true;
38
- constructor(args: string[], timeoutMs: number);
39
- }
40
- /** True when a {@link GitRunner} rejection is a timeout kill rather than a non-zero exit. */
41
- export declare function isGitTimeout(err: unknown): err is GitTimeoutError;
42
- /**
43
- * A {@link GitRunner} backed by `execFile('git', ...)`. Rejects on any git error, and with a
44
- * {@link GitTimeoutError} when the operation outran its {@link gitTimeoutMs} budget.
45
- *
46
- * The buffer is raised well past the default because a repo crawl (`git ls-files`) prints a
47
- * line per file, and a large checkout overruns it.
48
- */
49
- export declare function nodeGitRunner(): GitRunner;
50
- /**
51
- * Whether `cwd` sits inside a git working tree (#997). Lets a caller tell "this project cannot
52
- * host a worktree at all" from "git was there and the operation failed", which are the same
53
- * rejection out of `git worktree add` but call for opposite handling.
54
- *
55
- * Forgiving in one direction only: an unreadable / missing git reads as "no repo", which is the
56
- * conservative answer for the caller that treats a repo's failure as fatal.
57
- */
58
- export declare function isGitRepo(cwd: string, git?: GitRunner): Promise<boolean>;
59
- /** The root of the checkout `cwd` is in — an agent's own, from anywhere under it. Rejects outside a repo. */
60
- export declare function checkoutRoot(cwd: string, git?: GitRunner): Promise<string>;
61
- /**
62
- * The line of a failed git invocation worth showing: git's own `fatal:` / `error:` / `remote:`
63
- * line when there is one, else the first line, else a placeholder.
64
- */
65
- export declare function gitReason(err: unknown): string;
66
- /** Push a branch to `origin`, setting its upstream. The failure is git's own line, not a stack. */
67
- export declare function pushBranch(repo: string, branch: string, git?: GitRunner): Promise<{
68
- ok: true;
69
- } | {
70
- ok: false;
71
- error: string;
72
- }>;
73
- //# sourceMappingURL=git.d.ts.map
package/dist/git.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6EAA6E;AAC7E,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAExE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAS,CAAA;AAEzC,6FAA6F;AAC7F,eAAO,MAAM,oBAAoB,QAAS,CAAA;AAE1C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,SAAU,CAAA;AA4C1C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAgBnD;AAED;;;;;;GAMG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IAGtC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM;IAH5B,QAAQ,CAAC,QAAQ,QAAO;IACxB,YACW,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,EAI3B;CACF;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,eAAe,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,SAAS,CAgBzC;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,SAA2B,GAAG,OAAO,CAAC,OAAO,CAAC,CAI/F;AAED,6GAA6G;AAC7G,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,SAA2B,GAAG,OAAO,CAAC,MAAM,CAAC,CAEjG;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAI9C;AAED,mGAAmG;AACnG,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,SAA2B,GAC/B,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAOtD"}
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":"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"}