@openagt/skill-github 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 +42 -0
- package/bin/github +11 -0
- package/dist/cli.d.ts +39 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +182 -0
- package/dist/cli.js.map +1 -0
- package/dist/create.d.ts +45 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +37 -0
- package/dist/create.js.map +1 -0
- package/dist/gh.d.ts +11 -0
- package/dist/gh.d.ts.map +1 -0
- package/dist/gh.js +23 -0
- package/dist/gh.js.map +1 -0
- package/dist/home.d.ts +17 -0
- package/dist/home.d.ts.map +1 -0
- package/dist/home.js +36 -0
- package/dist/home.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.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 +35 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +61 -0
- package/dist/merge.js.map +1 -0
- package/dist/open.d.ts +63 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +52 -0
- package/dist/open.js.map +1 -0
- package/dist/requests.d.ts +50 -0
- package/dist/requests.d.ts.map +1 -0
- package/dist/requests.js +61 -0
- package/dist/requests.js.map +1 -0
- package/package.json +51 -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,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github
|
|
3
|
+
description: "The project's git host is GitHub: how to open your pull request when you are asked to, how to land it, and how to read and close the project's issues."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GitHub
|
|
7
|
+
|
|
8
|
+
The project's pull requests and issues are on GitHub. This skill is how you reach them.
|
|
9
|
+
|
|
10
|
+
## The command
|
|
11
|
+
|
|
12
|
+
`github` is a dependency of this repository (`@openagt/skill-github`). If `node_modules` is missing, install with the lockfile's package manager (`npm install` for `package-lock.json`). Then run `npx github` inside your checkout. It prints JSON; a refusal exits 1 with a line on stderr; a wrong command line exits 2 with the usage.
|
|
13
|
+
|
|
14
|
+
## Your pull request
|
|
15
|
+
|
|
16
|
+
Open a pull request only when your task or the person asks for one, or asks you to publish your work. Push your branch first (your branches skill says how), then:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
npx github open --title "<one line naming what the change does>" --body "<what changed, and why>"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
It opens the pull request for the branch you are on and prints it in `request` (`number`, `url`). A branch that already has an open pull request gets no second one: that one is answered, with `existing` true. Add `--merge` when the work may land on its own: the pull request then merges once its checks pass, also where the repository does not allow auto-merge (`merge` in the answer says `auto-armed`, `merged` or `watching`). Add `--draft` instead for a pull request a person should look at first; with `--merge` too, `--draft` is dropped.
|
|
23
|
+
|
|
24
|
+
A pull request whose body has a line `Closes #<number>` closes that issue when it merges.
|
|
25
|
+
|
|
26
|
+
## Reading pull requests and issues
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
npx github requests [--branch <b>] [--state open|merged|all] [--since <iso>]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The project's newest 50 pull requests, newest first, all states unless `--state`, only `<b>`'s with `--branch`; `--since` keeps those created since, or merged since with `--state merged`. Each has `number`, `url`, `state` (`open`, `merged`, `closed`), `title`, `draft`, `branch`, `head`, `createdAt`, and for a merged one `mergedAt` and `mergeCommit` (the commit it landed as).
|
|
33
|
+
|
|
34
|
+
For a pull request's discussion and diff, and for the project's issues, use `gh` directly: `gh pr view <number> --comments`, `gh pr diff <number>`, `gh issue list`, `gh issue view <number> --comments`, `gh issue comment <number> --body "<text>"`. `gh` is installed and logged in where you run.
|
|
35
|
+
|
|
36
|
+
## Landing a pull request for a person
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
npx github merge <number>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A draft is marked ready, then the merge is armed as `--merge` arms it: `outcome` says `auto-armed`, `merged` or `watching`; a request no longer open is refused as `not-open`. Only when whoever started you asked you to land it.
|
package/bin/github
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
|
+
})
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
import { type GhRunner } from './gh.js';
|
|
3
|
+
/**
|
|
4
|
+
* The command line over the package (#1820): the same functions for an agent in a shell, and for
|
|
5
|
+
* the framework and the scheduler, which run this command as the project's git host provider,
|
|
6
|
+
* declared in package.json as `"framework": { "git-host": "github" }`. One implementation, every
|
|
7
|
+
* surface a caller.
|
|
8
|
+
*
|
|
9
|
+
* The contract, the same as the other skills' commands: JSON on stdout, one line for a person on
|
|
10
|
+
* stderr, and the exit code says how it went — 0 for a result, 1 for a refusal or a failure, 2 for
|
|
11
|
+
* a command that could not be read. A refusal is reported on stdout as `{ ok: false, reason }` so
|
|
12
|
+
* a caller parsing the output learns why, and on stderr so a person does.
|
|
13
|
+
*
|
|
14
|
+
* Every command acts on the repository the working directory is in: `gh` finds the repository
|
|
15
|
+
* and its remote from there, as it does for a person.
|
|
16
|
+
*/
|
|
17
|
+
export declare const USAGE = "usage: github <command>\n\n requests [--branch <b>] [--state open|merged|all] [--since <iso>]\n the project's pull requests, newest first: number, url, state, title, draft, branch, head, createdAt, mergedAt, mergeCommit\n open [--branch <b>] --title <t> [--body <text>] [--draft] [--merge]\n open the pull request of branch <b> (default: the current branch; it must be on the remote already); an open one is answered as it is; --merge lands it on green\n merge <number> land pull request <number>: a draft is marked ready, then the merge is armed as --merge arms it\n watch <number> wait for pull request <number>'s checks and merge it once they pass; what --merge starts where the repository has no auto-merge\n home the project's page on GitHub, from its origin remote\n create [--check] create a private repository for a project with no origin, named <account>/<folder>, set it as origin and push;\n --check only says which repository that would be\n\nJSON on stdout. Exit code 1 for a refusal or a failure (the reason on stderr), 2 for a usage error.";
|
|
18
|
+
/** The streams and the working directory a run of the CLI sees. */
|
|
19
|
+
export interface CliIo {
|
|
20
|
+
cwd: string;
|
|
21
|
+
stdout: (line: string) => void;
|
|
22
|
+
stderr: (line: string) => void;
|
|
23
|
+
}
|
|
24
|
+
/** What a run of the CLI may be given instead of the real gh and git: for tests. */
|
|
25
|
+
export interface CliDeps {
|
|
26
|
+
gh?: GhRunner;
|
|
27
|
+
git?: GitRunner;
|
|
28
|
+
/** Start the merge watcher for a request; the default starts this package's own process. */
|
|
29
|
+
watch?: (repo: string, number: number) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/** A refusal: a rule said no, and the caller learns which. */
|
|
32
|
+
export type CliRefusal = {
|
|
33
|
+
ok: false;
|
|
34
|
+
reason: string;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
};
|
|
37
|
+
/** Run the CLI: `argv` is everything after the program name. Resolves to the exit code. */
|
|
38
|
+
export declare function runCli(argv: string[], io: CliIo, deps?: CliDeps): Promise<number>;
|
|
39
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACnE,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AAQrD;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,KAAK,msCAYkF,CAAA;AAEpG,mEAAmE;AACnE,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/B;AAED,oFAAoF;AACpF,MAAM,WAAW,OAAO;IACtB,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,4FAA4F;IAC5F,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACxD;AAED,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAA;AAY9E,2FAA2F;AAC3F,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,GAAE,OAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B3F"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
import { nodeGitRunner } from '@openagt/agent-data';
|
|
3
|
+
import { nodeGhRunner } from './gh.js';
|
|
4
|
+
import { listRequests } from './requests.js';
|
|
5
|
+
import { openRequest } from './open.js';
|
|
6
|
+
import { mergeRequest } from './merge.js';
|
|
7
|
+
import { watchAndMerge } from './merge-watch.js';
|
|
8
|
+
import { GIT_HOST_NAME, homeUrlFor } from './home.js';
|
|
9
|
+
import { createRepository, offerRepository } from './create.js';
|
|
10
|
+
/**
|
|
11
|
+
* The command line over the package (#1820): the same functions for an agent in a shell, and for
|
|
12
|
+
* the framework and the scheduler, which run this command as the project's git host provider,
|
|
13
|
+
* declared in package.json as `"framework": { "git-host": "github" }`. One implementation, every
|
|
14
|
+
* surface a caller.
|
|
15
|
+
*
|
|
16
|
+
* The contract, the same as the other skills' commands: JSON on stdout, one line for a person on
|
|
17
|
+
* stderr, and the exit code says how it went — 0 for a result, 1 for a refusal or a failure, 2 for
|
|
18
|
+
* a command that could not be read. A refusal is reported on stdout as `{ ok: false, reason }` so
|
|
19
|
+
* a caller parsing the output learns why, and on stderr so a person does.
|
|
20
|
+
*
|
|
21
|
+
* Every command acts on the repository the working directory is in: `gh` finds the repository
|
|
22
|
+
* and its remote from there, as it does for a person.
|
|
23
|
+
*/
|
|
24
|
+
export const USAGE = `usage: github <command>
|
|
25
|
+
|
|
26
|
+
requests [--branch <b>] [--state open|merged|all] [--since <iso>]
|
|
27
|
+
the project's pull requests, newest first: number, url, state, title, draft, branch, head, createdAt, mergedAt, mergeCommit
|
|
28
|
+
open [--branch <b>] --title <t> [--body <text>] [--draft] [--merge]
|
|
29
|
+
open the pull request of branch <b> (default: the current branch; it must be on the remote already); an open one is answered as it is; --merge lands it on green
|
|
30
|
+
merge <number> land pull request <number>: a draft is marked ready, then the merge is armed as --merge arms it
|
|
31
|
+
watch <number> wait for pull request <number>'s checks and merge it once they pass; what --merge starts where the repository has no auto-merge
|
|
32
|
+
home the project's page on GitHub, from its origin remote
|
|
33
|
+
create [--check] create a private repository for a project with no origin, named <account>/<folder>, set it as origin and push;
|
|
34
|
+
--check only says which repository that would be
|
|
35
|
+
|
|
36
|
+
JSON on stdout. Exit code 1 for a refusal or a failure (the reason on stderr), 2 for a usage error.`;
|
|
37
|
+
/** Thrown inside a command to end it with a refusal. */
|
|
38
|
+
class Refused extends Error {
|
|
39
|
+
outcome;
|
|
40
|
+
line;
|
|
41
|
+
constructor(outcome, line) {
|
|
42
|
+
super(line);
|
|
43
|
+
this.outcome = outcome;
|
|
44
|
+
this.line = line;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Thrown inside a command for an argument that cannot be read: usage on stderr, exit 2. */
|
|
48
|
+
class Usage extends Error {
|
|
49
|
+
}
|
|
50
|
+
/** Run the CLI: `argv` is everything after the program name. Resolves to the exit code. */
|
|
51
|
+
export async function runCli(argv, io, deps = {}) {
|
|
52
|
+
const [command, ...rest] = argv;
|
|
53
|
+
const run = command && Object.hasOwn(COMMANDS, command) ? COMMANDS[command] : undefined;
|
|
54
|
+
if (!run) {
|
|
55
|
+
io.stderr(USAGE);
|
|
56
|
+
return 2;
|
|
57
|
+
}
|
|
58
|
+
const context = { cwd: io.cwd, gh: deps.gh ?? nodeGhRunner(), git: deps.git ?? nodeGitRunner(), ...(deps.watch ? { watch: deps.watch } : {}), stderr: io.stderr };
|
|
59
|
+
try {
|
|
60
|
+
io.stdout(JSON.stringify(await run(rest, context)));
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (err instanceof Usage) {
|
|
65
|
+
io.stderr(`${err.message}\n\n${USAGE}`);
|
|
66
|
+
return 2;
|
|
67
|
+
}
|
|
68
|
+
if (err instanceof Refused) {
|
|
69
|
+
io.stdout(JSON.stringify(err.outcome));
|
|
70
|
+
io.stderr(err.line);
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
74
|
+
io.stdout(JSON.stringify({ ok: false, reason: 'git-host-failed', detail }));
|
|
75
|
+
io.stderr(detail);
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const COMMANDS = {
|
|
80
|
+
async requests(args, { cwd, gh }) {
|
|
81
|
+
const { values } = parse(args, { branch: { type: 'string' }, state: { type: 'string' }, since: { type: 'string' } }, 0);
|
|
82
|
+
const state = values.state ?? 'all';
|
|
83
|
+
if (state !== 'open' && state !== 'merged' && state !== 'all')
|
|
84
|
+
throw new Usage(`--state is open, merged or all, not ${state}`);
|
|
85
|
+
try {
|
|
86
|
+
return await listRequests(cwd, { ...(values.branch ? { branch: values.branch } : {}), state, ...(values.since ? { since: values.since } : {}) }, gh);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
90
|
+
throw new Refused({ ok: false, reason: 'git-host-failed', detail }, `the pull requests could not be read: ${detail}`);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
async open(args, { cwd, gh, git, watch }) {
|
|
94
|
+
const { values } = parse(args, { branch: { type: 'string' }, title: { type: 'string' }, body: { type: 'string' }, merge: { type: 'boolean' }, draft: { type: 'boolean' } }, 0);
|
|
95
|
+
if (!values.title?.trim())
|
|
96
|
+
throw new Usage('--title is required: one line naming what the change does');
|
|
97
|
+
if (values.branch !== undefined && !values.branch.trim())
|
|
98
|
+
throw new Usage('--branch names a branch');
|
|
99
|
+
const outcome = await openRequest(cwd, {
|
|
100
|
+
...(values.branch ? { branch: values.branch } : {}),
|
|
101
|
+
title: values.title.trim(),
|
|
102
|
+
...(values.body !== undefined ? { body: values.body } : {}),
|
|
103
|
+
...(values.merge ? { merge: true } : {}),
|
|
104
|
+
...(values.draft ? { draft: true } : {}),
|
|
105
|
+
gh,
|
|
106
|
+
git,
|
|
107
|
+
...(watch ? { watch } : {}),
|
|
108
|
+
});
|
|
109
|
+
if (!outcome.ok)
|
|
110
|
+
throw new Refused(outcome, openRefusalLine(cwd, outcome));
|
|
111
|
+
return outcome;
|
|
112
|
+
},
|
|
113
|
+
async merge(args, { cwd, gh, watch }) {
|
|
114
|
+
const number = numberArg(parse(args, {}, 1).positionals[0]);
|
|
115
|
+
const outcome = await mergeRequest(cwd, number, { gh, ...(watch ? { watch } : {}) });
|
|
116
|
+
if (outcome.outcome === 'not-open')
|
|
117
|
+
throw new Refused({ ok: false, reason: 'not-open', number, state: outcome.state }, `pull request ${number} is ${outcome.state.toLowerCase()}, not open`);
|
|
118
|
+
if (outcome.outcome === 'failed')
|
|
119
|
+
throw new Refused({ ok: false, reason: 'merge-failed', number, detail: outcome.error }, `pull request ${number} could not be landed: ${outcome.error}`);
|
|
120
|
+
return { ok: true, number, outcome: outcome.outcome };
|
|
121
|
+
},
|
|
122
|
+
async watch(args, { cwd, gh, stderr }) {
|
|
123
|
+
const number = numberArg(parse(args, {}, 1).positionals[0]);
|
|
124
|
+
const outcome = await watchAndMerge(cwd, number, { gh, log: stderr });
|
|
125
|
+
return { ok: outcome.outcome === 'merged', number, ...outcome };
|
|
126
|
+
},
|
|
127
|
+
async create(args, { cwd, gh, git }) {
|
|
128
|
+
const { values } = parse(args, { check: { type: 'boolean' } }, 0);
|
|
129
|
+
const outcome = values.check ? await offerRepository(cwd, { gh, git }) : await createRepository(cwd, { gh, git });
|
|
130
|
+
if (!outcome.ok)
|
|
131
|
+
throw new Refused(outcome, createRefusalLine(outcome));
|
|
132
|
+
return { ...outcome, name: GIT_HOST_NAME };
|
|
133
|
+
},
|
|
134
|
+
async home(args, { cwd, git }) {
|
|
135
|
+
parse(args, {}, 0);
|
|
136
|
+
const url = await homeUrlFor(cwd, git);
|
|
137
|
+
if (!url)
|
|
138
|
+
throw new Refused({ ok: false, reason: 'no-remote' }, 'no origin remote on GitHub');
|
|
139
|
+
return { ok: true, url, name: GIT_HOST_NAME };
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
/** Why no repository was created, as one line for a person. */
|
|
143
|
+
function createRefusalLine(outcome) {
|
|
144
|
+
switch (outcome.reason) {
|
|
145
|
+
case 'has-remote':
|
|
146
|
+
return 'this project already has an origin remote';
|
|
147
|
+
case 'not-logged-in':
|
|
148
|
+
return `GitHub could not be reached as you: ${outcome.detail}`;
|
|
149
|
+
case 'create-failed':
|
|
150
|
+
return `the repository could not be created: ${outcome.detail}`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Why a request was not opened, as one line for a person. */
|
|
154
|
+
function openRefusalLine(cwd, outcome) {
|
|
155
|
+
switch (outcome.reason) {
|
|
156
|
+
case 'no-branch':
|
|
157
|
+
return `${cwd} is on no branch; name one with --branch`;
|
|
158
|
+
case 'open-failed':
|
|
159
|
+
return `the pull request for ${outcome.branch} could not be opened: ${outcome.detail}`;
|
|
160
|
+
case 'merge-failed':
|
|
161
|
+
return `pull request ${outcome.request.number} is open, but its merge could not be armed: ${outcome.detail}`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function numberArg(arg) {
|
|
165
|
+
const number = Number(arg);
|
|
166
|
+
if (!Number.isInteger(number) || number <= 0)
|
|
167
|
+
throw new Usage(`${arg} is not a pull request number`);
|
|
168
|
+
return number;
|
|
169
|
+
}
|
|
170
|
+
/** `parseArgs` with the positional count checked: too few or too many is a usage error. */
|
|
171
|
+
function parse(args, options, min, max = min) {
|
|
172
|
+
try {
|
|
173
|
+
const parsed = parseArgs({ args, options, allowPositionals: true, strict: true });
|
|
174
|
+
if (parsed.positionals.length < min || parsed.positionals.length > max)
|
|
175
|
+
throw new Usage(`expected ${max === min ? min : `${min} to ${max}`} argument(s), got ${parsed.positionals.length}`);
|
|
176
|
+
return parsed;
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
throw err instanceof Usage ? err : new Usage(err instanceof Error ? err.message : String(err));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
import { type GhRunner } from './gh.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creating the project's repository on GitHub: for a project that lives on one machine only, a
|
|
5
|
+
* private repository under the logged-in account, named after the project's folder, set as the
|
|
6
|
+
* project's `origin` and pushed to. One step for the person, through `gh`.
|
|
7
|
+
*
|
|
8
|
+
* Always private: making code public is a decision this never takes. Only for a project with no
|
|
9
|
+
* `origin` yet: a project that has a remote is already somewhere, and is not moved.
|
|
10
|
+
*/
|
|
11
|
+
/** What can be created, said before anything is: the account's repository the project would become. */
|
|
12
|
+
export type CreateOffer = {
|
|
13
|
+
ok: true;
|
|
14
|
+
repository: string;
|
|
15
|
+
} | {
|
|
16
|
+
ok: false;
|
|
17
|
+
reason: 'has-remote';
|
|
18
|
+
} | {
|
|
19
|
+
ok: false;
|
|
20
|
+
reason: 'not-logged-in';
|
|
21
|
+
detail: string;
|
|
22
|
+
};
|
|
23
|
+
export type CreateOutcome = {
|
|
24
|
+
ok: true;
|
|
25
|
+
repository: string;
|
|
26
|
+
url: string;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
reason: 'has-remote';
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
reason: 'not-logged-in' | 'create-failed';
|
|
33
|
+
detail: string;
|
|
34
|
+
};
|
|
35
|
+
/** The repository the project would become: `<the logged-in account>/<the project folder's name>`. */
|
|
36
|
+
export declare function offerRepository(cwd: string, deps: {
|
|
37
|
+
gh: GhRunner;
|
|
38
|
+
git?: GitRunner;
|
|
39
|
+
}): Promise<CreateOffer>;
|
|
40
|
+
/** Create the private repository, set it as `origin`, and push the branch the project's folder is on. */
|
|
41
|
+
export declare function createRepository(cwd: string, deps: {
|
|
42
|
+
gh: GhRunner;
|
|
43
|
+
git?: GitRunner;
|
|
44
|
+
}): Promise<CreateOutcome>;
|
|
45
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACnE,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AAErD;;;;;;;GAOG;AAEH,uGAAuG;AACvG,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACnC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1D,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACnC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAO5E,sGAAsG;AACtG,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAWhH;AAED,yGAAyG;AACzG,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CASnH"}
|
package/dist/create.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import { nodeGitRunner } from '@openagt/agent-data';
|
|
3
|
+
import { errorMessage } from './gh.js';
|
|
4
|
+
/** Whether the repository at `cwd` has an `origin` remote. */
|
|
5
|
+
async function hasOrigin(cwd, git) {
|
|
6
|
+
return git(['remote'], cwd).then(out => out.split('\n').some(line => line.trim() === 'origin'), () => false);
|
|
7
|
+
}
|
|
8
|
+
/** The repository the project would become: `<the logged-in account>/<the project folder's name>`. */
|
|
9
|
+
export async function offerRepository(cwd, deps) {
|
|
10
|
+
const git = deps.git ?? nodeGitRunner();
|
|
11
|
+
if (await hasOrigin(cwd, git))
|
|
12
|
+
return { ok: false, reason: 'has-remote' };
|
|
13
|
+
let login;
|
|
14
|
+
try {
|
|
15
|
+
login = (await deps.gh(['api', 'user', '--jq', '.login'], cwd)).trim();
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return { ok: false, reason: 'not-logged-in', detail: errorMessage(err) };
|
|
19
|
+
}
|
|
20
|
+
if (!login)
|
|
21
|
+
return { ok: false, reason: 'not-logged-in', detail: 'gh named no account' };
|
|
22
|
+
return { ok: true, repository: `${login}/${basename(cwd)}` };
|
|
23
|
+
}
|
|
24
|
+
/** Create the private repository, set it as `origin`, and push the branch the project's folder is on. */
|
|
25
|
+
export async function createRepository(cwd, deps) {
|
|
26
|
+
const offer = await offerRepository(cwd, deps);
|
|
27
|
+
if (!offer.ok)
|
|
28
|
+
return offer;
|
|
29
|
+
try {
|
|
30
|
+
await deps.gh(['repo', 'create', offer.repository, '--private', '--source', '.', '--remote', 'origin', '--push'], cwd);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
return { ok: false, reason: 'create-failed', detail: errorMessage(err) };
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, repository: offer.repository, url: `https://github.com/${offer.repository}` };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,aAAa,EAAkB,MAAM,qBAAqB,CAAA;AACnE,OAAO,EAAE,YAAY,EAAiB,MAAM,SAAS,CAAA;AAsBrD,8DAA8D;AAC9D,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,GAAc;IAClD,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAA;AAC9G,CAAC;AAED,sGAAsG;AACtG,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,IAAuC;IACxF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,aAAa,EAAE,CAAA;IACvC,IAAI,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAA;IACzE,IAAI,KAAa,CAAA;IACjB,IAAI,CAAC;QACH,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACxE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAA;IAC1E,CAAC;IACD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAA;IACxF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,KAAK,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,CAAA;AAC9D,CAAC;AAED,yGAAyG;AACzG,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAW,EAAE,IAAuC;IACzF,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAC9C,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,KAAK,CAAA;IAC3B,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAA;IACxH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAA;IAC1E,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,sBAAsB,KAAK,CAAC,UAAU,EAAE,EAAE,CAAA;AAClG,CAAC"}
|
package/dist/gh.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `gh` CLI, in the one package that runs it (#1820). Every call the package makes to GitHub
|
|
3
|
+
* goes through a runner of this shape, so a test scripts GitHub's answers and the code is the
|
|
4
|
+
* same either way.
|
|
5
|
+
*/
|
|
6
|
+
/** A `gh` runner: the standard output of one invocation; rejects with gh's own line on failure. */
|
|
7
|
+
export type GhRunner = (args: string[], cwd: string) => Promise<string>;
|
|
8
|
+
/** `gh` on PATH, one minute per call: these talk to the network. */
|
|
9
|
+
export declare function nodeGhRunner(): GhRunner;
|
|
10
|
+
export declare function errorMessage(err: unknown): string;
|
|
11
|
+
//# sourceMappingURL=gh.d.ts.map
|
package/dist/gh.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gh.d.ts","sourceRoot":"","sources":["../src/gh.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,mGAAmG;AACnG,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAEvE,oEAAoE;AACpE,wBAAgB,YAAY,IAAI,QAAQ,CAUvC;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEjD"}
|
package/dist/gh.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `gh` CLI, in the one package that runs it (#1820). Every call the package makes to GitHub
|
|
3
|
+
* goes through a runner of this shape, so a test scripts GitHub's answers and the code is the
|
|
4
|
+
* same either way.
|
|
5
|
+
*/
|
|
6
|
+
/** `gh` on PATH, one minute per call: these talk to the network. */
|
|
7
|
+
export function nodeGhRunner() {
|
|
8
|
+
return async (args, cwd) => {
|
|
9
|
+
const { execFile } = await import('node:child_process');
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
execFile('gh', args, { cwd, timeout: 60_000 }, (err, stdout, stderr) => {
|
|
12
|
+
if (err)
|
|
13
|
+
reject(new Error(String(stderr).trim() || err.message));
|
|
14
|
+
else
|
|
15
|
+
resolve(String(stdout));
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function errorMessage(err) {
|
|
21
|
+
return err instanceof Error ? err.message : String(err);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=gh.js.map
|
package/dist/gh.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gh.js","sourceRoot":"","sources":["../src/gh.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,oEAAoE;AACpE,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACzB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;QACvD,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC7C,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBACrE,IAAI,GAAG;oBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAA;;oBAC3D,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;YAC9B,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AACzD,CAAC"}
|
package/dist/home.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
/**
|
|
3
|
+
* The project's page on GitHub (#489, #1820): derived from the `origin` remote, so the dashboard's
|
|
4
|
+
* project bar can link straight to it. The framework asks this command and never spells the host.
|
|
5
|
+
*/
|
|
6
|
+
/** The git host's name, as the framework labels the link. */
|
|
7
|
+
export declare const GIT_HOST_NAME = "GitHub";
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a git remote URL to an `https://github.com/<owner>/<repo>` URL, or undefined when
|
|
10
|
+
* it is not a GitHub remote. Handles the scp-style (`git@github.com:o/r.git`), ssh
|
|
11
|
+
* (`ssh://git@github.com/o/r.git`), and https (`https://github.com/o/r.git`) forms, dropping
|
|
12
|
+
* a `.git` suffix, an embedded credential, and a trailing slash.
|
|
13
|
+
*/
|
|
14
|
+
export declare function homeUrlFromRemote(remote: string): string | undefined;
|
|
15
|
+
/** The repo's GitHub URL from its `origin` remote, or undefined (no remote / not GitHub). */
|
|
16
|
+
export declare function homeUrlFor(cwd: string, git?: GitRunner): Promise<string | undefined>;
|
|
17
|
+
//# sourceMappingURL=home.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"home.d.ts","sourceRoot":"","sources":["../src/home.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEnE;;;GAGG;AAEH,6DAA6D;AAC7D,eAAO,MAAM,aAAa,WAAW,CAAA;AAErC;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWpE;AAED,6FAA6F;AAC7F,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,SAA2B,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM3G"}
|
package/dist/home.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { nodeGitRunner } from '@openagt/agent-data';
|
|
2
|
+
/**
|
|
3
|
+
* The project's page on GitHub (#489, #1820): derived from the `origin` remote, so the dashboard's
|
|
4
|
+
* project bar can link straight to it. The framework asks this command and never spells the host.
|
|
5
|
+
*/
|
|
6
|
+
/** The git host's name, as the framework labels the link. */
|
|
7
|
+
export const GIT_HOST_NAME = 'GitHub';
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a git remote URL to an `https://github.com/<owner>/<repo>` URL, or undefined when
|
|
10
|
+
* it is not a GitHub remote. Handles the scp-style (`git@github.com:o/r.git`), ssh
|
|
11
|
+
* (`ssh://git@github.com/o/r.git`), and https (`https://github.com/o/r.git`) forms, dropping
|
|
12
|
+
* a `.git` suffix, an embedded credential, and a trailing slash.
|
|
13
|
+
*/
|
|
14
|
+
export function homeUrlFromRemote(remote) {
|
|
15
|
+
const url = remote.trim();
|
|
16
|
+
const match = url.match(/^git@github\.com:(.+?)$/) ||
|
|
17
|
+
url.match(/^ssh:\/\/git@github\.com\/(.+?)$/) ||
|
|
18
|
+
url.match(/^https?:\/\/(?:[^@/]+@)?github\.com\/(.+?)$/);
|
|
19
|
+
if (!match || !match[1])
|
|
20
|
+
return undefined;
|
|
21
|
+
const slug = match[1].replace(/\.git$/, '').replace(/\/$/, '');
|
|
22
|
+
// Guard against a junk capture: a GitHub slug is `owner/repo`, both non-empty.
|
|
23
|
+
if (!/^[^/]+\/[^/]+$/.test(slug))
|
|
24
|
+
return undefined;
|
|
25
|
+
return `https://github.com/${slug}`;
|
|
26
|
+
}
|
|
27
|
+
/** The repo's GitHub URL from its `origin` remote, or undefined (no remote / not GitHub). */
|
|
28
|
+
export async function homeUrlFor(cwd, git = nodeGitRunner()) {
|
|
29
|
+
try {
|
|
30
|
+
return homeUrlFromRemote(await git(['remote', 'get-url', 'origin'], cwd));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=home.js.map
|
package/dist/home.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"home.js","sourceRoot":"","sources":["../src/home.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkB,MAAM,qBAAqB,CAAA;AAEnE;;;GAGG;AAEH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAA;AAErC;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;IACzB,MAAM,KAAK,GACT,GAAG,CAAC,KAAK,CAAC,yBAAyB,CAAC;QACpC,GAAG,CAAC,KAAK,CAAC,kCAAkC,CAAC;QAC7C,GAAG,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAA;IAC1D,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAA;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC9D,+EAA+E;IAC/E,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IAClD,OAAO,sBAAsB,IAAI,EAAE,CAAA;AACrC,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,GAAG,GAAc,aAAa,EAAE;IAC5E,IAAI,CAAC;QACH,OAAO,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { nodeGhRunner, type GhRunner } from './gh.js';
|
|
2
|
+
export { listRequests, parseRequests, requestsArgs, sinceFilter, openRequestOf, REQUESTS_LIMIT, type Request, type RequestsQuery } from './requests.js';
|
|
3
|
+
export { openRequest, currentBranch, requestNumber, type OpenOptions, type OpenOutcome } from './open.js';
|
|
4
|
+
export { armMerge, mergeRequest, type MergeArming, type MergeOutcome, type MergeOptions, type WatchStarter } from './merge.js';
|
|
5
|
+
export { readPr, watchAndMerge, spawnMergeWatch, NO_CHECKS_GRACE_MS, WATCH_EVERY_MS, WATCH_FOR_MS, WATCH_LOG_DIR, type ChecksState, type WatchOptions, type WatchOutcome } from './merge-watch.js';
|
|
6
|
+
export { homeUrlFromRemote, homeUrlFor, GIT_HOST_NAME } from './home.js';
|
|
7
|
+
export { runCli, USAGE, type CliIo, type CliDeps, type CliRefusal } from './cli.js';
|
|
8
|
+
export { createRepository, offerRepository, type CreateOffer, type CreateOutcome } from './create.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AACvJ,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACzG,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9H,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAClM,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACxE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { nodeGhRunner } from './gh.js';
|
|
2
|
+
export { listRequests, parseRequests, requestsArgs, sinceFilter, openRequestOf, REQUESTS_LIMIT } from './requests.js';
|
|
3
|
+
export { openRequest, currentBranch, requestNumber } from './open.js';
|
|
4
|
+
export { armMerge, mergeRequest } from './merge.js';
|
|
5
|
+
export { readPr, watchAndMerge, spawnMergeWatch, NO_CHECKS_GRACE_MS, WATCH_EVERY_MS, WATCH_FOR_MS, WATCH_LOG_DIR } from './merge-watch.js';
|
|
6
|
+
export { homeUrlFromRemote, homeUrlFor, GIT_HOST_NAME } from './home.js';
|
|
7
|
+
export { runCli, USAGE } from './cli.js';
|
|
8
|
+
export { createRepository, offerRepository } from './create.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiB,MAAM,SAAS,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAoC,MAAM,eAAe,CAAA;AACvJ,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAsC,MAAM,WAAW,CAAA;AACzG,OAAO,EAAE,QAAQ,EAAE,YAAY,EAA6E,MAAM,YAAY,CAAA;AAC9H,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAA0D,MAAM,kBAAkB,CAAA;AAClM,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACxE,OAAO,EAAE,MAAM,EAAE,KAAK,EAA6C,MAAM,UAAU,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAwC,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { GhRunner } from './gh.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: `github watch <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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-watch.d.ts","sourceRoot":"","sources":["../src/merge-watch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC;;;;;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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
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 '@openagt/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(`[github] #${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/github', 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: `github watch <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, 'watch', 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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-watch.js","sourceRoot":"","sources":["../src/merge-watch.ts"],"names":[],"mappings":"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"}
|
package/dist/merge.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type GhRunner } from './gh.js';
|
|
2
|
+
/**
|
|
3
|
+
* Landing a pull request (#1820): armed to merge on green where the repository allows GitHub's
|
|
4
|
+
* auto-merge; merged at once where GitHub says the request is already green; where the repository
|
|
5
|
+
* allows no auto-merge, this package's own watcher waits for the checks and merges. Two doors on
|
|
6
|
+
* the same arming: `open --merge`, the agent's, and `merge <number>`, a person's or the scheduler's,
|
|
7
|
+
* which marks a draft ready first.
|
|
8
|
+
*/
|
|
9
|
+
/** How arming a merge went. */
|
|
10
|
+
export type MergeArming = {
|
|
11
|
+
outcome: 'auto-armed' | 'merged' | 'watching';
|
|
12
|
+
} | {
|
|
13
|
+
outcome: 'failed';
|
|
14
|
+
error: string;
|
|
15
|
+
};
|
|
16
|
+
/** Start the merge watcher for a request; the default starts this package's own process. For tests. */
|
|
17
|
+
export type WatchStarter = (repo: string, number: number) => Promise<void>;
|
|
18
|
+
export declare function armMerge(repo: string, number: number, gh: GhRunner, watch?: WatchStarter): Promise<MergeArming>;
|
|
19
|
+
/** What landing a request did: armed as `open --merge` arms, or why not. */
|
|
20
|
+
export type MergeOutcome = MergeArming | {
|
|
21
|
+
outcome: 'not-open';
|
|
22
|
+
state: string;
|
|
23
|
+
};
|
|
24
|
+
export interface MergeOptions {
|
|
25
|
+
gh?: GhRunner;
|
|
26
|
+
watch?: WatchStarter;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Land a pull request: "it is good, land it". A draft is marked ready first, since asking for the
|
|
30
|
+
* merge is the statement that its review happened; then the merge is armed exactly as
|
|
31
|
+
* `open --merge` arms it. A request that is not open is `not-open` with its state: already merged
|
|
32
|
+
* is an answer, not an action.
|
|
33
|
+
*/
|
|
34
|
+
export declare function mergeRequest(repo: string, number: number, opts?: MergeOptions): Promise<MergeOutcome>;
|
|
35
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGnE;;;;;;GAMG;AAEH,+BAA+B;AAC/B,MAAM,MAAM,WAAW,GAAG;IAAE,OAAO,EAAE,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAElH,uGAAuG;AACvG,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAU1E,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,GAAE,YAA8B,GAAG,OAAO,CAAC,WAAW,CAAC,CAoBtI;AAED,4EAA4E;AAC5E,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/E,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,KAAK,CAAC,EAAE,YAAY,CAAA;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAkB/G"}
|
package/dist/merge.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { errorMessage, nodeGhRunner } from './gh.js';
|
|
2
|
+
import { spawnMergeWatch } from './merge-watch.js';
|
|
3
|
+
/**
|
|
4
|
+
* GitHub's words for "auto-merge is not allowed in this repository" and for "this request is
|
|
5
|
+
* already green, so there is nothing to wait for". Matched loosely on purpose: a rephrase on
|
|
6
|
+
* GitHub's side lands in `failed`, said out loud, never in a wrong merge.
|
|
7
|
+
*/
|
|
8
|
+
const AUTO_MERGE_OFF = /auto[- ]?merge is not allowed/i;
|
|
9
|
+
const ALREADY_GREEN = /clean status/i;
|
|
10
|
+
export async function armMerge(repo, number, gh, watch = spawnMergeWatch) {
|
|
11
|
+
try {
|
|
12
|
+
await gh(['pr', 'merge', String(number), '--squash', '--auto'], repo);
|
|
13
|
+
return { outcome: 'auto-armed' };
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
const refusal = errorMessage(err);
|
|
17
|
+
try {
|
|
18
|
+
if (ALREADY_GREEN.test(refusal)) {
|
|
19
|
+
await gh(['pr', 'merge', String(number), '--squash'], repo);
|
|
20
|
+
return { outcome: 'merged' };
|
|
21
|
+
}
|
|
22
|
+
if (AUTO_MERGE_OFF.test(refusal)) {
|
|
23
|
+
await watch(repo, number);
|
|
24
|
+
return { outcome: 'watching' };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (next) {
|
|
28
|
+
return { outcome: 'failed', error: errorMessage(next) };
|
|
29
|
+
}
|
|
30
|
+
return { outcome: 'failed', error: refusal };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Land a pull request: "it is good, land it". A draft is marked ready first, since asking for the
|
|
35
|
+
* merge is the statement that its review happened; then the merge is armed exactly as
|
|
36
|
+
* `open --merge` arms it. A request that is not open is `not-open` with its state: already merged
|
|
37
|
+
* is an answer, not an action.
|
|
38
|
+
*/
|
|
39
|
+
export async function mergeRequest(repo, number, opts = {}) {
|
|
40
|
+
const gh = opts.gh ?? nodeGhRunner();
|
|
41
|
+
let view;
|
|
42
|
+
try {
|
|
43
|
+
view = JSON.parse(await gh(['pr', 'view', String(number), '--json', 'state,isDraft'], repo));
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return { outcome: 'failed', error: errorMessage(err) };
|
|
47
|
+
}
|
|
48
|
+
const state = typeof view.state === 'string' ? view.state : 'OPEN';
|
|
49
|
+
if (state !== 'OPEN')
|
|
50
|
+
return { outcome: 'not-open', state };
|
|
51
|
+
if (view.isDraft === true) {
|
|
52
|
+
try {
|
|
53
|
+
await gh(['pr', 'ready', String(number)], repo);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
return { outcome: 'failed', error: errorMessage(err) };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return armMerge(repo, number, gh, opts.watch ?? spawnMergeWatch);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
|
package/dist/open.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
import { type GhRunner } from './gh.js';
|
|
3
|
+
import { type MergeArming, type WatchStarter } from './merge.js';
|
|
4
|
+
/**
|
|
5
|
+
* Opening a pull request (#1820): the agent's own step after its branch is pushed, and the
|
|
6
|
+
* framework's on a person's Open PR. The words are the caller's; a branch that already has an
|
|
7
|
+
* open request gets no second one, and only the merge arming happens. Pushing is not here: the
|
|
8
|
+
* branches package pushes, this package asks GitHub.
|
|
9
|
+
*/
|
|
10
|
+
export interface OpenOptions {
|
|
11
|
+
/** The branch; the working directory's current branch when absent. */
|
|
12
|
+
branch?: string;
|
|
13
|
+
/** One line naming what the change does. */
|
|
14
|
+
title: string;
|
|
15
|
+
/** What changed, and why. */
|
|
16
|
+
body?: string;
|
|
17
|
+
/** Open as a draft. Not with `merge`: a draft cannot be merged. */
|
|
18
|
+
draft?: boolean;
|
|
19
|
+
/** Arm the merge: the request lands on its own once its checks pass. */
|
|
20
|
+
merge?: boolean;
|
|
21
|
+
git?: GitRunner;
|
|
22
|
+
gh?: GhRunner;
|
|
23
|
+
watch?: WatchStarter;
|
|
24
|
+
}
|
|
25
|
+
export type OpenOutcome = {
|
|
26
|
+
ok: true;
|
|
27
|
+
branch: string;
|
|
28
|
+
request: {
|
|
29
|
+
number: number;
|
|
30
|
+
url: string;
|
|
31
|
+
};
|
|
32
|
+
existing: boolean;
|
|
33
|
+
merge?: MergeArming;
|
|
34
|
+
}
|
|
35
|
+
/** The working directory is on no branch and none was named. */
|
|
36
|
+
| {
|
|
37
|
+
ok: false;
|
|
38
|
+
reason: 'no-branch';
|
|
39
|
+
}
|
|
40
|
+
/** GitHub would not open the request: gh's own line. */
|
|
41
|
+
| {
|
|
42
|
+
ok: false;
|
|
43
|
+
reason: 'open-failed';
|
|
44
|
+
branch: string;
|
|
45
|
+
detail: string;
|
|
46
|
+
}
|
|
47
|
+
/** The request is open, but its merge could not be armed. */
|
|
48
|
+
| {
|
|
49
|
+
ok: false;
|
|
50
|
+
reason: 'merge-failed';
|
|
51
|
+
branch: string;
|
|
52
|
+
request: {
|
|
53
|
+
number: number;
|
|
54
|
+
url: string;
|
|
55
|
+
};
|
|
56
|
+
detail: string;
|
|
57
|
+
};
|
|
58
|
+
/** The current branch of a directory, or undefined when detached or outside a repository. */
|
|
59
|
+
export declare function currentBranch(cwd: string, git: GitRunner): Promise<string | undefined>;
|
|
60
|
+
/** The number in a request's URL, as gh prints it after `pr create`. */
|
|
61
|
+
export declare function requestNumber(url: string | undefined): number | undefined;
|
|
62
|
+
export declare function openRequest(cwd: string, opts: OpenOptions): Promise<OpenOutcome>;
|
|
63
|
+
//# sourceMappingURL=open.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../src/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACnE,OAAO,EAA8B,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AACnE,OAAO,EAAY,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAG1E;;;;;GAKG;AAEH,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,KAAK,CAAC,EAAE,YAAY,CAAA;CACrB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE;AAChH,gEAAgE;GAC9D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE;AACpC,wDAAwD;GACtD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACtE,6DAA6D;GAC3D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnH,6FAA6F;AAC7F,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO5F;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGzE;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA0BtF"}
|
package/dist/open.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { nodeGitRunner } from '@openagt/agent-data';
|
|
2
|
+
import { errorMessage, nodeGhRunner } from './gh.js';
|
|
3
|
+
import { armMerge } from './merge.js';
|
|
4
|
+
import { openRequestOf } from './requests.js';
|
|
5
|
+
/** The current branch of a directory, or undefined when detached or outside a repository. */
|
|
6
|
+
export async function currentBranch(cwd, git) {
|
|
7
|
+
try {
|
|
8
|
+
const name = (await git(['rev-parse', '--abbrev-ref', 'HEAD'], cwd)).trim();
|
|
9
|
+
return name && name !== 'HEAD' ? name : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** The number in a request's URL, as gh prints it after `pr create`. */
|
|
16
|
+
export function requestNumber(url) {
|
|
17
|
+
const match = url?.match(/\/pull\/(\d+)(?:$|[/?#])/);
|
|
18
|
+
return match ? Number(match[1]) : undefined;
|
|
19
|
+
}
|
|
20
|
+
export async function openRequest(cwd, opts) {
|
|
21
|
+
const git = opts.git ?? nodeGitRunner();
|
|
22
|
+
const gh = opts.gh ?? nodeGhRunner();
|
|
23
|
+
const branch = opts.branch ?? (await currentBranch(cwd, git));
|
|
24
|
+
if (!branch)
|
|
25
|
+
return { ok: false, reason: 'no-branch' };
|
|
26
|
+
let request = await openRequestOf(cwd, branch, gh);
|
|
27
|
+
let existing = true;
|
|
28
|
+
if (!request) {
|
|
29
|
+
existing = false;
|
|
30
|
+
try {
|
|
31
|
+
const args = ['pr', 'create', '--head', branch, '--title', opts.title, '--body', opts.body ?? ''];
|
|
32
|
+
if (opts.draft && !opts.merge)
|
|
33
|
+
args.push('--draft');
|
|
34
|
+
const out = await gh(args, cwd);
|
|
35
|
+
const url = out.trim().split('\n').filter(Boolean).at(-1);
|
|
36
|
+
const number = requestNumber(url);
|
|
37
|
+
request = url && number !== undefined ? { number, url } : await openRequestOf(cwd, branch, gh);
|
|
38
|
+
if (!request)
|
|
39
|
+
return { ok: false, reason: 'open-failed', branch, detail: 'gh printed no pull request URL' };
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return { ok: false, reason: 'open-failed', branch, detail: errorMessage(err) };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (!opts.merge)
|
|
46
|
+
return { ok: true, branch, request, existing };
|
|
47
|
+
const merge = await armMerge(cwd, request.number, gh, opts.watch);
|
|
48
|
+
if (merge.outcome === 'failed')
|
|
49
|
+
return { ok: false, reason: 'merge-failed', branch, request, detail: merge.error };
|
|
50
|
+
return { ok: true, branch, request, existing, merge };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=open.js.map
|
package/dist/open.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.js","sourceRoot":"","sources":["../src/open.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type GhRunner } from './gh.js';
|
|
2
|
+
/**
|
|
3
|
+
* The project's pull requests, as one read (#1820): the ones of a branch, the open ones, the ones
|
|
4
|
+
* merged since a time. One shape for every caller, the framework's reads and the scheduler's
|
|
5
|
+
* record alike, so nothing outside this package knows how GitHub spells a state.
|
|
6
|
+
*/
|
|
7
|
+
/** One pull request, as the command prints it. */
|
|
8
|
+
export interface Request {
|
|
9
|
+
number: number;
|
|
10
|
+
url: string;
|
|
11
|
+
state: 'open' | 'merged' | 'closed';
|
|
12
|
+
title: string;
|
|
13
|
+
draft: boolean;
|
|
14
|
+
/** The head branch's name. */
|
|
15
|
+
branch: string;
|
|
16
|
+
/** The head commit's sha. */
|
|
17
|
+
head: string;
|
|
18
|
+
createdAt: string;
|
|
19
|
+
mergedAt?: string;
|
|
20
|
+
/** The commit a merged request landed as on the base branch. */
|
|
21
|
+
mergeCommit?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface RequestsQuery {
|
|
24
|
+
/** Only the requests whose head is this branch. */
|
|
25
|
+
branch?: string;
|
|
26
|
+
/** Default `all`. */
|
|
27
|
+
state?: 'open' | 'merged' | 'all';
|
|
28
|
+
/** Keep the requests created at or after this time; merged at or after it with `state: 'merged'`. */
|
|
29
|
+
since?: string;
|
|
30
|
+
}
|
|
31
|
+
/** The most requests one read answers. */
|
|
32
|
+
export declare const REQUESTS_LIMIT = 50;
|
|
33
|
+
/** The gh command line for a query: what the test checks, and what runs. */
|
|
34
|
+
export declare function requestsArgs(query: RequestsQuery): string[];
|
|
35
|
+
/** The rows gh printed, as requests; a row without a number and a url is no request. */
|
|
36
|
+
export declare function parseRequests(output: unknown): Request[];
|
|
37
|
+
/** The requests at or after `since`: by merge time when only merged ones were asked for, else by creation time. */
|
|
38
|
+
export declare function sinceFilter(requests: Request[], query: RequestsQuery): Request[];
|
|
39
|
+
/**
|
|
40
|
+
* The requests matching `query`, newest first as gh lists them. Throws when gh cannot answer
|
|
41
|
+
* (not installed, not logged in, no remote): "none" and "could not tell" must not look alike to a
|
|
42
|
+
* caller about to open a request, or keeping a baseline of what it announced.
|
|
43
|
+
*/
|
|
44
|
+
export declare function listRequests(repo: string, query: RequestsQuery, gh?: GhRunner): Promise<Request[]>;
|
|
45
|
+
/** The open request of a branch, or `undefined`; `undefined` too when gh cannot tell. */
|
|
46
|
+
export declare function openRequestOf(repo: string, branch: string, gh: GhRunner): Promise<{
|
|
47
|
+
number: number;
|
|
48
|
+
url: string;
|
|
49
|
+
} | undefined>;
|
|
50
|
+
//# sourceMappingURL=requests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requests.d.ts","sourceRoot":"","sources":["../src/requests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AAErD;;;;GAIG;AAEH,kDAAkD;AAClD,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;IACjC,qGAAqG;IACrG,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,0CAA0C;AAC1C,eAAO,MAAM,cAAc,KAAK,CAAA;AAIhC,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,EAAE,CAE3D;AAoBD,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,CAmBxD;AAED,mHAAmH;AACnH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,EAAE,CAIhF;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,GAAE,QAAyB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAExH;AAED,yFAAyF;AACzF,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,CAOpI"}
|
package/dist/requests.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { nodeGhRunner } from './gh.js';
|
|
2
|
+
/** The most requests one read answers. */
|
|
3
|
+
export const REQUESTS_LIMIT = 50;
|
|
4
|
+
const FIELDS = 'number,url,state,title,isDraft,headRefName,headRefOid,createdAt,mergedAt,mergeCommit';
|
|
5
|
+
/** The gh command line for a query: what the test checks, and what runs. */
|
|
6
|
+
export function requestsArgs(query) {
|
|
7
|
+
return ['pr', 'list', '--state', query.state ?? 'all', ...(query.branch ? ['--head', query.branch] : []), '--limit', String(REQUESTS_LIMIT), '--json', FIELDS];
|
|
8
|
+
}
|
|
9
|
+
/** gh's OPEN / MERGED / CLOSED, lowercased; anything else reads as closed. */
|
|
10
|
+
function stateOf(state) {
|
|
11
|
+
return state === 'OPEN' ? 'open' : state === 'MERGED' ? 'merged' : 'closed';
|
|
12
|
+
}
|
|
13
|
+
/** The rows gh printed, as requests; a row without a number and a url is no request. */
|
|
14
|
+
export function parseRequests(output) {
|
|
15
|
+
if (!Array.isArray(output))
|
|
16
|
+
return [];
|
|
17
|
+
const requests = [];
|
|
18
|
+
for (const row of output) {
|
|
19
|
+
if (!row || typeof row.number !== 'number' || typeof row.url !== 'string')
|
|
20
|
+
continue;
|
|
21
|
+
requests.push({
|
|
22
|
+
number: row.number,
|
|
23
|
+
url: row.url,
|
|
24
|
+
state: stateOf(row.state),
|
|
25
|
+
title: typeof row.title === 'string' ? row.title : '',
|
|
26
|
+
draft: row.isDraft === true,
|
|
27
|
+
branch: typeof row.headRefName === 'string' ? row.headRefName : '',
|
|
28
|
+
head: typeof row.headRefOid === 'string' ? row.headRefOid : '',
|
|
29
|
+
createdAt: typeof row.createdAt === 'string' ? row.createdAt : '',
|
|
30
|
+
...(typeof row.mergedAt === 'string' && row.mergedAt ? { mergedAt: row.mergedAt } : {}),
|
|
31
|
+
...(typeof row.mergeCommit?.oid === 'string' && row.mergeCommit.oid ? { mergeCommit: row.mergeCommit.oid } : {}),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return requests;
|
|
35
|
+
}
|
|
36
|
+
/** The requests at or after `since`: by merge time when only merged ones were asked for, else by creation time. */
|
|
37
|
+
export function sinceFilter(requests, query) {
|
|
38
|
+
if (!query.since)
|
|
39
|
+
return requests;
|
|
40
|
+
const since = query.since;
|
|
41
|
+
return requests.filter(r => (query.state === 'merged' ? (r.mergedAt ?? '') : r.createdAt) >= since);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The requests matching `query`, newest first as gh lists them. Throws when gh cannot answer
|
|
45
|
+
* (not installed, not logged in, no remote): "none" and "could not tell" must not look alike to a
|
|
46
|
+
* caller about to open a request, or keeping a baseline of what it announced.
|
|
47
|
+
*/
|
|
48
|
+
export async function listRequests(repo, query, gh = nodeGhRunner()) {
|
|
49
|
+
return sinceFilter(parseRequests(JSON.parse(await gh(requestsArgs(query), repo))), query);
|
|
50
|
+
}
|
|
51
|
+
/** The open request of a branch, or `undefined`; `undefined` too when gh cannot tell. */
|
|
52
|
+
export async function openRequestOf(repo, branch, gh) {
|
|
53
|
+
try {
|
|
54
|
+
const [first] = await listRequests(repo, { branch, state: 'open' }, gh);
|
|
55
|
+
return first ? { number: first.number, url: first.url } : undefined;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=requests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requests.js","sourceRoot":"","sources":["../src/requests.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openagt/skill-github",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "GitHub as a skill for coding agents: the project's pull requests and issues through gh, and the one command the framework and the scheduler run for the git host: requests, open, merge, home.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/framework/the-framework.git",
|
|
9
|
+
"directory": "packages/skill-github"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=22.12.0"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"bin/github",
|
|
21
|
+
"SKILL.md"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"github": "bin/github"
|
|
25
|
+
},
|
|
26
|
+
"framework": {
|
|
27
|
+
"git-host": "github",
|
|
28
|
+
"repository": "github"
|
|
29
|
+
},
|
|
30
|
+
"exports": {
|
|
31
|
+
"./package.json": "./package.json",
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./SKILL.md": "./SKILL.md"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@openagt/agent-data": "0.0.1"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^20.0.0",
|
|
43
|
+
"typescript": "^7.0.2"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -p tsconfig.build.json",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"test": "tsc -p tsconfig.test.json && node --test --test-timeout=60000 'dist-test/**/*.test.js'",
|
|
49
|
+
"clean": "rm -rf dist dist-test"
|
|
50
|
+
}
|
|
51
|
+
}
|