opencode-forgekeeper 1.0.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.
- package/README.md +387 -0
- package/dist/index.js +2319 -0
- package/dist/tui.js +1529 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# opencode-forgekeeper
|
|
2
|
+
|
|
3
|
+
An [OpenCode](https://opencode.ai) plugin that tracks the issues or PRs/MRs a session works on.
|
|
4
|
+
It prefixes the session title with their references, shows the PR/MR status in the prompt
|
|
5
|
+
footer, and tells the agent about new review feedback. It supports GitLab and GitHub. The status
|
|
6
|
+
and review notifications are optional; see [Keep only session titles](#keep-only-session-titles).
|
|
7
|
+
|
|
8
|
+
Requires OpenCode 2. It replaces
|
|
9
|
+
[`opencode-forge-session-title`](https://www.npmjs.com/package/opencode-forge-session-title); see
|
|
10
|
+
[Migrating from opencode-forge-session-title](#migrating-from-opencode-forge-session-title).
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
opencode plugin add opencode-forgekeeper
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This adds the plugin to your `opencode.json`:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"$schema": "https://opencode.ai/config.json",
|
|
23
|
+
"plugins": ["opencode-forgekeeper"]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The package has a server entry point and a CLI entry point, which OpenCode loads automatically.
|
|
28
|
+
The server owns session targets, title prefixes, forge lookups, polling, and notification
|
|
29
|
+
delivery. The CLI holds leases on the sessions it has shown, their latest snapshots, and each
|
|
30
|
+
directory's settings. It renders the status that the server serves and shows its notices as
|
|
31
|
+
toasts.
|
|
32
|
+
|
|
33
|
+
### Requirements
|
|
34
|
+
|
|
35
|
+
- [`glab`](https://gitlab.com/gitlab-org/cli) for GitLab and [`gh`](https://cli.github.com/)
|
|
36
|
+
for GitHub, on the `PATH` of the OpenCode server. Each is only needed for its forge.
|
|
37
|
+
- Each CLI logged in to every host it's used for: `glab auth login --hostname <host>` and
|
|
38
|
+
`gh auth login --hostname <host>`. The plugin uses their credentials and has none of its
|
|
39
|
+
own.
|
|
40
|
+
- For self-managed GitLab hosts not named `gitlab.*`, and for GitHub Enterprise, the
|
|
41
|
+
`hosts` and `githubHosts` [options](#options).
|
|
42
|
+
|
|
43
|
+
Without a forge CLI, titles still follow targets and branches, and `/forge-status` reports
|
|
44
|
+
what's missing, such as `glab is not installed`.
|
|
45
|
+
|
|
46
|
+
## Session targets
|
|
47
|
+
|
|
48
|
+
The server registers the `set_session_target` tool and adds instructions to the agent's
|
|
49
|
+
context. When you establish or change the primary issue, PR, or MR, the agent sets its full
|
|
50
|
+
URL as the session target. Background references, comparisons, and dependencies keep the
|
|
51
|
+
current target. After the agent creates a PR/MR for the current task, it sets the new PR/MR
|
|
52
|
+
as the target, passing the current issue as `issue_url`.
|
|
53
|
+
|
|
54
|
+
For example, if the title starts with `[#123, !45]` and you ask the agent to review MR `!456`,
|
|
55
|
+
the prefix becomes `[!456]`. Without `issue_url`, the server reads the PR/MR's source branch
|
|
56
|
+
and takes the issue number from its name, so reviewing an MR from `321-fix-timeout` produces
|
|
57
|
+
`[#321, !456]`.
|
|
58
|
+
|
|
59
|
+
Targets persist per session across plugin reloads. Calling `set_session_target` with
|
|
60
|
+
`target: "branch"` returns to branch-based naming. Child sessions and untitled sessions are
|
|
61
|
+
skipped.
|
|
62
|
+
|
|
63
|
+
### Several targets
|
|
64
|
+
|
|
65
|
+
A session can work on up to 50 issues and PRs/MRs at once, such as a set of related MRs
|
|
66
|
+
under review. Pass their full URLs in `targets` instead of `target`, and choose how they
|
|
67
|
+
change the current targets with `operation`:
|
|
68
|
+
|
|
69
|
+
| `operation` | Effect |
|
|
70
|
+
| ------------------- | ------------------------------------------------------------------------------ |
|
|
71
|
+
| `replace` (default) | Sets exactly the given targets. |
|
|
72
|
+
| `add` | Appends the given targets and keeps the current ones. |
|
|
73
|
+
| `remove` | Drops the given targets. Removing the last one returns to branch-based naming. |
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"targets": [
|
|
78
|
+
"https://gitlab.com/group/project/-/merge_requests/101",
|
|
79
|
+
"https://gitlab.com/group/other/-/merge_requests/102"
|
|
80
|
+
],
|
|
81
|
+
"operation": "add"
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The server normalizes and deduplicates the URLs, which can come from different projects.
|
|
86
|
+
|
|
87
|
+
With `targets`, `issue_url` relates every listed target to the same issue, such as a set of
|
|
88
|
+
MRs for one issue. Every target must then be a PR/MR from the issue's forge. With
|
|
89
|
+
`operation: "add"`, only the added targets get the issue.
|
|
90
|
+
|
|
91
|
+
The agent guidance is generic. When you ask the agent to work on several issues or PRs/MRs,
|
|
92
|
+
it sets all of them as targets. If you describe them instead of linking them, the agent
|
|
93
|
+
finds them first, then sets them. After it creates a PR/MR for a task with several targets,
|
|
94
|
+
it adds the new one instead of replacing the others.
|
|
95
|
+
|
|
96
|
+
With several targets, the title prefix lists their own references, such as `[!101, !102]`.
|
|
97
|
+
When every target has the same related issue, from `issue_url` or from its source branch
|
|
98
|
+
name, the issue comes first, such as `[#12, !101, !102]`. More than four targets list the
|
|
99
|
+
first three and count the rest, such as `[!101, !102, !103, +3]`.
|
|
100
|
+
|
|
101
|
+
Target URLs can name a GitLab MR, issue, or work item on any host, or a GitHub PR or issue.
|
|
102
|
+
GitHub URLs are accepted on any host that isn't recognizably GitLab, so GitHub Enterprise
|
|
103
|
+
targets work.
|
|
104
|
+
|
|
105
|
+
## Branch-based naming
|
|
106
|
+
|
|
107
|
+
Without an explicit target, the title follows the checked-out branch:
|
|
108
|
+
|
|
109
|
+
1. The prefix starts with the issue number from the branch name, or the branch name
|
|
110
|
+
without one.
|
|
111
|
+
2. It adds the newest open PR/MR from the branch, or `!N/A` (GitLab) or `#N/A` (GitHub)
|
|
112
|
+
until one exists.
|
|
113
|
+
|
|
114
|
+
The title is reconciled after target changes, title changes, and successful agent runs.
|
|
115
|
+
Text you add to the title, including your own bracketed prefixes, is kept.
|
|
116
|
+
|
|
117
|
+
Branch names longer than 40 characters are shortened with an ellipsis in the prefix. Titles
|
|
118
|
+
stay within 100 characters by shortening the text after the prefix, never the prefix
|
|
119
|
+
itself, so the plugin can still find and replace its prefix later.
|
|
120
|
+
|
|
121
|
+
The default branch and branches named `main`, `master`, `develop`, or `HEAD` get no prefix.
|
|
122
|
+
|
|
123
|
+
The branch's remote, pushed branch name, and forge come from the same repository discovery
|
|
124
|
+
as the status footer. That discovery uses the branch's push or upstream remote, recognizes
|
|
125
|
+
forks, and classifies hosts with the `hosts` and `githubHosts` options.
|
|
126
|
+
|
|
127
|
+
| Pattern | Example branch | Issue |
|
|
128
|
+
| --------------------------------- | ----------------------- | ----- |
|
|
129
|
+
| `<prefix>/<number>-<description>` | `feature/123-add-login` | `123` |
|
|
130
|
+
| `<number>-<description>` | `123-fix-typo` | `123` |
|
|
131
|
+
| `<description>-<number>` | `fix-typo-123` | `123` |
|
|
132
|
+
| `<prefix>/<number>/<description>` | `user/123/some-work` | `123` |
|
|
133
|
+
| `issue-<number>`, `gh-<number>` | `gh-42-improve-perf` | `42` |
|
|
134
|
+
| `fix-<number>`, `feat-<number>` | `fix-99` | `99` |
|
|
135
|
+
|
|
136
|
+
## Status footer
|
|
137
|
+
|
|
138
|
+
The server picks each session's PRs/MRs in this order:
|
|
139
|
+
|
|
140
|
+
1. The explicit targets.
|
|
141
|
+
1. A PR/MR number in a title prefix that you wrote, such as `!456` in `[!456] Review`.
|
|
142
|
+
1. The checked-out branch's open PRs/MRs.
|
|
143
|
+
|
|
144
|
+
A prefix that the plugin wrote from the branch doesn't count as a title reference, so the
|
|
145
|
+
footer follows the branch when its PR/MR closes and another opens. The title prefix itself
|
|
146
|
+
reuses a branch's PR/MR number for up to 10 minutes.
|
|
147
|
+
|
|
148
|
+
Explicit targets that aren't PRs/MRs, such as issues, show no status. Unresolved explicit
|
|
149
|
+
PRs/MRs never fall back to the branch.
|
|
150
|
+
|
|
151
|
+
The server runs at most four `glab` or `gh` commands at once for status and review
|
|
152
|
+
feedback, across all sessions. When some target lookups fail, the footer shows the others
|
|
153
|
+
and counts the failures as `N unavailable`. A target whose lookup failed with an error
|
|
154
|
+
keeps its previous status until a later poll succeeds. A GitLab response that reports an
|
|
155
|
+
error counts as a failed lookup, never as a missing MR, so it's retried with backoff.
|
|
156
|
+
|
|
157
|
+
When the CLI can't reach the server, such as while the server plugin reloads, the footer
|
|
158
|
+
keeps the last status and marks it `stale`. The CLI never looks PRs/MRs up itself, so it
|
|
159
|
+
can't show another project's PR/MR with the same number as a target.
|
|
160
|
+
|
|
161
|
+
With several PR/MR targets, the footer shows counts instead of one PR/MR's status, such as
|
|
162
|
+
`5 MRs · 🤖 2 reviewing · 1 CI failed · 1 conflict · 1 merged`. Clicking it opens the
|
|
163
|
+
status dialog, which lists each PR/MR.
|
|
164
|
+
|
|
165
|
+
The footer shows checks or the pipeline, unresolved threads, conflicts, approval, and a
|
|
166
|
+
running GitLab Duo review. GitHub also shows a requested change. Unknown mergeability stays
|
|
167
|
+
unknown rather than appearing conflict-free.
|
|
168
|
+
|
|
169
|
+
The footer shows `approved` only when GitLab reports that approval requirements are met,
|
|
170
|
+
at least one person approved, and every human reviewer approved. Bot approvals, such as
|
|
171
|
+
Duo's, don't count. While human reviewers haven't approved, it shows `awaiting @username`,
|
|
172
|
+
or `awaiting N reviewers` for more than two.
|
|
173
|
+
|
|
174
|
+
Clicking the PR/MR number or pipeline opens it in the browser. Clicking any other indicator
|
|
175
|
+
opens the status dialog. These commands are also available from the command palette:
|
|
176
|
+
|
|
177
|
+
- `/forge-status` opens a dialog with full status and a refresh action. Aliases:
|
|
178
|
+
`/mr-status` and `/pr-status`.
|
|
179
|
+
- `/forge-open` opens the PR/MR in the browser. Aliases: `/mr-open` and `/pr-open`.
|
|
180
|
+
|
|
181
|
+
The server caches each session's status and polls every 2 minutes while a CLI shows an open
|
|
182
|
+
PR/MR, or every 30 seconds while an automated review runs. Each change reaches the CLIs as
|
|
183
|
+
an RPC event.
|
|
184
|
+
|
|
185
|
+
### Which sessions the server watches
|
|
186
|
+
|
|
187
|
+
The server watches only sessions that a CLI has shown since it started. The CLI renews a
|
|
188
|
+
lease on each of them every minute, and a lease that isn't renewed for 3 minutes ends, such
|
|
189
|
+
as after the CLI exits. A session keeps polling while it's on screen in any CLI. A hidden
|
|
190
|
+
session with a lease keeps polling while it has a running automated review, human feedback
|
|
191
|
+
to watch, or a notification to retry.
|
|
192
|
+
|
|
193
|
+
A hidden session that the server hasn't looked up yet, such as after the server plugin
|
|
194
|
+
reloads, is looked up once to find out whether it has reviews to watch. A target change
|
|
195
|
+
also looks up a hidden session that isn't polling.
|
|
196
|
+
|
|
197
|
+
Each checkout's server plugin instance watches only the sessions in that checkout. When a
|
|
198
|
+
session moves to another worktree, the CLI moves its lease to that checkout's instance. The
|
|
199
|
+
old instance stops watching the session, even if another CLI still leases it there. Before
|
|
200
|
+
each notification, the server reads the session again, so a session that moved while a
|
|
201
|
+
lookup or fetch ran is left to its new checkout. A review that finishes during the move can
|
|
202
|
+
go unannounced, because the new instance never saw it running. If the session can't be
|
|
203
|
+
read, the notification is retried later instead of being dropped.
|
|
204
|
+
|
|
205
|
+
After that read, the server checks the targets again and writes the message for the
|
|
206
|
+
PRs/MRs that are still targets. A target removed in the meantime isn't mentioned.
|
|
207
|
+
|
|
208
|
+
When the server plugin stops, such as on a reload, no new lookup, fetch, notification,
|
|
209
|
+
title update, or target change starts, and queued `glab` and `gh` commands don't run. A
|
|
210
|
+
title update that is still looking up its branch doesn't rename the session or store
|
|
211
|
+
anything, because the replacement instance owns the session's state by then. Notifications
|
|
212
|
+
that were already being sent can finish and store their outcome, so the replacement instance
|
|
213
|
+
doesn't send them again. Cleanup waits up to 5 seconds for them. One that takes longer may be
|
|
214
|
+
sent again.
|
|
215
|
+
|
|
216
|
+
## Review notifications
|
|
217
|
+
|
|
218
|
+
For each explicit target that is an open PR/MR, the server tells the agent about reviews.
|
|
219
|
+
A target that merges or closes gets no more notifications, and a notification waiting for a
|
|
220
|
+
retry is dropped.
|
|
221
|
+
Each notification is a queued synthetic message, followed by a toast in the CLIs that have
|
|
222
|
+
shown the session. The message resumes the session unless `resumeSession` is `false`.
|
|
223
|
+
Messages include the full URL of each PR/MR. PRs/MRs from the title or the branch don't get
|
|
224
|
+
notifications.
|
|
225
|
+
|
|
226
|
+
Before it sends a notification, the server checks that the PR/MR is still one of the
|
|
227
|
+
session's targets. A lookup that started before the targets changed is discarded, so a
|
|
228
|
+
removed target never triggers a notification.
|
|
229
|
+
|
|
230
|
+
| Notification | GitLab | GitHub |
|
|
231
|
+
| ------------------------------------------ | ----------- | ------------- |
|
|
232
|
+
| An automated review finished with feedback | GitLab Duo | Not supported |
|
|
233
|
+
| New human review feedback | MR comments | Not supported |
|
|
234
|
+
|
|
235
|
+
- **Automated reviews**: Duo's final state of `REVIEWED` or `REQUESTED_CHANGES` asks the
|
|
236
|
+
agent to read its comments. Other final states, such as `APPROVED`, send nothing. The
|
|
237
|
+
server notifies only when it sees a review go from running to finished, so a review that
|
|
238
|
+
finished before the server first saw it running isn't announced. Reviews that finish in
|
|
239
|
+
the same poll share one message.
|
|
240
|
+
- **Human reviews**: new comments and replies from people other than you and bots are
|
|
241
|
+
announced once none of their unresolved comments has changed for 5 minutes. Expect one
|
|
242
|
+
message 5 to 7 minutes after the review goes quiet. Resolved threads are skipped.
|
|
243
|
+
|
|
244
|
+
The first look at a PR/MR records its newest comment as a baseline, so older comments never
|
|
245
|
+
count as new. Announced comments are stored per session and PR/MR, so a restart replays
|
|
246
|
+
nothing but still catches comments posted in the meantime.
|
|
247
|
+
|
|
248
|
+
A failed send shows an error toast and is retried after 10 minutes. A failed automated
|
|
249
|
+
review notification stays pending in the server's storage, so it's retried even after a
|
|
250
|
+
restart, as long as the review still has feedback and the PR/MR is still an open target.
|
|
251
|
+
|
|
252
|
+
Every checkout's server plugin instance shares the plugin's storage, so each baseline and
|
|
253
|
+
each pending notification has its own storage key. Instances never overwrite each other's
|
|
254
|
+
records. A failed storage write shows an error toast once.
|
|
255
|
+
|
|
256
|
+
On GitLab, the server reads the newest 2,000 comments. On busier MRs, edits to older
|
|
257
|
+
comments don't count as activity.
|
|
258
|
+
|
|
259
|
+
## Options
|
|
260
|
+
|
|
261
|
+
Set options with the object form of the `plugins` entry in `opencode.json`:
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"plugins": [{ "package": "opencode-forgekeeper", "options": { "notifyHumanReviews": false } }]
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
| Option | Default | Description |
|
|
270
|
+
| ------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
271
|
+
| `reviewStatus` | `true` | Show the PR/MR status and notify the agent about reviews. Set to `false` to keep only session targets and titles. |
|
|
272
|
+
| `hosts` | `["gitlab.com"]` | GitLab hosts whose remotes are recognized, besides hosts named `gitlab.*`. |
|
|
273
|
+
| `githubHosts` | `["github.com"]` | GitHub hosts, including GitHub Enterprise hosts. |
|
|
274
|
+
| `pollSeconds` | `120` | Normal polling interval. Running automated reviews poll at 30 seconds or this value, whichever is lower. |
|
|
275
|
+
| `notifyAutomatedReviews` | `true` | Tell the agent when an automated review finishes with feedback. The former name, `notifyDuoReview`, still works. |
|
|
276
|
+
| `notifyHumanReviews` | `true` | Tell the agent about new human review feedback. |
|
|
277
|
+
| `resumeSession` | `true` | Resume the session when telling the agent about a review. Set to `false` to queue the message for the session's next turn instead. |
|
|
278
|
+
|
|
279
|
+
The server entry point reads every option, so changing one takes effect when the server
|
|
280
|
+
plugin reloads. The CLI has no options of its own. Each checkout's server reads the
|
|
281
|
+
options for its location, so the CLI can show status in one project and not in another.
|
|
282
|
+
|
|
283
|
+
### Keep only session titles
|
|
284
|
+
|
|
285
|
+
To keep the behavior of `opencode-forge-session-title` alone, turn off review status:
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"plugins": [{ "package": "opencode-forgekeeper", "options": { "reviewStatus": false } }]
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The server still registers `set_session_target`, adds the agent guidance, and maintains the
|
|
294
|
+
title prefix. It doesn't poll or send review notifications, and the CLI shows no footer.
|
|
295
|
+
`/forge-status` and `/forge-open` explain that the status is off.
|
|
296
|
+
|
|
297
|
+
## RPC
|
|
298
|
+
|
|
299
|
+
The server registers an [RPC](https://opencode.ai/v2/docs/build/plugins/rpc) that the CLI
|
|
300
|
+
uses, defined in `src/rpc.ts`:
|
|
301
|
+
|
|
302
|
+
- `target({ sessionID })` returns `{ url, issueUrl?, targets }` for explicit targets, and
|
|
303
|
+
`{}` in branch mode. `targets` lists every target as `{ url, issueUrl? }`. `url` and
|
|
304
|
+
`issueUrl` describe the first target, for callers that expect a single target.
|
|
305
|
+
- `watch({ clientID, keys, visible? })` renews the caller's leases on `keys` and returns
|
|
306
|
+
`{ enabled, statuses }`. A key is a session ID, or `""` for the checkout outside a
|
|
307
|
+
session. `visible` is the key on screen.
|
|
308
|
+
- `release({ clientID })` drops the caller's leases.
|
|
309
|
+
- `refresh({ key })` looks the key up now and returns `{ enabled, snapshot }`.
|
|
310
|
+
- `targetChanged` fires after `set_session_target` runs, with the same fields as `target`
|
|
311
|
+
and the `sessionID`. It has only the `sessionID` in branch mode.
|
|
312
|
+
- `status` fires with `{ directory, key, snapshot }` when a key's status changes.
|
|
313
|
+
- `notice` fires with `{ sessionID?, title?, message, variant }` for the CLIs to show as a
|
|
314
|
+
toast.
|
|
315
|
+
|
|
316
|
+
Keys are relative to the location of the call or event, because each checkout's server
|
|
317
|
+
plugin instance watches its own sessions.
|
|
318
|
+
|
|
319
|
+
## Migrating from opencode-forge-session-title
|
|
320
|
+
|
|
321
|
+
Replace `opencode-forge-session-title` with `opencode-forgekeeper` in `opencode.json`. Never
|
|
322
|
+
load both: the server keeps `opencode-forge-session-title`'s plugin and RPC IDs, so the two
|
|
323
|
+
would collide.
|
|
324
|
+
|
|
325
|
+
| Entry point | Plugin ID | Stored state |
|
|
326
|
+
| ----------- | ------------------------------ | ---------------------------------------------------------------------------------------- |
|
|
327
|
+
| Server | `opencode-forge-session-title` | Session targets, owned title prefixes, human-review baselines, and pending notifications |
|
|
328
|
+
| CLI | `pedropombeiro.forgekeeper` | None |
|
|
329
|
+
|
|
330
|
+
Because the IDs match, stored targets carry over, and callers of the `target` RPC keep working.
|
|
331
|
+
The server reads a session's single stored `target` as a list of one, and writes `targets` from
|
|
332
|
+
the next change on.
|
|
333
|
+
|
|
334
|
+
With default options, the new footer and review notifications start right away, and
|
|
335
|
+
notifications resume the session. Set `reviewStatus` to `false` to keep only titles, or
|
|
336
|
+
`resumeSession` to `false` to queue notifications for the next turn.
|
|
337
|
+
|
|
338
|
+
## Code layout
|
|
339
|
+
|
|
340
|
+
- `src/index.ts` wires the server: the forge catalogs from the options, with a shared
|
|
341
|
+
concurrency limit for status, and title lookups.
|
|
342
|
+
- `src/server.ts` registers the tool, the agent guidance, title reconciliation, and the RPC.
|
|
343
|
+
- `src/status-server.ts` connects the status service to sessions, storage, notifications,
|
|
344
|
+
and RPC events. `src/status-service.ts` holds the leases and decides which keys poll.
|
|
345
|
+
- `src/target.ts` parses, normalizes, changes, and formats session targets, and matches
|
|
346
|
+
forge URLs against them.
|
|
347
|
+
- `src/limit.ts` bounds how many forge requests run at once.
|
|
348
|
+
- `src/title.ts` extracts branch issue numbers and reconciles title prefixes.
|
|
349
|
+
- `src/options.ts` reads the plugin's status options.
|
|
350
|
+
- `src/lookups.ts` answers the title's forge questions: the branch's newest PR/MR number
|
|
351
|
+
and a PR/MR's source branch.
|
|
352
|
+
- `src/status-client.ts` holds the CLI's leases, snapshots, and per-directory settings.
|
|
353
|
+
`src/tui.tsx` connects it to the RPC and renders the footer, dialog, commands, and notices.
|
|
354
|
+
- `src/forge.ts` defines the forge-neutral types, the `Forge` adapter interface, and
|
|
355
|
+
`ForgeTraits` with its optional `automatedReview` and `feedback` capabilities.
|
|
356
|
+
- `src/gitlab.ts` and `src/github.ts` provide each forge's adapter and traits. Adapters own
|
|
357
|
+
API calls, pagination, error classification, and status normalization. Traits cover
|
|
358
|
+
everything else: hosts, URL parsing, reference syntax, vocabulary, and forge-specific
|
|
359
|
+
footer and dialog content.
|
|
360
|
+
- `src/forges.ts` registers each forge's traits, classifies hosts, opens adapters, and
|
|
361
|
+
parses URLs.
|
|
362
|
+
- `src/git.ts` resolves a checkout's remotes, branch, and pushed branch name.
|
|
363
|
+
- `src/locate.ts` picks each key's PRs/MRs. `src/store.ts` caches lookups, polls, backs off
|
|
364
|
+
after failures, and discards lookups that started before a change. `src/format.ts`
|
|
365
|
+
renders the footer and dialog.
|
|
366
|
+
- `src/automated-review-watch.ts`, `src/human-review-watch.ts`, and
|
|
367
|
+
`src/gitlab-feedback.ts` decide when to notify the agent.
|
|
368
|
+
|
|
369
|
+
Shared code never compares a forge against a specific name. It asks the forge's traits, or
|
|
370
|
+
checks for a capability, instead. To add a forge, add its kind to `ForgeKind` and
|
|
371
|
+
`ReviewRequest`, implement `Forge` and `ForgeTraits`, and register the traits in
|
|
372
|
+
`src/forges.ts`.
|
|
373
|
+
|
|
374
|
+
## Development
|
|
375
|
+
|
|
376
|
+
From the repository root:
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
mise run test
|
|
380
|
+
mise run typecheck
|
|
381
|
+
mise run lint
|
|
382
|
+
mise run build forgekeeper
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## License
|
|
386
|
+
|
|
387
|
+
[MIT](../../LICENSE)
|