@a2f0/agent-tool 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +288 -0
- package/package.json +36 -0
- package/skills/cross-agent-review/SKILL.md +46 -0
- package/skills/open-pr/SKILL.md +31 -0
- package/skills/reset/SKILL.md +25 -0
- package/skills/ship-pr/SKILL.md +48 -0
- package/skills/squash-merge/SKILL.md +39 -0
- package/src/cli.ts +121 -0
- package/src/config.ts +101 -0
- package/src/doctor.ts +23 -0
- package/src/git/currentBranchPr.ts +114 -0
- package/src/git/environment.ts +10 -0
- package/src/git/prContext.ts +497 -0
- package/src/harnesses.ts +53 -0
- package/src/index.ts +9 -0
- package/src/pr/assertBranchPushed.ts +21 -0
- package/src/pr/assertNoClaudeBranding.ts +52 -0
- package/src/pr/baseFreshness.ts +67 -0
- package/src/pr/mergeChecks.ts +90 -0
- package/src/pr/openPr.ts +80 -0
- package/src/pr/prNumberSuffix.ts +43 -0
- package/src/pr/squashMerge.ts +229 -0
- package/src/pr/subjectLine.ts +21 -0
- package/src/pr/validateCommitSubject.ts +12 -0
- package/src/review/destinationPaths.ts +154 -0
- package/src/review/executable.ts +34 -0
- package/src/review/pinnedReviewInput.ts +91 -0
- package/src/review/rawGitTree.ts +301 -0
- package/src/review/reviewEffort.ts +54 -0
- package/src/review/reviewOutput.ts +62 -0
- package/src/review/reviewPrompt.ts +104 -0
- package/src/review/reviewSnapshot.ts +67 -0
- package/src/review/runReview.ts +62 -0
- package/src/review/solicitClaudeCodeReview.ts +140 -0
- package/src/review/solicitCodexReview.ts +205 -0
- package/src/review/solicitOpencodeReview.ts +233 -0
- package/src/runAgentToolAction.ts +100 -0
- package/src/skills/bundled.ts +8 -0
- package/src/skills/install.ts +92 -0
- package/src/version/bumpVersions.ts +196 -0
- package/src/version/lockfileVersions.ts +43 -0
- package/src/version/packageVersion.ts +112 -0
- package/src/version/prepareVersions.ts +135 -0
- package/src/version/resolveVersionConflicts.ts +126 -0
- package/src/version/versionPreparationGit.ts +127 -0
package/README.md
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# agent-tool
|
|
2
|
+
|
|
3
|
+
A standalone CLI for independent agent reviews, GitHub PR operations, package
|
|
4
|
+
version helpers, and portable skills for Claude Code, Codex, and OpenCode.
|
|
5
|
+
Extracted from the agent tooling in the projects under `~/github`.
|
|
6
|
+
|
|
7
|
+
The first implementation retains TypeScript and the existing regression suite.
|
|
8
|
+
Bun compiles the code and skill text into one executable, so installed users do
|
|
9
|
+
not need Node, Bun, TypeScript, or repository-local npm dependencies. The current
|
|
10
|
+
macOS ARM64 executable is approximately 58 MiB because it includes its runtime.
|
|
11
|
+
Rust remains a reasonable later implementation choice; it is not required for
|
|
12
|
+
Brew or Debian packaging. See [the design](docs/design.md) and
|
|
13
|
+
[extraction record](docs/extraction.md).
|
|
14
|
+
|
|
15
|
+
## Try it
|
|
16
|
+
|
|
17
|
+
Development and CI use Bun 1.4.2. The source CLI runs on Bun 1.3.11 or newer,
|
|
18
|
+
but `versions prepare` needs Bun 1.4 or newer on PATH to refresh workspace
|
|
19
|
+
versions in `bun.lock`. Runtime requires Git; GitHub commands also require
|
|
20
|
+
authenticated `gh`. Review requires an authenticated CLI for the chosen agent.
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
bun install --frozen-lockfile
|
|
24
|
+
bun run build
|
|
25
|
+
./dist/agent-tool --help
|
|
26
|
+
./dist/agent-tool doctor
|
|
27
|
+
|
|
28
|
+
# Point the installed tool at an existing project:
|
|
29
|
+
./dist/agent-tool --repo ../some-project review codex
|
|
30
|
+
# Review against an explicit local base without GitHub:
|
|
31
|
+
./dist/agent-tool --repo ../some-project review opencode high --base main
|
|
32
|
+
|
|
33
|
+
# Preview shared skills, then install them when ready:
|
|
34
|
+
./dist/agent-tool --repo ../some-project skills install
|
|
35
|
+
./dist/agent-tool --repo ../some-project skills install --apply
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The same binary works when copied outside this repository. No sibling project
|
|
39
|
+
has to retain `packages/agent-tool` once its callers are migrated.
|
|
40
|
+
|
|
41
|
+
### Use as a package dependency
|
|
42
|
+
|
|
43
|
+
Projects can install the source CLI from npm as `@a2f0/agent-tool`:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
bun add --dev --exact @a2f0/agent-tool
|
|
47
|
+
# or
|
|
48
|
+
npm install --save-dev --save-exact @a2f0/agent-tool
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Run `bun run agent-tool --help` after adding a script such as
|
|
52
|
+
`"agent-tool": "agent-tool"`. The package exposes
|
|
53
|
+
`node_modules/.bin/agent-tool`; invoke it directly for commands containing an
|
|
54
|
+
empty positional argument, such as
|
|
55
|
+
`node_modules/.bin/agent-tool pr merge '' "$REVIEWED_SHA" "$REVIEW_BASE_REF"`.
|
|
56
|
+
The source package includes the CLI and embedded skills and needs no dependency
|
|
57
|
+
install scripts or compile step. The executable is TypeScript run by Bun, so Bun
|
|
58
|
+
must be on `PATH` wherever it runs, including npm projects and CI; without it,
|
|
59
|
+
the shell reports `env: bun: No such file or directory`. The standalone binary
|
|
60
|
+
described above remains available for use without Bun.
|
|
61
|
+
|
|
62
|
+
To use an unreleased commit, pin a full commit SHA from GitHub instead:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@a2f0/agent-tool": "github:a2f0/agent-tool#<full-commit-sha>"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
npm records a GitHub dependency in `package-lock.json` as a `git+ssh` URL, but
|
|
73
|
+
`npm ci` installs this public repository without SSH credentials.
|
|
74
|
+
|
|
75
|
+
## Commands
|
|
76
|
+
|
|
77
|
+
| Command | Behavior |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `review claude\|codex\|opencode [effort] [--base ref]` | Review committed HEAD using a pinned raw-file snapshot |
|
|
80
|
+
| `pr open [title]` | Open a same-repository PR; body from stdin; branch already pushed |
|
|
81
|
+
| `pr merge <subject-or-empty> <head-oid> <base-branch>` | Check CI and synchronously squash the reviewed HEAD |
|
|
82
|
+
| `versions plan\|bump\|check <base-oid>` | Plan, rewrite, or check package versions against an exact base |
|
|
83
|
+
| `versions prepare <base-oid>` | Bump, refresh `bun.lock`, validate, and commit versions; print a JSON receipt |
|
|
84
|
+
| `versions resolve-conflicts` | Resolve only version-field conflicts in configured manifests |
|
|
85
|
+
| `skills list` | List embedded portable skills |
|
|
86
|
+
| `skills install [--harness all\|claude\|codex\|opencode] [--apply]` | Preview by default; install or update managed skills |
|
|
87
|
+
| `skills check [--harness all\|claude\|codex\|opencode]` | Read-only CI check for current managed skills |
|
|
88
|
+
| `init` / `config show` | Create or inspect data-only project policy |
|
|
89
|
+
| `doctor` | Inspect local versions and required CLI flags without running a model |
|
|
90
|
+
|
|
91
|
+
Effort accepts `low`, `medium`, `high`, `xhigh`, and `max`. Defaults are `xhigh`
|
|
92
|
+
for Claude and `high` for Codex and OpenCode. Older camelCase action names remain
|
|
93
|
+
available; run `--help` for the compatibility list. The modern merge command
|
|
94
|
+
requires reviewed HEAD and base branch; legacy `squashMerge` also retains its
|
|
95
|
+
manual, unguarded form.
|
|
96
|
+
|
|
97
|
+
A successful review exit means a complete review was returned, including a
|
|
98
|
+
`VERDICT:` line. BLOCKER or MAJOR findings still require action. The review CLI
|
|
99
|
+
does not repair, push, or merge; the coordinating skills orchestrate that work.
|
|
100
|
+
|
|
101
|
+
## Project configuration
|
|
102
|
+
|
|
103
|
+
Run `agent-tool init` in a project to create `agent-tool.json`. Existing policy
|
|
104
|
+
files are never overwritten. Missing settings use defaults; misspelled keys,
|
|
105
|
+
invalid types, and unknown schema versions fail before work begins. Global
|
|
106
|
+
`--config <file>` or `AGENT_TOOL_CONFIG` selects an explicit policy file, useful
|
|
107
|
+
when reviewing a feature checkout with policy supplied from a trusted source.
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"schemaVersion": 1,
|
|
112
|
+
"subject": { "conventional": true, "maxLength": 50 },
|
|
113
|
+
"merge": {
|
|
114
|
+
"requiredChecks": [{ "name": "CI gate", "workflow": "CI" }],
|
|
115
|
+
"requireStrictBaseFreshness": true
|
|
116
|
+
},
|
|
117
|
+
"review": {
|
|
118
|
+
"claudeModel": null,
|
|
119
|
+
"codexModel": null,
|
|
120
|
+
"opencodeModel": "deepseek/deepseek-v4-pro",
|
|
121
|
+
"opencodeVariants": { "xhigh": "max" },
|
|
122
|
+
"timeoutMs": 600000
|
|
123
|
+
},
|
|
124
|
+
"versions": {
|
|
125
|
+
"packages": ["packages/api", "packages/client"],
|
|
126
|
+
"lockfile": "bun.lock",
|
|
127
|
+
"validate": [["bun", "run", "lint", "--", "--staged"]],
|
|
128
|
+
"commitMessage": "chore: bump package versions"
|
|
129
|
+
},
|
|
130
|
+
"pr": { "rejectClaudeBranding": true }
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The default title policy uses conventional commits and a 72-character limit.
|
|
135
|
+
No commitlint binary or JavaScript config is executed. Required CI names are
|
|
136
|
+
project settings; default policy requires reported checks, rejects unsuccessful
|
|
137
|
+
checks, and allows intentionally skipped optional jobs. A named required check
|
|
138
|
+
must succeed. Configure explicit required names for shipping.
|
|
139
|
+
|
|
140
|
+
The OpenCode model default preserves the source projects' fallback provider; set
|
|
141
|
+
it to a model your account supports. `opencodeVariants` overrides mappings for
|
|
142
|
+
the supplied levels, with unspecified entries passed through unchanged. Claude
|
|
143
|
+
and Codex use their CLI defaults unless a model is configured.
|
|
144
|
+
|
|
145
|
+
Version helpers discover committed `package.json` workspaces when
|
|
146
|
+
`versions.packages` is null, or use the configured relative package directories;
|
|
147
|
+
`.` names the repository's root package, which then changes with any file,
|
|
148
|
+
including files in other configured packages.
|
|
149
|
+
They preserve deliberate major/minor releases and bump changed packages one
|
|
150
|
+
patch past the base. `plan`, `bump`, and `check` do not regenerate lockfiles or
|
|
151
|
+
commit changes.
|
|
152
|
+
|
|
153
|
+
`versions prepare` owns the whole sequence before a review snapshot. It needs a
|
|
154
|
+
clean worktree on an attached branch with the exact base already merged. It
|
|
155
|
+
rewrites versions and, when `versions.lockfile` is `"bun.lock"` (the default),
|
|
156
|
+
runs `bun install --lockfile-only --ignore-scripts`. It then verifies that
|
|
157
|
+
`bun.lock` records every workspace version, because Bun before 1.4 exits
|
|
158
|
+
successfully without refreshing them. Each `versions.validate` command runs
|
|
159
|
+
without a shell against the staged result; commands resolve on PATH outside
|
|
160
|
+
the repository. The commit uses `versions.commitMessage` and runs the project's
|
|
161
|
+
commit hooks, and must reproduce the prepared tree. Set `versions.lockfile` to
|
|
162
|
+
null for projects without a Bun lockfile. On success it prints a JSON receipt
|
|
163
|
+
with `baseOid`, `startHead`, `headOid`, `committed`, `lockfileChanged`, and the
|
|
164
|
+
rewritten `versions`. A failure before the commit restores the manifests,
|
|
165
|
+
lockfile, and index unless HEAD or other paths changed; otherwise it preserves
|
|
166
|
+
that state for inspection. Repeating against the same base creates no commit.
|
|
167
|
+
|
|
168
|
+
## Portable skills
|
|
169
|
+
|
|
170
|
+
Canonical instructions live once in `skills/`. The build embeds them, and the
|
|
171
|
+
installer places the same bytes where each harness discovers them:
|
|
172
|
+
|
|
173
|
+
| Target | Project discovery directory |
|
|
174
|
+
| --- | --- |
|
|
175
|
+
| Claude Code | `.claude/skills` |
|
|
176
|
+
| Codex | `.agents/skills` |
|
|
177
|
+
| OpenCode alone | `.opencode/skills` |
|
|
178
|
+
| All three | `.agents/skills` and `.claude/skills`; OpenCode discovers both |
|
|
179
|
+
|
|
180
|
+
Discovery paths follow the official [Codex](https://learn.chatgpt.com/docs/build-skills#where-codex-loads-local-skills),
|
|
181
|
+
[Claude Code](https://code.claude.com/docs/en/skills), and
|
|
182
|
+
[OpenCode](https://opencode.ai/docs/skills/) documentation. Restart the harness
|
|
183
|
+
if new skills do not appear. OpenCode may see identical names through both
|
|
184
|
+
compatibility paths; the installed instructions are identical.
|
|
185
|
+
|
|
186
|
+
The bundle provides `cross-agent-review`, `open-pr`, `squash-merge`, `ship-pr`, and
|
|
187
|
+
`reset`. The installer tracks ownership with `.agent-tool-skills.json`, previews
|
|
188
|
+
changes without writing, and rejects unmanaged or locally edited files and
|
|
189
|
+
symlink destinations. Preserve existing project-specific skills before
|
|
190
|
+
migration rather than overwriting them. Project validation and setup commands
|
|
191
|
+
stay in repository guidance; the shared skills make no package-manager or
|
|
192
|
+
application-layout assumptions. The installer is project-scoped; it does not
|
|
193
|
+
modify global harness settings, MCP configuration, or authentication.
|
|
194
|
+
|
|
195
|
+
To normalize consumers, install the bundled skills and keep project validation,
|
|
196
|
+
review-bot rules, and deployment checks in `AGENTS.md`. Put title and required CI
|
|
197
|
+
settings in `agent-tool.json` instead of copying the shipping workflow. After a
|
|
198
|
+
dependency update, run `agent-tool skills install --apply` and commit the updated
|
|
199
|
+
skills with `.agent-tool-skills.json`. Run `agent-tool skills check` in hooks and
|
|
200
|
+
CI: it fails for missing, outdated, unmanaged, or locally edited skills without
|
|
201
|
+
writing anything. Use the same `--harness` when installing and checking a single
|
|
202
|
+
harness. Existing unmanaged skills must be preserved or deliberately migrated
|
|
203
|
+
before installation; there is no force-overwrite mode.
|
|
204
|
+
|
|
205
|
+
## Review guarantees and limits
|
|
206
|
+
|
|
207
|
+
The extraction retains exact base/head snapshots, base-commit review policy,
|
|
208
|
+
raw Git blob materialization, collision/path validation, text diffs without
|
|
209
|
+
external drivers, snapshot cleanup, verdict gating, and one retry for incomplete
|
|
210
|
+
successful output. Git hooks and replace objects are disabled for helper Git
|
|
211
|
+
operations. Reviewer executables must resolve outside the project, and reviews
|
|
212
|
+
have configurable timeouts. Modern review rejects HEAD drift before returning.
|
|
213
|
+
|
|
214
|
+
Claude uses safe mode and read-only tools. Codex uses an ephemeral session,
|
|
215
|
+
snapshot-scoped filesystem permissions, no model-command environment
|
|
216
|
+
inheritance, disabled hosted web search, and final-message capture. OpenCode uses
|
|
217
|
+
a neutral directory, pure mode, inline permissions denying every tool by default,
|
|
218
|
+
and specific read-only allowances. Unknown/unsupported CLI flags fail the run;
|
|
219
|
+
there is no fallback that silently removes those protections.
|
|
220
|
+
|
|
221
|
+
These are different harness protections, not a uniform OS isolation guarantee.
|
|
222
|
+
Claude and OpenCode rely on their tool permission implementations. The launcher
|
|
223
|
+
still uses local authentication and a host environment; the stronger
|
|
224
|
+
credential-stripped macOS preflight from `stealth` has not been ported. A verdict
|
|
225
|
+
is a completion marker, not proof that a model's findings are correct.
|
|
226
|
+
|
|
227
|
+
GitHub atomically enforces expected HEAD during merging, but this mutation has
|
|
228
|
+
no atomic expected-base precondition. Skills recheck base freshness; repository
|
|
229
|
+
protection must enforce stricter concurrent-update requirements. With
|
|
230
|
+
`merge.requireStrictBaseFreshness`, `pr merge` refuses unless the base's
|
|
231
|
+
effective rules include an active repository ruleset requiring strict status
|
|
232
|
+
checks that the authenticated actor can never bypass. GitHub then rejects a
|
|
233
|
+
head that no longer contains the latest base. Classic branch protection is not
|
|
234
|
+
inspected. Fork reviews
|
|
235
|
+
can resolve upstream PR bases; the PR-opening helper initially supports
|
|
236
|
+
same-repository branches.
|
|
237
|
+
|
|
238
|
+
## Packages and verification
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
bun run typecheck
|
|
242
|
+
bun test
|
|
243
|
+
bun scripts/smoke-package.ts
|
|
244
|
+
bun run build
|
|
245
|
+
bun scripts/smoke.ts dist/agent-tool
|
|
246
|
+
|
|
247
|
+
# Build macOS/Linux ARM64/x64 archives and a formula with real SHA-256 values:
|
|
248
|
+
bun scripts/release.ts a2f0/agent-tool
|
|
249
|
+
|
|
250
|
+
# On Linux with dpkg-deb; substitute your release version:
|
|
251
|
+
scripts/package-deb.sh dist/agent-tool-0.1.0-linux-x64/bin/agent-tool amd64
|
|
252
|
+
# Install a produced Debian package:
|
|
253
|
+
sudo apt install ./dist/agent-tool_0.1.0_amd64.deb
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Release scripts create local artifacts; they do not publish anything. Put the
|
|
257
|
+
archives at the generated release URLs and the generated `agent-tool.rb` in a
|
|
258
|
+
Homebrew tap to support `brew install <owner>/<tap>/agent-tool`. A downloadable
|
|
259
|
+
`.deb` supports `apt install ./file.deb`; `apt install agent-tool` by name also
|
|
260
|
+
requires hosting a signed APT repository and configuring that source. Neither a
|
|
261
|
+
tap nor an APT repository is live yet. The GitHub release workflow builds and
|
|
262
|
+
uploads CI artifacts only.
|
|
263
|
+
|
|
264
|
+
### npm releases
|
|
265
|
+
|
|
266
|
+
ship-pr bumps the root `package.json` patch version on every merge, and the
|
|
267
|
+
[publish workflow](.github/workflows/npm-publish.yml) publishes each version
|
|
268
|
+
newer than npm's `latest` with
|
|
269
|
+
[trusted publishing](https://docs.npmjs.com/trusted-publishers) and provenance.
|
|
270
|
+
No npm token is stored. The job runs in the `npm` environment, which only
|
|
271
|
+
`main` can deploy to, and npm's trusted publisher names that environment and
|
|
272
|
+
`npm-publish.yml`. A deliberate major or minor bump in a PR is kept. Runs never
|
|
273
|
+
overlap, and when merges land together only the newest pending run starts, so
|
|
274
|
+
intermediate versions can be skipped on npm.
|
|
275
|
+
|
|
276
|
+
npm adds a trusted publisher only to a package that already exists, so the
|
|
277
|
+
first version was published by hand with `npm publish --ignore-scripts`. Until
|
|
278
|
+
a package exists, the workflow fails with that instruction rather than
|
|
279
|
+
attempting a publish npm would reject.
|
|
280
|
+
|
|
281
|
+
Standalone builds use [Bun's executable support](https://bun.sh/docs/bundler/executables).
|
|
282
|
+
The compiled smoke test invokes all three adapters through local stubs with Bun
|
|
283
|
+
and Node absent from PATH; it makes no paid model calls. CI checks source tests,
|
|
284
|
+
type checking, compiled smoke tests, and Debian packaging on Linux, plus source
|
|
285
|
+
and compiled checks on macOS.
|
|
286
|
+
|
|
287
|
+
See [the validation record](docs/validation.md) for observed results and the
|
|
288
|
+
limits of the local tests.
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@a2f0/agent-tool",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Portable cross-agent review, PR workflows, and shared skills",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/a2f0/agent-tool.git"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"agent-tool": "src/index.ts"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"src",
|
|
18
|
+
"skills",
|
|
19
|
+
"!src/**/*.test.ts",
|
|
20
|
+
"!src/**/*.testUtils.ts"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"bun": ">=1.3.11"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"start": "bun src/index.ts",
|
|
27
|
+
"test": "bun test src scripts",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"generate": "bun scripts/bundle-skills.ts",
|
|
30
|
+
"build": "bun run generate && bun build --compile --minify src/index.ts --outfile dist/agent-tool"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/bun": "1.3.11",
|
|
34
|
+
"typescript": "5.9.3"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cross-agent-review
|
|
3
|
+
description: Review committed branch changes with an independent Claude Code, Codex, or OpenCode reviewer, and repair actionable findings when requested.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cross-Agent Review
|
|
7
|
+
|
|
8
|
+
Use the installed `agent-tool` executable. Read the repository's development and
|
|
9
|
+
review policy and `agent-tool.json` when present. Keep project validation commands
|
|
10
|
+
and release rules in the repository; do not assume Bun, a workspace layout, or
|
|
11
|
+
particular CI job names.
|
|
12
|
+
|
|
13
|
+
Select a reviewer different from the coordinating agent when possible. Honor a
|
|
14
|
+
requested reviewer or effort; otherwise use an available independent reviewer.
|
|
15
|
+
Run `agent-tool doctor` if CLI compatibility is uncertain.
|
|
16
|
+
|
|
17
|
+
Review committed changes with `agent-tool review claude`, `agent-tool review codex`,
|
|
18
|
+
or `agent-tool review opencode`. Optional effort is `low`, `medium`, `high`,
|
|
19
|
+
`xhigh`, or `max`. For a local or non-GitHub repository, add `--base <commit-or-ref>`.
|
|
20
|
+
For a coordinated GitHub review, pin the fetched base using
|
|
21
|
+
`AGENT_TOOL_REVIEW_BASE_REF` and `AGENT_TOOL_REVIEW_BASE_OID`; fetch from the
|
|
22
|
+
repository that owns the PR, which can differ from the checkout's origin.
|
|
23
|
+
|
|
24
|
+
Record the exact base and HEAD before reviewing. The tool reviews raw committed
|
|
25
|
+
files and excludes worktree edits. A zero exit means a complete review was
|
|
26
|
+
returned; it does not mean the findings are non-blocking. Read the final
|
|
27
|
+
`VERDICT: BLOCKER|MAJOR|MINOR|SUGGESTION|CLEAN` and the findings. A reviewer
|
|
28
|
+
that labels findings `[P0]` through `[P3]` uses the same scale: `[P0]` and
|
|
29
|
+
`[P1]` are BLOCKER and MAJOR, `[P2]` and `[P3]` are MINOR and SUGGESTION.
|
|
30
|
+
|
|
31
|
+
If a CLI is unavailable, out of credits, or returns an unusable review, try another
|
|
32
|
+
available reviewer and disclose the fallback. Do not silently convert a failed
|
|
33
|
+
review into CLEAN or call an in-session review independent.
|
|
34
|
+
|
|
35
|
+
For report-only requests, report findings without changing files or history.
|
|
36
|
+
When repairs are authorized, address actionable BLOCKER and MAJOR findings,
|
|
37
|
+
validate with the repository's relevant checks, commit the repairs, and review
|
|
38
|
+
the new HEAD. Continue while meaningful progress is possible; report an impasse
|
|
39
|
+
when progress requires a material user decision. A repaired commit is unreviewed
|
|
40
|
+
until the next review completes. Preserve unrelated work.
|
|
41
|
+
|
|
42
|
+
Before reporting success, confirm HEAD still matches the reviewed commit and
|
|
43
|
+
that an existing PR points to that same commit. If the base moved, refresh it and
|
|
44
|
+
review again before shipping. Report reviewer, exact base and head IDs, verdict,
|
|
45
|
+
remaining findings, validation, and any repairs performed. This skill does not
|
|
46
|
+
authorize pushing, opening a PR, or merging unless the user requested that work.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: open-pr
|
|
3
|
+
description: Open a GitHub pull request for the current feature branch with a validated title and a reviewable description.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Open PR
|
|
7
|
+
|
|
8
|
+
Use the installed `agent-tool`. Follow repository policy for validation, commits,
|
|
9
|
+
and branch naming. Resolve the repository and default branch with Git and `gh`;
|
|
10
|
+
do not assume the branch is named main. Preserve unrelated work and finish
|
|
11
|
+
requested changes and appropriate checks before publishing them.
|
|
12
|
+
|
|
13
|
+
Ensure the intended commit is on a feature branch and push it through the
|
|
14
|
+
repository's normal push workflow. Respect existing push authorization; do not
|
|
15
|
+
bypass hooks. If the user only requested a draft description, prepare that text
|
|
16
|
+
without publishing a PR.
|
|
17
|
+
|
|
18
|
+
Write a title that satisfies the data-only `agent-tool.json` subject policy.
|
|
19
|
+
Describe the concrete resulting behavior and relevant validation. Put multiline
|
|
20
|
+
body text in a temporary file, then run:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
agent-tool pr open 'feat: describe the change' < /path/to/pr-body.txt
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Omitting the title uses the latest commit subject. The tool refuses duplicate
|
|
27
|
+
open PRs and requires the branch to be pushed at its local HEAD. It does not
|
|
28
|
+
push for you. Resolve a fork's head/base identities explicitly if the repository
|
|
29
|
+
uses fork PRs; the initial open helper supports same-repository branches.
|
|
30
|
+
|
|
31
|
+
Report the created PR link and any remaining validation or review work.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reset
|
|
3
|
+
description: Return a clean checkout to its updated default branch after confirmed shipping or an explicit checkout cleanup request.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Reset
|
|
7
|
+
|
|
8
|
+
This is checkout cleanup, not a destructive Git reset. Read repository guidance
|
|
9
|
+
for setup and hooks. Determine the default branch through the repository's
|
|
10
|
+
remote metadata rather than assuming main.
|
|
11
|
+
|
|
12
|
+
Check worktree and index first. Preserve user edits; if they prevent switching,
|
|
13
|
+
report the condition instead of discarding or automatically stashing them. For
|
|
14
|
+
post-merge cleanup, confirm the PR is MERGED before removing a feature branch.
|
|
15
|
+
|
|
16
|
+
Fetch the default branch, switch to it, and update by fast-forward only. A
|
|
17
|
+
diverged default branch requires inspection rather than forced replacement.
|
|
18
|
+
Delete a merged feature branch only within the requested cleanup scope and
|
|
19
|
+
after verifying its exact commit is the shipped commit; squash merging does not
|
|
20
|
+
make Git ancestry-based branch deletion proof of shipping. Honor keep-branch.
|
|
21
|
+
|
|
22
|
+
Run only the hook installer or dependency setup documented by this repository
|
|
23
|
+
when the updated checkout requires it. Do not assume a package manager or copy
|
|
24
|
+
setup commands from another project. Report final branch, commit, and any
|
|
25
|
+
unfinished cleanup.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ship-pr
|
|
3
|
+
description: Complete an authorized commit, independent review, repair, PR, CI, and squash-merge workflow for a GitHub repository.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ship PR
|
|
7
|
+
|
|
8
|
+
Use the installed `agent-tool`. Read repository guidance and policy first;
|
|
9
|
+
validation, versioning, package management, and hook installation remain project
|
|
10
|
+
concerns. Shipping authorizes this workflow within the user's stated scope.
|
|
11
|
+
Preserve unrelated edits and honor requested stopping points such as open-PR
|
|
12
|
+
only, report-only review, or keeping the feature branch.
|
|
13
|
+
|
|
14
|
+
1. Finish the requested change on a feature branch. Validate it with appropriate
|
|
15
|
+
project checks and commit only the intended files.
|
|
16
|
+
2. Determine the current PR's base repository and branch, or the repository's
|
|
17
|
+
default branch when no PR exists. Fetch and pin its exact base. Integrate it
|
|
18
|
+
using the repository's normal workflow, resolving conflicts without losing
|
|
19
|
+
user work. If project policy versions packages, prepare versions against the
|
|
20
|
+
pinned base after every integration or repair and before each review, using
|
|
21
|
+
the project's commands or `agent-tool versions prepare <base-oid>`, which
|
|
22
|
+
commits bumped manifests and the refreshed lockfile.
|
|
23
|
+
`agent-tool versions resolve-conflicts` resolves conflicts confined to
|
|
24
|
+
version fields. Review the resulting committed HEAD with an independent
|
|
25
|
+
reviewer using the bundled cross-agent-review skill. Repair blocking findings
|
|
26
|
+
and review every changed HEAD again. Review failure blocks shipping.
|
|
27
|
+
3. Push the reviewed HEAD through normal hooks, then verify the pushed SHA still
|
|
28
|
+
equals the reviewed SHA. If a hook changed content or HEAD, review that result.
|
|
29
|
+
Open the PR using the open-pr skill, or update the existing PR as needed.
|
|
30
|
+
4. Allow any configured review bots time to respond, then fetch unresolved PR
|
|
31
|
+
review threads. Follow repository policy for feedback: fix valid findings,
|
|
32
|
+
reply in the original threads, and resolve only fully addressed comments.
|
|
33
|
+
Validate, commit, push, and independently review every feedback or CI repair.
|
|
34
|
+
Wait for CI, recheck the live base, PR base branch, and local and remote heads.
|
|
35
|
+
A changed base requires integration, validation, and another review. When
|
|
36
|
+
versions are managed, `agent-tool versions check <base-oid>` must pass for the
|
|
37
|
+
live base. Use the squash-merge skill to merge the exact reviewed HEAD.
|
|
38
|
+
5. After confirmed MERGED, honor keep-branch; otherwise use the reset skill to
|
|
39
|
+
return to the updated default branch and apply any documented project setup.
|
|
40
|
+
6. When the project deploys the merged branch, verify the deployment for the
|
|
41
|
+
merge commit and run its documented live checks. Report deployment failures
|
|
42
|
+
separately from a successful merge; follow project policy for repairs.
|
|
43
|
+
|
|
44
|
+
Never infer a clean review from process success alone or carry a review across
|
|
45
|
+
content changes. Do not weaken configured CI policy to get a merge through.
|
|
46
|
+
If a step fails, preserve useful intermediate work and report the concrete
|
|
47
|
+
remaining issue. Report PR link, merged commit, review verdict, validation, and
|
|
48
|
+
final checkout state.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: squash-merge
|
|
3
|
+
description: Squash-merge a reviewed GitHub pull request while binding the mutation to the reviewed commit and checking CI.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Squash Merge
|
|
7
|
+
|
|
8
|
+
Use the installed `agent-tool` and repository merge policy. Merge only within
|
|
9
|
+
the user's authorized scope. Have the exact reviewed HEAD, base commit, and base
|
|
10
|
+
branch available, and read the verdict and unresolved findings. BLOCKER or MAJOR
|
|
11
|
+
findings require repair and a new review before merging.
|
|
12
|
+
Follow the repository's review-bot policy and require all actionable blocking
|
|
13
|
+
review feedback to be addressed before merging.
|
|
14
|
+
|
|
15
|
+
Wait for required checks using `gh pr checks --watch --fail-fast`. Refresh the
|
|
16
|
+
PR and live base from the repository that owns the PR. Confirm local HEAD and PR
|
|
17
|
+
HEAD both equal the reviewed commit, the base branch is unchanged, the live base
|
|
18
|
+
still equals the reviewed base, and that base is an ancestor of reviewed HEAD.
|
|
19
|
+
If any identity changed, refresh, validate, and review again.
|
|
20
|
+
|
|
21
|
+
Use a title that satisfies `agent-tool.json` and pass both reviewed HEAD and
|
|
22
|
+
base branch:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
agent-tool pr merge 'feat: describe the change' <reviewed-head-oid> <base-branch>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The helper enforces HEAD atomically through GitHub's `expectedHeadOid`, checks
|
|
29
|
+
CI, rejects queued or automatic merges, and creates a subject-only squash with
|
|
30
|
+
the PR reference. GitHub has no atomic expected-base argument in this mutation;
|
|
31
|
+
repository branch protection must enforce any required base freshness. With
|
|
32
|
+
`merge.requireStrictBaseFreshness`, the helper refuses to merge unless an
|
|
33
|
+
active strict status ruleset that the actor cannot bypass protects the base. Do
|
|
34
|
+
not retarget the PR concurrently with merging.
|
|
35
|
+
|
|
36
|
+
Confirm GitHub reports MERGED before any cleanup. A failed or uncertain mutation
|
|
37
|
+
is not evidence of a merge. Preserve the branch on failure. After a confirmed
|
|
38
|
+
merge, perform checkout cleanup only when requested or as part of an authorized
|
|
39
|
+
shipping workflow. Report the PR and merge result.
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { writeFileSync, writeSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { DEFAULT_CONFIG, loadConfig } from "./config";
|
|
5
|
+
import { doctor } from "./doctor";
|
|
6
|
+
import { review } from "./harnesses";
|
|
7
|
+
import { runAgentToolAction } from "./runAgentToolAction";
|
|
8
|
+
import { BUNDLED_SKILLS } from "./skills/bundled";
|
|
9
|
+
import { checkSkills, installSkills } from "./skills/install";
|
|
10
|
+
import { bumpVersions, checkVersions, planVersions } from "./version/bumpVersions";
|
|
11
|
+
import { prepareVersions } from "./version/prepareVersions";
|
|
12
|
+
import { resolveVersionConflicts } from "./version/resolveVersionConflicts";
|
|
13
|
+
import { openPr } from "./pr/openPr";
|
|
14
|
+
import { squashMerge } from "./pr/squashMerge";
|
|
15
|
+
import metadata from "../package.json";
|
|
16
|
+
|
|
17
|
+
export const HELP = `agent-tool ${metadata.version}
|
|
18
|
+
|
|
19
|
+
Usage: agent-tool [--repo <directory>] [--config <file>] <command>
|
|
20
|
+
|
|
21
|
+
review <claude|codex|opencode> [effort] [--base <local-ref>]
|
|
22
|
+
pr open [title] Body from stdin; branch already pushed
|
|
23
|
+
pr merge <subject-or-empty> <head-oid> <base-branch>
|
|
24
|
+
versions <plan|bump|check|prepare> <base-oid>
|
|
25
|
+
versions resolve-conflicts
|
|
26
|
+
skills list
|
|
27
|
+
skills install [--harness <all|claude|codex|opencode>] [--apply]
|
|
28
|
+
skills check [--harness <all|claude|codex|opencode>]
|
|
29
|
+
init Create agent-tool.json without overwriting
|
|
30
|
+
config show
|
|
31
|
+
doctor JSON versions and supported review flags
|
|
32
|
+
--help | --version
|
|
33
|
+
|
|
34
|
+
Legacy actions remain available: solicitClaudeCodeReview, solicitCodexReview,
|
|
35
|
+
solicitOpencodeReview, openPr, squashMerge, bumpVersions, checkVersions,
|
|
36
|
+
resolveVersionConflicts. A successful review exit means a usable review was
|
|
37
|
+
returned; inspect VERDICT before shipping. Skill install previews unless --apply.
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
function output(value: unknown): void { writeSync(1, `${JSON.stringify(value, null, 2)}\n`); }
|
|
41
|
+
function usage(condition: boolean, message: string): void { if (!condition) throw new Error(message); }
|
|
42
|
+
function gitRoot(): string {
|
|
43
|
+
return execFileSync("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function main(argv = process.argv.slice(2)): number {
|
|
47
|
+
const args = [...argv];
|
|
48
|
+
while (args[0] === "--repo" || args[0] === "--config") {
|
|
49
|
+
const flag = args.shift()!;
|
|
50
|
+
const value = args.shift();
|
|
51
|
+
usage(!!value && !value.startsWith("--"), `${flag} requires a path.`);
|
|
52
|
+
if (flag === "--repo") process.chdir(value!);
|
|
53
|
+
else process.env.AGENT_TOOL_CONFIG = path.resolve(value!);
|
|
54
|
+
}
|
|
55
|
+
const command = args.shift();
|
|
56
|
+
if (!command || command === "--help" || command === "-h") {
|
|
57
|
+
usage(args.length === 0, "Unexpected arguments after --help.");
|
|
58
|
+
writeSync(1, HELP); return 0;
|
|
59
|
+
}
|
|
60
|
+
if (command === "--version") { usage(args.length === 0, "Unexpected version arguments."); writeSync(1, `${metadata.version}\n`); return 0; }
|
|
61
|
+
if (command === "init") {
|
|
62
|
+
usage(args.length === 0, "Usage: agent-tool init");
|
|
63
|
+
writeFileSync(path.join(process.cwd(), "agent-tool.json"), `${JSON.stringify(DEFAULT_CONFIG, null, 2)}\n`, { flag: "wx" });
|
|
64
|
+
writeSync(1, "Created agent-tool.json. Set project policy before shipping.\n"); return 0;
|
|
65
|
+
}
|
|
66
|
+
if (command === "config") { usage(args.length === 1 && args[0] === "show", "Usage: agent-tool config show"); output(loadConfig()); return 0; }
|
|
67
|
+
if (command === "doctor") {
|
|
68
|
+
usage(args.length === 0, "Usage: agent-tool doctor");
|
|
69
|
+
const result = doctor(process.cwd()); output(result);
|
|
70
|
+
return result.tools.find(item => item.name === "git")?.compatible && result.tools.some(item => ["claude", "codex", "opencode"].includes(item.name) && item.compatible) ? 0 : 1;
|
|
71
|
+
}
|
|
72
|
+
if (command === "skills") {
|
|
73
|
+
const action = args.shift();
|
|
74
|
+
if (action === "list") { usage(args.length === 0, "Usage: agent-tool skills list"); output(Object.keys(BUNDLED_SKILLS)); return 0; }
|
|
75
|
+
usage(action === "install" || action === "check", "Usage: agent-tool skills <list|install|check>");
|
|
76
|
+
let target = "all", apply = false;
|
|
77
|
+
while (args.length) {
|
|
78
|
+
const flag = args.shift();
|
|
79
|
+
if (flag === "--apply") { usage(action === "install", "skills check is read-only; --apply is not allowed."); usage(!apply, "Duplicate --apply."); apply = true; }
|
|
80
|
+
else if (flag === "--harness") { const value = args.shift(); usage(!!value, "--harness requires a name."); target = value!; }
|
|
81
|
+
else throw new Error(`Unknown skills ${action} argument '${flag}'.`);
|
|
82
|
+
}
|
|
83
|
+
if (action === "check") {
|
|
84
|
+
const changes = checkSkills(process.cwd(), target);
|
|
85
|
+
const ok = changes.every(change => change.status === "unchanged");
|
|
86
|
+
output({ ok, changes }); return ok ? 0 : 1;
|
|
87
|
+
}
|
|
88
|
+
output({ applied: apply, changes: installSkills(process.cwd(), target, apply) }); return 0;
|
|
89
|
+
}
|
|
90
|
+
const root = gitRoot();
|
|
91
|
+
process.chdir(root);
|
|
92
|
+
loadConfig(root); // Validate policy before running a reviewer or mutating GitHub.
|
|
93
|
+
if (command === "review") {
|
|
94
|
+
const name = args.shift(); usage(!!name, "Usage: agent-tool review <claude|codex|opencode> [effort] [--base ref]");
|
|
95
|
+
let effort: string | undefined, base: string | undefined;
|
|
96
|
+
while (args.length) {
|
|
97
|
+
const value = args.shift()!;
|
|
98
|
+
if (value === "--base") { usage(base === undefined && !!args[0] && !args[0].startsWith("--"), "--base requires one local ref."); base = args.shift(); }
|
|
99
|
+
else { usage(effort === undefined && !value.startsWith("-"), `Unexpected review argument '${value}'.`); effort = value; }
|
|
100
|
+
}
|
|
101
|
+
return review(root, name!, effort, base);
|
|
102
|
+
}
|
|
103
|
+
if (command === "pr") {
|
|
104
|
+
const action = args.shift();
|
|
105
|
+
if (action === "open") { usage(args.length <= 1, "Usage: agent-tool pr open [title]"); return openPr(root, args[0]); }
|
|
106
|
+
usage(action === "merge" && args.length === 3, "Usage: agent-tool pr merge <subject-or-empty> <reviewed-head-oid> <base-branch>");
|
|
107
|
+
usage(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/i.test(args[1]!), "Merge requires a full reviewed HEAD OID.");
|
|
108
|
+
return squashMerge(root, args[0], args[1], args[2]);
|
|
109
|
+
}
|
|
110
|
+
if (command === "versions") {
|
|
111
|
+
const action = args.shift();
|
|
112
|
+
if (action === "resolve-conflicts") { usage(args.length === 0, "resolve-conflicts takes no arguments."); return resolveVersionConflicts(root); }
|
|
113
|
+
usage(args.length === 1, "Usage: agent-tool versions <plan|bump|check|prepare> <base-oid>");
|
|
114
|
+
if (action === "plan") { output(planVersions(root, args[0])); return 0; }
|
|
115
|
+
if (action === "bump") return bumpVersions(root, args[0]);
|
|
116
|
+
if (action === "check") return checkVersions(root, args[0]);
|
|
117
|
+
if (action === "prepare") return prepareVersions(root, args[0]);
|
|
118
|
+
throw new Error(`Unknown version action '${action}'.`);
|
|
119
|
+
}
|
|
120
|
+
return runAgentToolAction(root, [command, ...args]);
|
|
121
|
+
}
|