@a2f0/agent-tool 0.1.48 → 0.1.50
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/package.json +1 -1
- package/src/cli.ts +3 -4
- package/src/permissions/catalog.ts +0 -1
- package/src/pr/squashMerge.ts +1 -1
- package/src/runAgentToolAction.ts +8 -14
- package/src/workflows/pr.ts +18 -2
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -16,7 +16,6 @@ import { bumpVersions, checkVersions, planVersions } from "./version/bumpVersion
|
|
|
16
16
|
import { prepareVersions } from "./version/prepareVersions";
|
|
17
17
|
import { resolveVersionConflicts } from "./version/resolveVersionConflicts";
|
|
18
18
|
import { openPr } from "./pr/openPr";
|
|
19
|
-
import { squashMerge } from "./pr/squashMerge";
|
|
20
19
|
import metadata from "../package.json";
|
|
21
20
|
import { runCommand } from "./commands/run";
|
|
22
21
|
import { inspectLog } from "./commands/output";
|
|
@@ -25,7 +24,7 @@ import { branchCommand } from "./workflows/branch";
|
|
|
25
24
|
import { commitsCommand, binariesCommand, gateCommand } from "./workflows/checks";
|
|
26
25
|
import { hooksCommand } from "./workflows/hooks";
|
|
27
26
|
import { repoCommand, policyCommand, toolchainCommand } from "./workflows/repository";
|
|
28
|
-
import { prWorkflowCommand } from "./workflows/pr";
|
|
27
|
+
import { mergeReviewedHead, prWorkflowCommand } from "./workflows/pr";
|
|
29
28
|
import { releaseCommand } from "./release/commands";
|
|
30
29
|
import { checkoutRoot } from "./workflows/core";
|
|
31
30
|
import { procCommand, teardownCommand } from "./processes/commands";
|
|
@@ -89,7 +88,7 @@ Usage: agent-tool [--repo <directory>] [--config <file>] <command>
|
|
|
89
88
|
--help | --version
|
|
90
89
|
|
|
91
90
|
Legacy actions remain available: solicitClaudeCodeReview, solicitCodexReview,
|
|
92
|
-
solicitOpencodeReview, openPr,
|
|
91
|
+
solicitOpencodeReview, openPr, bumpVersions, checkVersions,
|
|
93
92
|
resolveVersionConflicts. A successful review exit means a usable review was
|
|
94
93
|
returned; inspect VERDICT before shipping. Skill install previews unless --apply.
|
|
95
94
|
`;
|
|
@@ -251,7 +250,7 @@ export function main(argv = process.argv.slice(2)): number | Promise<number> {
|
|
|
251
250
|
}
|
|
252
251
|
usage(action === "merge" && args.length === 3, "Usage: agent-tool pr merge <subject-or-empty> <reviewed-head-oid> <base-branch>");
|
|
253
252
|
usage(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/i.test(args[1]!), "Merge requires a full reviewed HEAD OID.");
|
|
254
|
-
return
|
|
253
|
+
return mergeReviewedHead(root, args[0], args[1], args[2]);
|
|
255
254
|
}
|
|
256
255
|
if (command === "versions") {
|
|
257
256
|
const action = args.shift();
|
|
@@ -37,7 +37,6 @@ export const CATALOG: CatalogEntry[] = [
|
|
|
37
37
|
...["checkVersions"].map(command => ({ command, class: "read" as const })),
|
|
38
38
|
...["bumpVersions", "resolveVersionConflicts", "solicitClaudeCodeReview", "solicitCodexReview", "solicitOpencodeReview"].map(command => ({ command, class: "local" as const })),
|
|
39
39
|
{ command: "openPr", class: "share" },
|
|
40
|
-
{ command: "squashMerge", class: "release" },
|
|
41
40
|
];
|
|
42
41
|
// `run`, `run-group` and `proc start` execute arbitrary commands, so they stay unclassified and keep the harness default.
|
|
43
42
|
// `bun x` runs only executables that `permissions.executables` classifies; project.ts adds those.
|
package/src/pr/squashMerge.ts
CHANGED
|
@@ -97,7 +97,7 @@ export function parsePullRequestMergeTarget(
|
|
|
97
97
|
recordField(pullRequest, "autoMergeRequest") !== null
|
|
98
98
|
) {
|
|
99
99
|
throw new Error(
|
|
100
|
-
"Pull request already has a queued or automatic merge; cancel it before running
|
|
100
|
+
"Pull request already has a queued or automatic merge; cancel it before running pr merge.",
|
|
101
101
|
);
|
|
102
102
|
}
|
|
103
103
|
if (expectedBaseRef !== undefined && baseRefName !== expectedBaseRef) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { openPr } from "./pr/openPr";
|
|
2
|
-
import { squashMerge } from "./pr/squashMerge";
|
|
3
2
|
import { solicitClaudeCodeReview } from "./review/solicitClaudeCodeReview";
|
|
4
3
|
import { solicitCodexReview } from "./review/solicitCodexReview";
|
|
5
4
|
import { solicitOpencodeReview } from "./review/solicitOpencodeReview";
|
|
@@ -7,7 +6,7 @@ import { bumpVersions, checkVersions } from "./version/bumpVersions";
|
|
|
7
6
|
import { resolveVersionConflicts } from "./version/resolveVersionConflicts";
|
|
8
7
|
|
|
9
8
|
const AGENT_TOOL_USAGE =
|
|
10
|
-
"Usage: agent-tool <solicitClaudeCodeReview|solicitCodexReview|solicitOpencodeReview|openPr|
|
|
9
|
+
"Usage: agent-tool <solicitClaudeCodeReview|solicitCodexReview|solicitOpencodeReview|openPr|bumpVersions|checkVersions|resolveVersionConflicts> [args]\n";
|
|
11
10
|
|
|
12
11
|
export interface AgentToolActions {
|
|
13
12
|
readonly bumpVersions: (rootDir: string, baseOid?: string) => number;
|
|
@@ -20,12 +19,6 @@ export interface AgentToolActions {
|
|
|
20
19
|
) => number | Promise<number>;
|
|
21
20
|
readonly solicitCodexReview: (rootDir: string, effort?: string) => number | Promise<number>;
|
|
22
21
|
readonly solicitOpencodeReview: (rootDir: string, effort?: string) => number | Promise<number>;
|
|
23
|
-
readonly squashMerge: (
|
|
24
|
-
rootDir: string,
|
|
25
|
-
subject?: string,
|
|
26
|
-
expectedHeadSha?: string,
|
|
27
|
-
expectedBaseRef?: string,
|
|
28
|
-
) => number;
|
|
29
22
|
}
|
|
30
23
|
|
|
31
24
|
const defaultActions: AgentToolActions = {
|
|
@@ -36,7 +29,6 @@ const defaultActions: AgentToolActions = {
|
|
|
36
29
|
solicitClaudeCodeReview,
|
|
37
30
|
solicitCodexReview,
|
|
38
31
|
solicitOpencodeReview,
|
|
39
|
-
squashMerge,
|
|
40
32
|
};
|
|
41
33
|
|
|
42
34
|
function assertMaximumPositionals(
|
|
@@ -58,7 +50,7 @@ export function runAgentToolAction(
|
|
|
58
50
|
actions: AgentToolActions = defaultActions,
|
|
59
51
|
): number | Promise<number> {
|
|
60
52
|
const [action, ...positionals] = args;
|
|
61
|
-
const [first
|
|
53
|
+
const [first] = positionals;
|
|
62
54
|
switch (action) {
|
|
63
55
|
case "bumpVersions": {
|
|
64
56
|
assertMaximumPositionals(action, positionals, 1);
|
|
@@ -88,10 +80,12 @@ export function runAgentToolAction(
|
|
|
88
80
|
assertMaximumPositionals(action, positionals, 1);
|
|
89
81
|
return actions.openPr(rootDir, first);
|
|
90
82
|
}
|
|
91
|
-
case "squashMerge":
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
83
|
+
case "squashMerge":
|
|
84
|
+
// Removed: its optional reviewed head let a merge skip preflight.
|
|
85
|
+
process.stderr.write(
|
|
86
|
+
"squashMerge was removed; use agent-tool pr merge <subject-or-empty> <reviewed-head-oid> <base-branch>.\n",
|
|
87
|
+
);
|
|
88
|
+
return 1;
|
|
95
89
|
default:
|
|
96
90
|
process.stderr.write(`Unknown action: ${action ?? "(none)"}\n`);
|
|
97
91
|
process.stderr.write(AGENT_TOOL_USAGE);
|
package/src/workflows/pr.ts
CHANGED
|
@@ -4,11 +4,11 @@ import { closeSync, readFileSync } from "node:fs";
|
|
|
4
4
|
import { createLog } from "../commands/logFile";
|
|
5
5
|
import { DEFAULT_LIMITS, logTail, outputLimit } from "../commands/output";
|
|
6
6
|
import { loadConfig } from "../config";
|
|
7
|
-
import { remoteBranchHead, resolvePr, resolveRepositoryGitUrl } from "../git/prContext";
|
|
7
|
+
import { remoteBranchHead, resolveFreshBaseRef, resolvePr, resolveRepositoryGitUrl } from "../git/prContext";
|
|
8
8
|
import { assertMergeChecks } from "../pr/mergeChecks";
|
|
9
9
|
import { assertStrictBaseFreshness } from "../pr/baseFreshness";
|
|
10
10
|
import { baseFreshness, behindReason, cancelRuns } from "./prFreshness";
|
|
11
|
-
import { resolvePullRequestMergeTarget } from "../pr/squashMerge";
|
|
11
|
+
import { resolvePullRequestMergeTarget, squashMerge } from "../pr/squashMerge";
|
|
12
12
|
import { planVersions } from "../version/bumpVersions";
|
|
13
13
|
import { assert, clean, currentBranch, git, head, json, oid, option, flag, output, poll, timeoutOptions } from "./core";
|
|
14
14
|
|
|
@@ -86,6 +86,22 @@ export function preflight(expectedHead: string, baseBranch: string, baseOid: str
|
|
|
86
86
|
return { ok: true, repo: identity.repo, pr: pr.number, url: pr.url, headOid: expectedHead, baseBranch, baseOid, checks: checks.checks };
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Merge a reviewed HEAD only after every preflight gate passes against the
|
|
91
|
+
* freshly fetched live base: a skipped, failed, or stale preflight run never
|
|
92
|
+
* leads to a merge. The reviewed HEAD and base branch are required, so no
|
|
93
|
+
* merge path skips preflight.
|
|
94
|
+
*/
|
|
95
|
+
export function mergeReviewedHead(rootDir: string, subject: string | undefined, expectedHead: string, baseBranch: string): number {
|
|
96
|
+
assert(!!expectedHead && !!baseBranch, "Merge requires a reviewed HEAD and a base branch.");
|
|
97
|
+
const identity = resolvePr();
|
|
98
|
+
const baseOid = resolveFreshBaseRef(identity.repo, baseBranch);
|
|
99
|
+
try { preflight(expectedHead, baseBranch, baseOid); }
|
|
100
|
+
catch (error) { throw new Error(`Refusing to merge: preflight failed against live ${baseBranch} ${baseOid}: ${(error as Error).message}`); }
|
|
101
|
+
// Bind the merge to the branch that passed preflight, so a retarget in between is refused.
|
|
102
|
+
return squashMerge(rootDir, subject, expectedHead, baseBranch);
|
|
103
|
+
}
|
|
104
|
+
|
|
89
105
|
interface Comment { id: string; databaseId: number; body: string; url: string; author: { login: string } | null }
|
|
90
106
|
interface Page<T> { nodes: T[]; pageInfo: { hasNextPage: boolean; endCursor: string | null } }
|
|
91
107
|
interface Thread { id: string; isResolved: boolean; isOutdated: boolean; path: string; line: number | null; comments: Page<Comment> }
|