vouchington-tooling 0.46.0 → 0.47.0
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.
|
@@ -12,9 +12,9 @@ export type PullRequestProvenance = {
|
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
14
|
* The pull request body: the Summary, Impact, Related issues and Details sections followed by the
|
|
15
|
-
* provenance lines. With `noSourceIssue` the Related issues section
|
|
16
|
-
* (default {@link defaultNoSourceStatement}) instead of `- None`. Pure;
|
|
17
|
-
* device and worktree facts.
|
|
15
|
+
* provenance lines. With `noSourceIssue` the Related issues section ends with `noSourceStatement`
|
|
16
|
+
* (default {@link defaultNoSourceStatement}), after any `Refs` lines, instead of `- None`. Pure;
|
|
17
|
+
* the caller supplies the device and worktree facts.
|
|
18
18
|
*/
|
|
19
19
|
export declare function renderPullRequestBody(input: PullRequestInput, provenance: PullRequestProvenance, noSourceStatement?: string): string;
|
|
20
20
|
/**
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { basename } from 'node:path';
|
|
2
|
+
import { formatReferenceKey } from "../gh-cli/closing-refs.mjs";
|
|
2
3
|
function relatedLine(issue) {
|
|
3
4
|
const verb = issue.relation === 'closes' ? 'Closes' : 'Refs';
|
|
4
|
-
|
|
5
|
+
const [owner, repo] = issue.repository?.split('/') ?? [];
|
|
6
|
+
return `- ${verb} ${formatReferenceKey({ number: issue.number, owner, repo })} — ${issue.reason}`;
|
|
5
7
|
}
|
|
6
8
|
/** The default no-source statement; a policy `renderNoSourceIssue` export may replace it. */
|
|
7
9
|
function defaultNoSourceStatement(reason) {
|
|
@@ -9,18 +11,16 @@ function defaultNoSourceStatement(reason) {
|
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* The pull request body: the Summary, Impact, Related issues and Details sections followed by the
|
|
12
|
-
* provenance lines. With `noSourceIssue` the Related issues section
|
|
13
|
-
* (default {@link defaultNoSourceStatement}) instead of `- None`. Pure;
|
|
14
|
-
* device and worktree facts.
|
|
14
|
+
* provenance lines. With `noSourceIssue` the Related issues section ends with `noSourceStatement`
|
|
15
|
+
* (default {@link defaultNoSourceStatement}), after any `Refs` lines, instead of `- None`. Pure;
|
|
16
|
+
* the caller supplies the device and worktree facts.
|
|
15
17
|
*/
|
|
16
18
|
export function renderPullRequestBody(input, provenance, noSourceStatement) {
|
|
17
|
-
const related = input.relatedIssues.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
: (noSourceStatement ?? defaultNoSourceStatement(input.noSourceIssue.reason)),
|
|
23
|
-
];
|
|
19
|
+
const related = input.relatedIssues.map(relatedLine);
|
|
20
|
+
if (input.noSourceIssue !== undefined)
|
|
21
|
+
related.push(noSourceStatement ?? defaultNoSourceStatement(input.noSourceIssue.reason));
|
|
22
|
+
if (!related.length)
|
|
23
|
+
related.push('- None');
|
|
24
24
|
return [
|
|
25
25
|
'## Summary',
|
|
26
26
|
'',
|
|
@@ -4,13 +4,15 @@ export type RelatedIssue = {
|
|
|
4
4
|
number: number;
|
|
5
5
|
relation: 'closes' | 'refs';
|
|
6
6
|
reason: string;
|
|
7
|
+
/** `owner/name` of a cross-repository issue (rendered lowercase); absent for this repository. */
|
|
8
|
+
repository?: string;
|
|
7
9
|
};
|
|
8
10
|
export type PullRequestInput = {
|
|
9
11
|
title: string;
|
|
10
12
|
summary: string;
|
|
11
13
|
impact: string;
|
|
12
14
|
relatedIssues: RelatedIssue[];
|
|
13
|
-
/**
|
|
15
|
+
/** Declares no source issue; relatedIssues beside it must all be `refs`. */
|
|
14
16
|
noSourceIssue?: {
|
|
15
17
|
reason: string;
|
|
16
18
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hasClosingIssueReference } from "../gh-cli/closing-refs.mjs";
|
|
1
2
|
import { optionalString, rejectUnknown, requireObject, requiredChoice, requiredString, requiredStringArray, } from "./args.mjs";
|
|
2
3
|
import { trustedMode } from "./pr-mode.mjs";
|
|
3
4
|
const PR_MODES = ['interactive', 'autonomous'];
|
|
@@ -9,17 +10,30 @@ export function positiveInteger(value, label) {
|
|
|
9
10
|
throw new Error(`${label} must be a positive integer`);
|
|
10
11
|
return value;
|
|
11
12
|
}
|
|
13
|
+
// The owner is a GitHub login: alphanumerics and single hyphens, at most 39 characters.
|
|
14
|
+
const REPOSITORY_RE = /^(?=[^/]{1,39}\/)[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*\/[\w.-]+$/;
|
|
15
|
+
function readRepository(issue, label) {
|
|
16
|
+
if (issue.repository === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
// Not lowercased here: redaction runs on this value first, and rendering lowercases it.
|
|
19
|
+
const value = singleLine(issue, 'repository');
|
|
20
|
+
if (!REPOSITORY_RE.test(value))
|
|
21
|
+
throw new Error(`${label} must be a single owner/name`);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
12
24
|
function readRelatedIssues(args) {
|
|
13
25
|
const value = args.relatedIssues;
|
|
14
26
|
if (!Array.isArray(value))
|
|
15
27
|
throw new Error('relatedIssues is required and must be an array');
|
|
16
28
|
return value.map((item, index) => {
|
|
17
29
|
const issue = requireObject(item);
|
|
18
|
-
rejectUnknown(issue, ['number', 'relation', 'reason']);
|
|
30
|
+
rejectUnknown(issue, ['number', 'relation', 'reason', 'repository']);
|
|
31
|
+
const repository = readRepository(issue, `relatedIssues[${index}].repository`);
|
|
19
32
|
return {
|
|
20
33
|
number: positiveInteger(issue.number, `relatedIssues[${index}].number`),
|
|
21
34
|
relation: requiredChoice(issue, 'relation', RELATIONS),
|
|
22
35
|
reason: requiredString(issue, 'reason'),
|
|
36
|
+
...(repository === undefined ? {} : { repository }),
|
|
23
37
|
};
|
|
24
38
|
});
|
|
25
39
|
}
|
|
@@ -28,8 +42,12 @@ function readNoSourceIssue(args, related) {
|
|
|
28
42
|
return undefined;
|
|
29
43
|
const value = requireObject(args.noSourceIssue);
|
|
30
44
|
rejectUnknown(value, ['reason']);
|
|
31
|
-
if (related.
|
|
32
|
-
throw new Error('noSourceIssue
|
|
45
|
+
if (related.some((issue) => issue.relation === 'closes'))
|
|
46
|
+
throw new Error('noSourceIssue cannot be combined with a relatedIssues entry that closes');
|
|
47
|
+
related.forEach((issue, index) => {
|
|
48
|
+
if (hasClosingIssueReference(issue.reason))
|
|
49
|
+
throw new Error(`relatedIssues[${index}].reason must not contain a closing reference beside noSourceIssue`);
|
|
50
|
+
});
|
|
33
51
|
return { reason: singleLine(value, 'reason') };
|
|
34
52
|
}
|
|
35
53
|
function readDetails(args) {
|
|
@@ -12,14 +12,20 @@ const FIELDS = {
|
|
|
12
12
|
impact: text('Who or what is affected, as markdown.'),
|
|
13
13
|
relatedIssues: {
|
|
14
14
|
type: 'array',
|
|
15
|
-
description: 'Issues this pull request closes or refs. Empty when it has none; with no source issue ' +
|
|
16
|
-
'
|
|
15
|
+
description: 'Issues this pull request closes or refs. Empty when it has none; with no source issue, ' +
|
|
16
|
+
'give noSourceIssue as well (beside a non-empty list every entry must be refs).',
|
|
17
17
|
items: {
|
|
18
18
|
type: 'object',
|
|
19
19
|
properties: {
|
|
20
20
|
number: { type: 'integer', minimum: 1 },
|
|
21
21
|
relation: { enum: ['closes', 'refs'] },
|
|
22
22
|
reason: text('Why the issue is related.'),
|
|
23
|
+
repository: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
pattern: '^(?=[^/]{1,39}/)[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*/[\\w.-]+$',
|
|
26
|
+
description: 'Optional owner/name (lowercased) of the issue repository, rendered as ' +
|
|
27
|
+
'owner/name#N; omit for an issue in this repository. Allowed for closes and refs.',
|
|
28
|
+
},
|
|
23
29
|
},
|
|
24
30
|
required: ['number', 'relation', 'reason'],
|
|
25
31
|
additionalProperties: false,
|
|
@@ -27,9 +33,10 @@ const FIELDS = {
|
|
|
27
33
|
},
|
|
28
34
|
noSourceIssue: {
|
|
29
35
|
type: 'object',
|
|
30
|
-
description: 'Declares a pull request with no source issue
|
|
31
|
-
'
|
|
32
|
-
'passed to the policy, which may allow it
|
|
36
|
+
description: 'Declares a pull request with no source issue; rejected beside a closes relatedIssues ' +
|
|
37
|
+
'entry, allowed beside refs-only ones. Rendered inside Related issues after any Refs ' +
|
|
38
|
+
'lines (the policy may supply the wording) and passed to the policy, which may allow it ' +
|
|
39
|
+
'only in interactive mode.',
|
|
33
40
|
properties: { reason: text('Single-line reason there is no source issue.') },
|
|
34
41
|
required: ['reason'],
|
|
35
42
|
additionalProperties: false,
|
package/docs/mcp-server.md
CHANGED
|
@@ -286,8 +286,10 @@ shape is an error, never a pass.
|
|
|
286
286
|
retrospective tools. All three take `sessionId` (required) and `worktree` (optional), need a server
|
|
287
287
|
launched inside a repository worktree (they run the checkout policy), and share this structured
|
|
288
288
|
input: `title` (single line, at most 256 characters), `summary`, `impact`, `relatedIssues`
|
|
289
|
-
(`[{ number, relation: "closes" | "refs", reason }]`, may be empty
|
|
290
|
-
|
|
289
|
+
(`[{ number, relation: "closes" | "refs", reason, repository? }]`, may be empty; the optional
|
|
290
|
+
`repository` is a single `owner/name` and renders the line as lowercase `owner/name#N`), optional
|
|
291
|
+
`noSourceIssue` (`{ reason }`, see below), `details` (string array), `base` (default `main`),
|
|
292
|
+
optional `agent`, optional
|
|
291
293
|
`model`, optional `mode` (`interactive` | `autonomous`) and `workspaceSetup`. `pr_update` also
|
|
292
294
|
requires `pr`; `pr_validate` accepts an optional `pr`.
|
|
293
295
|
|
|
@@ -319,9 +321,11 @@ which may keep sending it.
|
|
|
319
321
|
|
|
320
322
|
### Pull requests with no source issue
|
|
321
323
|
|
|
322
|
-
Pass `noSourceIssue: { reason }` (a single line) with an empty `relatedIssues
|
|
323
|
-
`relatedIssues`
|
|
324
|
-
`- No source issue — <reason>` instead of `- None
|
|
324
|
+
Pass `noSourceIssue: { reason }` (a single line) with an empty `relatedIssues`, or with a
|
|
325
|
+
`relatedIssues` whose every entry is `refs`; a `closes` entry beside it is rejected. The Related
|
|
326
|
+
issues section then holds the generic statement `- No source issue — <reason>` instead of `- None`,
|
|
327
|
+
preceded by the `- Refs … — <reason>` lines when there are any (the statement is always the last
|
|
328
|
+
line). A policy may replace the statement by
|
|
325
329
|
exporting `renderNoSourceIssue({ reason })`, returning a non-empty string (30 s). The field also
|
|
326
330
|
reaches `validatePullRequest` (with the trusted `mode`, so the policy can apply an interactive-only
|
|
327
331
|
rule) and `pullRequestDefaults`.
|
|
@@ -339,8 +343,9 @@ rule) and `pullRequestDefaults`.
|
|
|
339
343
|
|
|
340
344
|
## Related issues
|
|
341
345
|
|
|
342
|
-
- Closes #N — <reason> (or "Refs #N"; "- None" when empty;
|
|
343
|
-
"- No source issue — <reason>" for noSourceIssue
|
|
346
|
+
- Closes #N — <reason> (or "Refs #N"; owner/name#N with `repository`; "- None" when empty;
|
|
347
|
+
"- No source issue — <reason>" for noSourceIssue, rendered
|
|
348
|
+
after any Refs lines)
|
|
344
349
|
|
|
345
350
|
## Details
|
|
346
351
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vouchington-tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.0",
|
|
4
4
|
"description": "Vouchington CLI and extractable tooling libraries.",
|
|
5
5
|
"homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
|
|
6
6
|
"bugs": {
|