mergestorm 0.3.63 → 0.3.66
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 +30 -10
- package/dist/api.d.ts +68 -11
- package/dist/api.js +206 -8
- package/dist/automation-catalog.d.ts +17 -3
- package/dist/automation-catalog.js +4 -1
- package/dist/cli.js +21 -19
- package/dist/client/index.d.ts +6 -4
- package/dist/client/index.js +6 -4
- package/dist/commands/account.d.ts +3 -0
- package/dist/commands/account.js +75 -0
- package/dist/commands/browse.d.ts +1 -9
- package/dist/commands/browse.js +10 -8
- package/dist/commands/dismiss.d.ts +0 -2
- package/dist/commands/dismiss.js +2 -2
- package/dist/commands/help.js +3 -2
- package/dist/commands/issue.d.ts +24 -6
- package/dist/commands/issue.js +153 -31
- package/dist/commands/login.d.ts +2 -1
- package/dist/commands/login.js +50 -14
- package/dist/commands/logout.js +6 -7
- package/dist/commands/memory.d.ts +29 -0
- package/dist/commands/memory.js +154 -0
- package/dist/commands/registry.d.ts +1 -0
- package/dist/commands/registry.js +52 -11
- package/dist/commands/review-client.d.ts +0 -3
- package/dist/commands/review-client.js +3 -3
- package/dist/commands/review.d.ts +1 -1
- package/dist/commands/review.js +1 -1
- package/dist/commands/settings.d.ts +3 -4
- package/dist/commands/settings.js +51 -5
- package/dist/commands/stack.d.ts +20 -5
- package/dist/commands/stack.js +106 -7
- package/dist/commands/whoami.js +14 -10
- package/dist/config.d.ts +56 -2
- package/dist/config.js +271 -13
- package/dist/errors.d.ts +1 -1
- package/dist/gh.d.ts +0 -4
- package/dist/gh.js +4 -4
- package/dist/git.d.ts +1 -1
- package/dist/git.js +2 -2
- package/dist/memory-api.d.ts +74 -0
- package/dist/memory-api.js +69 -0
- package/dist/node-version.d.ts +0 -2
- package/dist/node-version.js +1 -1
- package/dist/pr-body.d.ts +4 -0
- package/dist/pr-body.js +15 -0
- package/dist/repo-identity.d.ts +2 -1
- package/dist/repo-identity.js +8 -4
- package/dist/server-publish.d.ts +5 -26
- package/dist/server-publish.js +47 -40
- package/dist/stack-blockers.d.ts +15 -36
- package/dist/stack-blockers.js +69 -33
- package/dist/stack-dto.d.ts +4 -0
- package/dist/stack-dto.js +15 -2
- package/dist/stack-meta.d.ts +2 -2
- package/dist/stack-meta.js +1 -1
- package/dist/stack-watch-loop.d.ts +2 -13
- package/dist/stack-watch-loop.js +35 -14
- package/dist/stack-watch.d.ts +2 -1
- package/dist/stack-watch.js +6 -2
- package/dist/submit-pr-base.d.ts +0 -1
- package/dist/submit-pr-base.js +1 -1
- package/dist/ui/ansi.d.ts +0 -10
- package/dist/ui/ansi.js +2 -2
- package/dist/ui/banner.d.ts +3 -1
- package/dist/ui/banner.js +13 -3
- package/dist/ui/box.d.ts +0 -2
- package/dist/ui/box.js +1 -1
- package/dist/ui/prompt.d.ts +0 -11
- package/dist/ui/prompt.js +7 -7
- package/dist/ui/tabs.d.ts +2 -2
- package/dist/ui/tabs.js +1 -1
- package/dist/ui/width.d.ts +0 -9
- package/dist/ui/width.js +3 -3
- package/dist/web-api.d.ts +17 -0
- package/dist/web-api.js +30 -0
- package/package.json +3 -1
- package/skill/SKILL.md +4 -0
- package/skill/mergestorm-bounce-watch/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -45,12 +45,12 @@ On a TTY, bare `mergestorm` (or `mergestorm shell`) opens a branded REPL: a comp
|
|
|
45
45
|
|
|
46
46
|
```
|
|
47
47
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
48
|
-
│ ▀▀▀▀▀▀▀▀▀▀▘ mergestorm v0.3.
|
|
48
|
+
│ ▀▀▀▀▀▀▀▀▀▀▘ mergestorm v0.3.66 │
|
|
49
49
|
│ ▀▀▀▀▀▀▀▀▀ ● msk_live_… · maelstrom │
|
|
50
50
|
│ ▀▀▀▀▀▀ [████████░░░░░░░░░░░░░░░░░░░░░░] 25% used │
|
|
51
51
|
│ ▝▀▀▀▀▀ local reviews + stacked PRs · mergestorm.ai │
|
|
52
52
|
│ ▝▀▀▘ review a diff · usage for tabs · /help for all commands │
|
|
53
|
-
│ ▀ New in v0.3.
|
|
53
|
+
│ ▀ New in v0.3.66: `issue wait`; submit needs an approved issue │
|
|
54
54
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
55
55
|
mergestorm
|
|
56
56
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
@@ -75,11 +75,14 @@ One-shot subcommands work the same inside the shell and from argv (scripts/CI):
|
|
|
75
75
|
```
|
|
76
76
|
mergestorm Interactive shell (TTY only)
|
|
77
77
|
mergestorm shell Explicit shell entry
|
|
78
|
-
mergestorm login Sign in via browser
|
|
78
|
+
mergestorm login Sign in via browser and add the account (up to 3 saved)
|
|
79
79
|
mergestorm login --start Print a sign-in URL and code, then exit (agents)
|
|
80
80
|
mergestorm login --finish Store the API key once the code from --start is approved
|
|
81
81
|
mergestorm login --key Paste an existing API key instead (headless/CI)
|
|
82
|
-
mergestorm logout
|
|
82
|
+
mergestorm logout Log out of the active account (or --account <name>) and revoke its key
|
|
83
|
+
mergestorm account list [--json] Saved accounts (up to 3), the active one marked
|
|
84
|
+
mergestorm account use <name> Make <name> the active account
|
|
85
|
+
mergestorm account remove <name> Log out of <name> and revoke its key
|
|
83
86
|
mergestorm review [base] [head] Review git diff base...head (default: origin/HEAD or main or master)
|
|
84
87
|
mergestorm status <job_id> Fetch a review job (envelope; --json --wait --timeout)
|
|
85
88
|
mergestorm status Tabbed Status / Usage / Jobs browser (TTY)
|
|
@@ -87,10 +90,11 @@ mergestorm credits [--json] Usage panel (bar + last 5 jobs); tabbed browser
|
|
|
87
90
|
mergestorm jobs [n] [--json] Recent review jobs (default 10, max 50)
|
|
88
91
|
mergestorm branches [n] Recently reviewed branches (arrow-pick on TTY)
|
|
89
92
|
mergestorm chain [slug] Branch review-chain timeline (default: current branch)
|
|
90
|
-
mergestorm whoami [--json]
|
|
93
|
+
mergestorm whoami [--json] Active account, key prefix, plan, API base, config path
|
|
91
94
|
mergestorm skill install --claude|--cursor|--agents Copy the Mergestorm skills into this repo
|
|
92
95
|
mergestorm thread <slug> Jobs in a review thread
|
|
93
|
-
mergestorm issue create --title <t> Open a GitHub issue through Mergestorm, no `gh` (`--body`, `--body-file`, `--label`, `--repo`)
|
|
96
|
+
mergestorm issue create --title <t> Open a GitHub issue through Mergestorm, no `gh` (`--body`, `--body-file`, `--label`, `--repo`, `--wait`)
|
|
97
|
+
mergestorm issue wait <n> Wait for the Vortex issue review; exits 0 once approved, 4 with points to answer
|
|
94
98
|
mergestorm stack create [name] New local stack layer (optional `--onto` / `--trunk` / `--extend`)
|
|
95
99
|
mergestorm stack submit Push layers, open PRs (`gh`), register via adopt (optional `--extend`, `--server-side`)
|
|
96
100
|
mergestorm stack publish Publish the current branch through Mergestorm with no GitHub credentials (`--base <branch> --extend` to stack, `--status` to read it)
|
|
@@ -113,7 +117,8 @@ mergestorm dismiss <owner/repo>#<n> --head <sha> --review <id> --finding <id> --
|
|
|
113
117
|
- It prints nothing while the stack is `waiting` or `in_progress`, including while Cyclone or Vortex holds the blocked PR (`busy[]`, `actAfter: "agents_idle"`).
|
|
114
118
|
- On attention it prints `MS-WATCH ATTENTION pr=<n> head=<sha> blocker="<blocker>" repair=<kind>` and exits 3. Attention names the lowest layer with a blocker an agent must fix, on any layer of the stack, not only the current candidate.
|
|
115
119
|
- A confirmed landing prints `MS-WATCH LANDED stack=<id> reason=landed` and exits 0. `closed`, `archived` and `not_found` end the watch with `MS-WATCH ATTENTION stack=<id> reason=<reason>` and exit 3. A missing stack does not prove a landing; verify each PR before reporting it merged.
|
|
116
|
-
- A
|
|
120
|
+
- A permanent read error is retried once; if the retry fails too, it prints `MS-WATCH ATTENTION failed stack=<id> error="<message>"` and exits 3. Timeouts, 5xx and rate limits are retried, but after 15 minutes with no stack read it prints the same line and exits 3.
|
|
121
|
+
- A watch that cannot start (bad flags, a config it cannot read, a crash) prints `MS-WATCH ATTENTION failed` too, so the command never exits without an `MS-WATCH` line unless you stop it (Ctrl+C or an abort, exit 130). Pass each flag as its own word: in zsh an unquoted `$args` holding several flags stays one word and the command refuses it.
|
|
117
122
|
- `--head <sha>` enrolls the SHA you just pushed. After fixing a blocker, restart the watcher with it.
|
|
118
123
|
- `--ignore <text>` keeps it silent on the first attention whose blocker contains that text (case-insensitive), until the blocker, its PR or its head changes. Use it for a blocker a human has taken over. Repeat the flag for more than one.
|
|
119
124
|
- `--until landed` keeps running after an attention, printing each new attention once, and exits only when the watch is done or reads keep failing.
|
|
@@ -166,25 +171,39 @@ mg stack publish --status # absent | pushed | opened | registered
|
|
|
166
171
|
```
|
|
167
172
|
|
|
168
173
|
- **Issues too.** `mg issue create` opens the issue a PR needs through the same Surge App, with existing labels only. Running it again with the same title returns the issue it already opened. Put `Fixes #<n>` in the commit message; `stack submit` carries it into the PR body.
|
|
174
|
+
- **Issue reviews first.** On a repository with Vortex issue reviews on, Vortex reviews each new issue before any PR. `mg issue wait <n>` (or `mg issue create --wait`) waits for that review: it exits 0 once the issue is approved, or exits 4 with the numbered points to answer. Answer them in a comment on the issue and wait again; Vortex re-reviews on each reply, up to four rounds, after which a person approves with an issue comment whose first line is `mergestorm-issue: approve`. `stack submit` refuses to open a PR whose tip commit links an issue that is not approved yet, or links no issue at all while issue reviews are on.
|
|
169
175
|
- **Exact contents.** Every commit travels with its Git tree SHA and the whole content of each changed file, so binaries, deletions, renames, executable bits and symlinks arrive unchanged. Mergestorm moves the branch only when GitHub builds the same tree, and `mg` checks the returned tree against your local branch.
|
|
170
176
|
- **Never overwrites surprises.** `mg` looks at the branch on GitHub first. If its files match one of your local commits, only the commits after that one are appended. If they match none (someone else pushed, or you rebased), `mg` stops with `head_changed`; `--replace-head <sha>` replaces exactly that head and nothing else. Mergestorm refuses `main`, `master`, the default branch, protected branches and its own `mg-stack-*`, `mg-park-*` and `mq-batch-*` branches.
|
|
171
177
|
- **Safe to run again.** After a timeout or an error, run the same command again. It finds the branch, pull request and stack it already made and finishes the missing step; it does not create duplicates.
|
|
172
178
|
- **Read-only home directory.** `mg` keeps the saved login and the local stack state of `stack create` / `stack submit` under `~/.mergestorm`. Where the home directory cannot be written, set `MERGESTORM_HOME` to a writable directory (outside the repository) and every command uses that instead. `MERGESTORM_API_KEY` supplies the key without `mg login`.
|
|
179
|
+
- **Several accounts.** `mg login` keeps up to three accounts signed in; `mg account use <name>` picks the active one, and `--account <name>` or `MERGESTORM_ACCOUNT` runs one command as another.
|
|
173
180
|
- **Limits.** 20 commits, 500 changed paths, 2 MiB a file and 8 MiB in total per publication. Submodules and merge commits are refused with the path or the reason. `stack submit --server-side` publishes the first two layers of a stack; a third layer needs a park freeze, which this mode does not create, so add it once the first two are registered.
|
|
174
181
|
|
|
175
182
|
If local authoring state is stale or malformed, use `mg stack reset --force`; this clears only pre-submit CLI state and never deletes branches, PRs, or registered stacks.
|
|
176
183
|
|
|
177
184
|
### `login`
|
|
178
185
|
|
|
179
|
-
Runs a browser device-authorization flow: prints a short code, opens `https://mergestorm.ai/cli/auth`, and — once you approve — mints and
|
|
186
|
+
Runs a browser device-authorization flow: prints a short code, opens `https://mergestorm.ai/cli/auth`, and — once you approve — mints an API key and saves it as an account in `~/.mergestorm/config.json` (mode `600`), or in `$MERGESTORM_HOME/config.json` when `MERGESTORM_HOME` is set. No key copy/paste needed. The account is named by its GitHub login (or its email when there is no GitHub login), as `mg whoami` reports it, and becomes the active account.
|
|
187
|
+
|
|
188
|
+
Up to three accounts can stay logged in at once. `mg login` again adds another account, or replaces the key of an account that is already saved. A fourth login is refused with the three names; remove one first. A login saved by an older `mg` migrates on first read into an account named by its key prefix (`msk_live_abcd`), and takes its real name the next time a command such as `mg whoami` reaches the API. The file also keeps the active account's key at its top level, so an older `mg` or `mergestorm-mcp` on the same machine keeps signing in as the active account.
|
|
180
189
|
|
|
181
190
|
For headless or CI environments, use `mergestorm login --key` to paste an existing `msk_live_…` key (create one on the dashboard [Settings → API](https://mergestorm.ai/settings#api)). On a TTY the paste is hidden (not echoed); the CLI verifies the key with the API before saving.
|
|
182
191
|
|
|
183
192
|
For an agent, which cannot sit in a prompt while a person signs in, use two steps. `mergestorm login --start` prints the URL and the short code and exits. Give both to the account owner, who opens [mergestorm.ai/cli/auth](https://mergestorm.ai/cli/auth) on any device, signs in, and approves the code. Then `mergestorm login --finish` stores the API key. The code lasts 10 minutes, and `--finish` can be run again if it is interrupted before the approval.
|
|
184
193
|
|
|
194
|
+
### `account`
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
mg account list # saved accounts, the active one marked ● (--json for machines)
|
|
198
|
+
mg account use work # make "work" the active account for every later command
|
|
199
|
+
mg account remove work # log "work" out: revoke its API key and drop it
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Every command runs as the active account. To run one command as another saved account without switching, pass `--account <name>` anywhere on the command line (`mg --account work stack submit`, `mg whoami --account work`), or set `MERGESTORM_ACCOUNT=<name>`; the flag wins over the variable, and both win over the active account. An unknown name stops the command and lists the saved accounts. `MERGESTORM_API_KEY` still overrides every saved account. The `mergestorm-mcp` server reads the same store, so `MERGESTORM_ACCOUNT` in its environment picks the account it serves.
|
|
203
|
+
|
|
185
204
|
### `logout`
|
|
186
205
|
|
|
187
|
-
|
|
206
|
+
Logs out of the active account (or the one named with `--account`): revokes that API key on the server when it can, then drops the account from the store. The next saved account, if any, becomes active. `mg account remove <name>` does the same for a named account.
|
|
188
207
|
|
|
189
208
|
### `review [base] [head]`
|
|
190
209
|
|
|
@@ -233,7 +252,8 @@ Require a logged-in key and API support for `GET /api/v1/me` and `GET /api/v1/re
|
|
|
233
252
|
|
|
234
253
|
| Variable | Purpose | Default |
|
|
235
254
|
|----------|---------|---------|
|
|
236
|
-
| `MERGESTORM_API_KEY` | API key (overrides
|
|
255
|
+
| `MERGESTORM_API_KEY` | API key (overrides every saved account) | — |
|
|
256
|
+
| `MERGESTORM_ACCOUNT` | Saved account to run as; `--account <name>` on a command wins over it | the active account |
|
|
237
257
|
| `MERGESTORM_API_URL` | API base URL | `https://api.mergestorm.ai` |
|
|
238
258
|
| `MERGESTORM_HOME` | Directory for the saved login and local stack state, for machines whose home directory is read-only | `~/.mergestorm` |
|
|
239
259
|
| `NO_COLOR` | Disable ANSI colors | — |
|
package/dist/api.d.ts
CHANGED
|
@@ -30,14 +30,20 @@ export declare function devicePost(base: string, route: string, json: unknown, o
|
|
|
30
30
|
status: number;
|
|
31
31
|
body: any;
|
|
32
32
|
}>;
|
|
33
|
-
|
|
33
|
+
type SurgeUsage = {
|
|
34
34
|
includedMinutes: number;
|
|
35
35
|
usedMinutes: number;
|
|
36
36
|
walletMinutes: number;
|
|
37
37
|
windowStart: string;
|
|
38
38
|
pricePerMinuteUsd: number;
|
|
39
39
|
};
|
|
40
|
+
export type AccountIdentity = {
|
|
41
|
+
github_login: string | null;
|
|
42
|
+
email: string | null;
|
|
43
|
+
};
|
|
44
|
+
export declare function accountLabel(account: AccountIdentity | null | undefined): string | null;
|
|
40
45
|
export type MeResponse = {
|
|
46
|
+
account?: AccountIdentity;
|
|
41
47
|
key: {
|
|
42
48
|
prefix: string;
|
|
43
49
|
name: string;
|
|
@@ -182,13 +188,18 @@ export type SettingsResponse = import("./automation-catalog.js").BearerSettingsV
|
|
|
182
188
|
github_connected: boolean;
|
|
183
189
|
stacks_ready?: boolean;
|
|
184
190
|
};
|
|
185
|
-
export declare const
|
|
191
|
+
export declare const REPO_SETTINGS_SWITCH_KEYS: readonly ["auto_patch_enabled", "auto_review_enabled", "auto_land_default", "merge_queue_batch_enabled", "merge_queue_overdrive", "issue_review_enabled"];
|
|
192
|
+
export declare const REPO_SETTINGS_KEYS: readonly ["auto_patch_enabled", "auto_review_enabled", "auto_land_default", "merge_queue_batch_enabled", "merge_queue_overdrive", "issue_review_enabled", "merge_queue_batch_size", "merge_queue_lands_alone_paths"];
|
|
186
193
|
export type RepoSettingsKey = (typeof REPO_SETTINGS_KEYS)[number];
|
|
194
|
+
type RepoSettingsValues = Record<(typeof REPO_SETTINGS_SWITCH_KEYS)[number], boolean | null> & {
|
|
195
|
+
merge_queue_batch_size: number | null;
|
|
196
|
+
merge_queue_lands_alone_paths: string[] | null;
|
|
197
|
+
};
|
|
187
198
|
export type RepoSettings = {
|
|
188
199
|
owner: string;
|
|
189
200
|
repo: string;
|
|
190
|
-
} &
|
|
191
|
-
export type RepoSettingsPatch = Partial<
|
|
201
|
+
} & RepoSettingsValues;
|
|
202
|
+
export type RepoSettingsPatch = Partial<RepoSettingsValues>;
|
|
192
203
|
export declare function listRepoSettings(cfg?: Config, opts?: {
|
|
193
204
|
timeoutMs?: number;
|
|
194
205
|
}): Promise<RepoSettings[]>;
|
|
@@ -214,6 +225,9 @@ export declare function patchSettings(patch: SettingsPatch, cfg?: Config, opts?:
|
|
|
214
225
|
export declare function getMe(cfg?: Config, opts?: {
|
|
215
226
|
timeoutMs?: number;
|
|
216
227
|
}): Promise<MeResponse | null>;
|
|
228
|
+
export declare function revokeOwnKey(cfg: Config, opts?: {
|
|
229
|
+
timeoutMs?: number;
|
|
230
|
+
}): Promise<boolean>;
|
|
217
231
|
export declare function listJobs(limit?: number, cfg?: Config, opts?: {
|
|
218
232
|
timeoutMs?: number;
|
|
219
233
|
}): Promise<JobListItem[]>;
|
|
@@ -252,16 +266,11 @@ export type StackPolicyPatch = {
|
|
|
252
266
|
export declare function stackPolicyBody(policy: StackPolicyPatch | undefined): StackPolicyPatch;
|
|
253
267
|
/** Adopt an open PR chain (Bearer POST /api/v1/stacks/adopt). */
|
|
254
268
|
export declare function adoptStack(owner: string, repo: string, prNumber: number, cfg?: Config, policy?: StackPolicyPatch): Promise<unknown>;
|
|
255
|
-
export declare const SURGE_REFUSAL_FALLBACK = "Mergestorm Surge is not installed or not ready on this repository. Install it from Agents.";
|
|
256
|
-
export declare function surgeRefusal(body: unknown): CommandError | null;
|
|
257
|
-
export declare const GITHUB_UNREADABLE_MESSAGE = "GitHub could not be read just now; try again.";
|
|
258
269
|
/** Mint or reuse the PR3+ park freeze (Bearer POST /api/v1/stacks/:id/ensure-upper-park). */
|
|
259
270
|
export declare function ensureUpperPark(stackId: string, cfg?: Config): Promise<{
|
|
260
271
|
freezeBranch: string;
|
|
261
272
|
created: boolean;
|
|
262
273
|
}>;
|
|
263
|
-
export declare const SURGE_INSTALL_URL = "https://github.com/apps/mergestorm-surge/installations/new";
|
|
264
|
-
export declare function surgeNotInstalledMessage(owner: string, repo: string, installUrl?: string): string;
|
|
265
274
|
export declare class LandedQueueReadError extends CommandError {
|
|
266
275
|
readonly status: number;
|
|
267
276
|
readonly retryAfterSeconds?: number | undefined;
|
|
@@ -331,7 +340,7 @@ export type StackPublicationRequest = {
|
|
|
331
340
|
};
|
|
332
341
|
autoLand?: boolean;
|
|
333
342
|
};
|
|
334
|
-
|
|
343
|
+
type StackPublicationStack = {
|
|
335
344
|
id: string;
|
|
336
345
|
autoPatchOverride: boolean | null;
|
|
337
346
|
autoLand: boolean;
|
|
@@ -378,7 +387,6 @@ export type StackPublication = {
|
|
|
378
387
|
};
|
|
379
388
|
stack: StackPublicationStack;
|
|
380
389
|
};
|
|
381
|
-
export declare const STACK_PUBLICATION_TIMEOUT_MS = 180000;
|
|
382
390
|
export declare function getStackPublication(cfg: Config, input: {
|
|
383
391
|
owner: string;
|
|
384
392
|
repo: string;
|
|
@@ -396,6 +404,55 @@ export declare function openStackIssue(input: {
|
|
|
396
404
|
url: string;
|
|
397
405
|
created: boolean;
|
|
398
406
|
}>;
|
|
407
|
+
export declare const ISSUE_REVIEW_STATES: readonly ["approved", "needs_answers", "pending", "unreviewed", "capped", "off"];
|
|
408
|
+
export type IssueReviewState = (typeof ISSUE_REVIEW_STATES)[number];
|
|
409
|
+
export type IssueReviewPoint = {
|
|
410
|
+
kind: string;
|
|
411
|
+
text: string;
|
|
412
|
+
};
|
|
413
|
+
export type IssueReviewStatus = {
|
|
414
|
+
owner: string;
|
|
415
|
+
repo: string;
|
|
416
|
+
number: number;
|
|
417
|
+
enabled: boolean;
|
|
418
|
+
state: IssueReviewState;
|
|
419
|
+
approvedBy: "vortex" | "human" | null;
|
|
420
|
+
round: number | null;
|
|
421
|
+
maxRounds: number;
|
|
422
|
+
verdict: "ready" | "ready_with_notes" | "needs_answers" | null;
|
|
423
|
+
summary: string | null;
|
|
424
|
+
points: IssueReviewPoint[];
|
|
425
|
+
commentUrl: string | null;
|
|
426
|
+
next: string;
|
|
427
|
+
};
|
|
428
|
+
export type IssueReviewTarget = {
|
|
429
|
+
owner: string;
|
|
430
|
+
repo: string;
|
|
431
|
+
number: number;
|
|
432
|
+
};
|
|
433
|
+
export declare const ISSUE_REVIEW_POLL_INTERVAL_MS = 10000;
|
|
434
|
+
export declare const ISSUE_REVIEW_DEFAULT_TIMEOUT_MS = 900000;
|
|
435
|
+
export declare function parseIssueReviewStatus(body: unknown, target: IssueReviewTarget): IssueReviewStatus | null;
|
|
436
|
+
export declare function getIssueReview(cfg: Config, target: IssueReviewTarget, opts?: {
|
|
437
|
+
timeoutMs?: number;
|
|
438
|
+
signal?: AbortSignal;
|
|
439
|
+
}): Promise<IssueReviewStatus | null>;
|
|
440
|
+
export declare class IssueReviewTimeoutError extends CommandError {
|
|
441
|
+
readonly lastStatus: IssueReviewStatus;
|
|
442
|
+
constructor(lastStatus: IssueReviewStatus);
|
|
443
|
+
}
|
|
444
|
+
export type PollIssueReviewOptions = {
|
|
445
|
+
timeoutMs?: number;
|
|
446
|
+
intervalMs?: number;
|
|
447
|
+
now?: () => number;
|
|
448
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
449
|
+
signal?: AbortSignal;
|
|
450
|
+
onTick?: (status: IssueReviewStatus | null) => void;
|
|
451
|
+
};
|
|
452
|
+
export declare function pollIssueReview(cfg: Config, target: IssueReviewTarget, opts?: PollIssueReviewOptions): Promise<IssueReviewStatus | null>;
|
|
453
|
+
export declare function issueReviewLines(status: IssueReviewStatus): string[];
|
|
454
|
+
export declare function issueNotApprovedError(issues: IssueReviewStatus[], message?: string | null): CommandError;
|
|
455
|
+
export declare function issueNotApprovedRefusal(status: number, body: unknown, owner: string, repo: string): CommandError | null;
|
|
399
456
|
/**
|
|
400
457
|
* Patch any subset of one owned stack's automation policy
|
|
401
458
|
* (Bearer PATCH /api/v1/stacks/:id). `null` clears an override.
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { apiBase, loadConfig, resolveApiKey } from "./config.js";
|
|
1
|
+
import { apiBase, loadConfig, resolveApiKey, resolveChatId } from "./config.js";
|
|
2
2
|
import { BEARER_SETTINGS_KEYS as SETTINGS_WRITABLE_KEYS, } from "./automation-catalog.js";
|
|
3
3
|
import { CommandError, REVIEW_EXIT, rateLimitedMessage, plainServerText, } from "./errors.js";
|
|
4
4
|
/** Default per-request timeout so a hung API cannot stall the shell/CI forever. */
|
|
@@ -84,8 +84,10 @@ export async function apiFetch(cfg, route, init = {}) {
|
|
|
84
84
|
throw new CommandError("No API key. Run `mergestorm login` (agents: `mergestorm login --start`, then `--finish`) or set MERGESTORM_API_KEY.", 1, "missing_api_key");
|
|
85
85
|
}
|
|
86
86
|
const { json, timeoutMs = DEFAULT_API_TIMEOUT_MS, signal: userSignal, ...rest } = init;
|
|
87
|
+
const chatId = resolveChatId(cfg);
|
|
87
88
|
const headers = {
|
|
88
89
|
Authorization: `Bearer ${key}`,
|
|
90
|
+
...(chatId ? { "x-mergestorm-chat-id": chatId } : {}),
|
|
89
91
|
...rest.headers,
|
|
90
92
|
};
|
|
91
93
|
let body = rest.body;
|
|
@@ -155,6 +157,13 @@ export async function devicePost(base, route, json, opts = {}) {
|
|
|
155
157
|
}
|
|
156
158
|
return { status: res.status, body: parsed };
|
|
157
159
|
}
|
|
160
|
+
export function accountLabel(account) {
|
|
161
|
+
const login = account?.github_login?.trim();
|
|
162
|
+
if (login)
|
|
163
|
+
return login;
|
|
164
|
+
const email = account?.email?.trim();
|
|
165
|
+
return email || null;
|
|
166
|
+
}
|
|
158
167
|
export function isPositiveInteger(value) {
|
|
159
168
|
return typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
160
169
|
}
|
|
@@ -172,7 +181,15 @@ export function applyPrReviewQuery(query, opts) {
|
|
|
172
181
|
* The connected flags are read-only there and deliberately absent here.
|
|
173
182
|
*/
|
|
174
183
|
export { SETTINGS_WRITABLE_KEYS };
|
|
175
|
-
export const
|
|
184
|
+
export const REPO_SETTINGS_SWITCH_KEYS = [
|
|
185
|
+
"auto_patch_enabled",
|
|
186
|
+
"auto_review_enabled",
|
|
187
|
+
"auto_land_default",
|
|
188
|
+
"merge_queue_batch_enabled",
|
|
189
|
+
"merge_queue_overdrive",
|
|
190
|
+
"issue_review_enabled",
|
|
191
|
+
];
|
|
192
|
+
export const REPO_SETTINGS_KEYS = [...REPO_SETTINGS_SWITCH_KEYS, "merge_queue_batch_size", "merge_queue_lands_alone_paths"];
|
|
176
193
|
function repoSettingsFailure(status, body, retryAfterSeconds, action) {
|
|
177
194
|
if (status === 429) {
|
|
178
195
|
return new CommandError(rateLimitedMessage(retryAfterSeconds), REVIEW_EXIT.rate_limited, "rate_limited", {
|
|
@@ -284,6 +301,20 @@ export async function getMe(cfg, opts) {
|
|
|
284
301
|
return null;
|
|
285
302
|
}
|
|
286
303
|
}
|
|
304
|
+
export async function revokeOwnKey(cfg, opts) {
|
|
305
|
+
try {
|
|
306
|
+
const { status } = await apiFetch(cfg, "/api/v1/me/key", {
|
|
307
|
+
method: "DELETE",
|
|
308
|
+
timeoutMs: opts?.timeoutMs,
|
|
309
|
+
});
|
|
310
|
+
return status === 200;
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
if (err instanceof CommandError)
|
|
314
|
+
return err.code === "auth_invalid";
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
287
318
|
export async function listJobs(limit = 10, cfg, opts) {
|
|
288
319
|
const resolved = cfg ?? (await loadConfig());
|
|
289
320
|
const capped = Math.min(Math.max(limit, 1), 50);
|
|
@@ -423,6 +454,9 @@ export async function adoptStack(owner, repo, prNumber, cfg, policy) {
|
|
|
423
454
|
if (status === 429) {
|
|
424
455
|
throw new CommandError(rateLimitedMessage(retryAfterSeconds), REVIEW_EXIT.rate_limited, "rate_limited", { retryAfterSeconds });
|
|
425
456
|
}
|
|
457
|
+
const issueRefusal = issueNotApprovedRefusal(status, body, owner, repo);
|
|
458
|
+
if (issueRefusal)
|
|
459
|
+
throw issueRefusal;
|
|
426
460
|
if (status !== 200) {
|
|
427
461
|
const error = body && typeof body === "object" && "error" in body && typeof body.error === "string"
|
|
428
462
|
? body.error
|
|
@@ -460,13 +494,14 @@ const INFRASTRUCTURE_REFUSAL_CODES = [
|
|
|
460
494
|
"cyclone_not_connected",
|
|
461
495
|
"cyclone_not_installed",
|
|
462
496
|
"cyclone_permission_missing",
|
|
497
|
+
"surge_other_account",
|
|
463
498
|
];
|
|
464
499
|
function refusalReason(body) {
|
|
465
500
|
const reason = (body && typeof body === "object" ? body.reason : undefined);
|
|
466
501
|
return typeof reason === "string" && plainServerText(reason).trim() ? plainServerText(reason).trim() : undefined;
|
|
467
502
|
}
|
|
468
|
-
|
|
469
|
-
|
|
503
|
+
const SURGE_REFUSAL_FALLBACK = "Mergestorm Surge is not installed or not ready on this repository. Install it from Agents.";
|
|
504
|
+
function surgeRefusal(body) {
|
|
470
505
|
if (!body || typeof body !== "object")
|
|
471
506
|
return null;
|
|
472
507
|
const refusal = body;
|
|
@@ -480,7 +515,7 @@ export function surgeRefusal(body) {
|
|
|
480
515
|
const installUrl = githubAppInstallUrl(refusal.installUrl) ?? "";
|
|
481
516
|
return new CommandError(installUrl && !message.includes(installUrl) ? `${message} (${installUrl})` : message, 1, error, { reason });
|
|
482
517
|
}
|
|
483
|
-
|
|
518
|
+
const GITHUB_UNREADABLE_MESSAGE = "GitHub could not be read just now; try again.";
|
|
484
519
|
function serverMessage(message) {
|
|
485
520
|
if (typeof message !== "string")
|
|
486
521
|
return null;
|
|
@@ -509,8 +544,8 @@ export async function ensureUpperPark(stackId, cfg) {
|
|
|
509
544
|
created: body.created === true,
|
|
510
545
|
};
|
|
511
546
|
}
|
|
512
|
-
|
|
513
|
-
|
|
547
|
+
const SURGE_INSTALL_URL = "https://github.com/apps/mergestorm-surge/installations/new";
|
|
548
|
+
function surgeNotInstalledMessage(owner, repo, installUrl = SURGE_INSTALL_URL) {
|
|
514
549
|
return (`Opening PRs needs either the GitHub CLI (gh auth login) or Mergestorm Surge installed on ` +
|
|
515
550
|
`${owner}/${repo} (${installUrl}).`);
|
|
516
551
|
}
|
|
@@ -524,6 +559,9 @@ function stackPullFailure(status, body, owner, repo, failLabel) {
|
|
|
524
559
|
const surge = surgeRefusal(body);
|
|
525
560
|
if (surge)
|
|
526
561
|
return surge;
|
|
562
|
+
const issueRefusal = issueNotApprovedRefusal(status, body, owner, repo);
|
|
563
|
+
if (issueRefusal)
|
|
564
|
+
return issueRefusal;
|
|
527
565
|
const text = `${String(rejection.error ?? "")} ${String(rejection.message ?? "")}`;
|
|
528
566
|
if (rejection.error === "cyclone_not_installed" ||
|
|
529
567
|
/not installed|installation[^\n]*not found/i.test(text)) {
|
|
@@ -590,7 +628,7 @@ export async function openStackPull(cfg, input) {
|
|
|
590
628
|
created: row.created === true,
|
|
591
629
|
};
|
|
592
630
|
}
|
|
593
|
-
|
|
631
|
+
const STACK_PUBLICATION_TIMEOUT_MS = 180_000;
|
|
594
632
|
const PUBLICATION_ERROR_CODES = [
|
|
595
633
|
"head_changed",
|
|
596
634
|
"base_changed",
|
|
@@ -652,6 +690,166 @@ export async function openStackIssue(input, cfg) {
|
|
|
652
690
|
}
|
|
653
691
|
return { number: row.number, url: typeof row.url === "string" ? row.url : "", created: row.created === true };
|
|
654
692
|
}
|
|
693
|
+
export const ISSUE_REVIEW_STATES = ["approved", "needs_answers", "pending", "unreviewed", "capped", "off"];
|
|
694
|
+
export const ISSUE_REVIEW_POLL_INTERVAL_MS = 10_000;
|
|
695
|
+
export const ISSUE_REVIEW_DEFAULT_TIMEOUT_MS = 900_000;
|
|
696
|
+
const ISSUE_REVIEW_MAX_POINTS = 8;
|
|
697
|
+
const ISSUE_REVIEW_DEFAULT_MAX_ROUNDS = 4;
|
|
698
|
+
function issueReviewText(value) {
|
|
699
|
+
if (typeof value !== "string")
|
|
700
|
+
return null;
|
|
701
|
+
const plain = plainServerText(value).trim();
|
|
702
|
+
return plain ? plain : null;
|
|
703
|
+
}
|
|
704
|
+
export function parseIssueReviewStatus(body, target) {
|
|
705
|
+
if (!body || typeof body !== "object")
|
|
706
|
+
return null;
|
|
707
|
+
const row = body;
|
|
708
|
+
const state = ISSUE_REVIEW_STATES.find((known) => known === row.state);
|
|
709
|
+
if (!state)
|
|
710
|
+
return null;
|
|
711
|
+
const points = Array.isArray(row.points)
|
|
712
|
+
? row.points
|
|
713
|
+
.flatMap((point) => {
|
|
714
|
+
const text = issueReviewText(point?.text);
|
|
715
|
+
if (!text)
|
|
716
|
+
return [];
|
|
717
|
+
return [{ kind: issueReviewText(point.kind) ?? "note", text }];
|
|
718
|
+
})
|
|
719
|
+
.slice(0, ISSUE_REVIEW_MAX_POINTS)
|
|
720
|
+
: [];
|
|
721
|
+
const verdict = row.verdict === "ready" || row.verdict === "ready_with_notes" || row.verdict === "needs_answers" ? row.verdict : null;
|
|
722
|
+
return {
|
|
723
|
+
owner: typeof row.owner === "string" && row.owner ? row.owner : target.owner,
|
|
724
|
+
repo: typeof row.repo === "string" && row.repo ? row.repo : target.repo,
|
|
725
|
+
number: isPositiveInteger(row.number) ? row.number : target.number,
|
|
726
|
+
enabled: row.enabled === true,
|
|
727
|
+
state,
|
|
728
|
+
approvedBy: row.approvedBy === "vortex" || row.approvedBy === "human" ? row.approvedBy : null,
|
|
729
|
+
round: isPositiveInteger(row.round) ? row.round : null,
|
|
730
|
+
maxRounds: isPositiveInteger(row.maxRounds) ? row.maxRounds : ISSUE_REVIEW_DEFAULT_MAX_ROUNDS,
|
|
731
|
+
verdict,
|
|
732
|
+
summary: issueReviewText(row.summary),
|
|
733
|
+
points,
|
|
734
|
+
commentUrl: issueReviewText(row.commentUrl),
|
|
735
|
+
next: issueReviewText(row.next) ?? "",
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
export async function getIssueReview(cfg, target, opts = {}) {
|
|
739
|
+
const query = new URLSearchParams({ owner: target.owner, repo: target.repo }).toString();
|
|
740
|
+
const { status, body, retryAfterSeconds } = await apiFetch(cfg, `/api/v1/stacks/issues/${encodeURIComponent(String(target.number))}/review?${query}`, opts);
|
|
741
|
+
if (status === 404)
|
|
742
|
+
return null;
|
|
743
|
+
if (status === 429) {
|
|
744
|
+
throw new CommandError(rateLimitedMessage(retryAfterSeconds), REVIEW_EXIT.rate_limited, "rate_limited", retryAfterSeconds !== undefined ? { retryAfterSeconds } : undefined);
|
|
745
|
+
}
|
|
746
|
+
const parsed = status === 200 ? parseIssueReviewStatus(body, target) : null;
|
|
747
|
+
if (!parsed) {
|
|
748
|
+
const message = serverMessage(body?.message);
|
|
749
|
+
throw new CommandError(message ?? `Failed to read the issue review of ${target.owner}/${target.repo}#${target.number} (HTTP ${status})`, 1, status >= 500 ? "issue_review_unavailable" : undefined);
|
|
750
|
+
}
|
|
751
|
+
return parsed;
|
|
752
|
+
}
|
|
753
|
+
export class IssueReviewTimeoutError extends CommandError {
|
|
754
|
+
lastStatus;
|
|
755
|
+
constructor(lastStatus) {
|
|
756
|
+
super(`Timed out waiting for the Vortex review of ${lastStatus.owner}/${lastStatus.repo}#${lastStatus.number}. Run \`mergestorm issue wait ${lastStatus.number}\` again.`, REVIEW_EXIT.timeout, "review_timeout");
|
|
757
|
+
this.name = "IssueReviewTimeoutError";
|
|
758
|
+
this.lastStatus = lastStatus;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function abortableSleep(ms, signal) {
|
|
762
|
+
return new Promise((resolve, reject) => {
|
|
763
|
+
const abortError = () => Object.assign(new Error("Aborted"), { name: "AbortError" });
|
|
764
|
+
if (signal?.aborted) {
|
|
765
|
+
reject(abortError());
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
const onAbort = () => {
|
|
769
|
+
clearTimeout(timer);
|
|
770
|
+
reject(abortError());
|
|
771
|
+
};
|
|
772
|
+
const timer = setTimeout(() => {
|
|
773
|
+
signal?.removeEventListener("abort", onAbort);
|
|
774
|
+
resolve();
|
|
775
|
+
}, ms);
|
|
776
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
function isTransientIssueReviewError(err) {
|
|
780
|
+
if (!(err instanceof Error) || err.name === "AbortError")
|
|
781
|
+
return false;
|
|
782
|
+
if (err instanceof CommandError) {
|
|
783
|
+
return err.code === "api_timeout" || err.code === "issue_review_unavailable" || err.code === "rate_limited";
|
|
784
|
+
}
|
|
785
|
+
return /fetch failed|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|socket hang up|network/i.test(err.message);
|
|
786
|
+
}
|
|
787
|
+
export async function pollIssueReview(cfg, target, opts = {}) {
|
|
788
|
+
const timeoutMs = opts.timeoutMs ?? ISSUE_REVIEW_DEFAULT_TIMEOUT_MS;
|
|
789
|
+
const intervalMs = opts.intervalMs ?? ISSUE_REVIEW_POLL_INTERVAL_MS;
|
|
790
|
+
const now = opts.now ?? Date.now;
|
|
791
|
+
const wait = opts.sleep ?? abortableSleep;
|
|
792
|
+
const deadline = now() + timeoutMs;
|
|
793
|
+
let last = null;
|
|
794
|
+
let failure = null;
|
|
795
|
+
for (;;) {
|
|
796
|
+
try {
|
|
797
|
+
const status = await getIssueReview(cfg, target, { signal: opts.signal });
|
|
798
|
+
if (!status || status.state !== "pending")
|
|
799
|
+
return status;
|
|
800
|
+
last = status;
|
|
801
|
+
opts.onTick?.(status);
|
|
802
|
+
}
|
|
803
|
+
catch (err) {
|
|
804
|
+
if (!isTransientIssueReviewError(err) || now() >= deadline)
|
|
805
|
+
throw err;
|
|
806
|
+
failure = err;
|
|
807
|
+
opts.onTick?.(null);
|
|
808
|
+
}
|
|
809
|
+
const remaining = deadline - now();
|
|
810
|
+
if (remaining <= 0) {
|
|
811
|
+
if (last)
|
|
812
|
+
throw new IssueReviewTimeoutError(last);
|
|
813
|
+
throw failure;
|
|
814
|
+
}
|
|
815
|
+
await wait(Math.min(intervalMs, remaining), opts.signal);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
export function issueReviewLines(status) {
|
|
819
|
+
const round = status.round ? ` (round ${status.round} of ${status.maxRounds})` : "";
|
|
820
|
+
const lines = [`${status.owner}/${status.repo}#${status.number}: ${status.state}${round}`];
|
|
821
|
+
if (status.summary)
|
|
822
|
+
lines.push(` ${status.summary}`);
|
|
823
|
+
status.points.forEach((point, i) => lines.push(` ${i + 1}. [${point.kind}] ${point.text}`));
|
|
824
|
+
if (status.commentUrl)
|
|
825
|
+
lines.push(` Review: ${status.commentUrl}`);
|
|
826
|
+
if (status.next)
|
|
827
|
+
lines.push(` Next: ${status.next}`);
|
|
828
|
+
return lines;
|
|
829
|
+
}
|
|
830
|
+
export function issueNotApprovedError(issues, message) {
|
|
831
|
+
const lines = [message?.trim() || "Vortex has not approved the linked issue yet; refusing to open the pull request."];
|
|
832
|
+
for (const issue of issues) {
|
|
833
|
+
lines.push(...issueReviewLines(issue));
|
|
834
|
+
lines.push(` Reply in a comment on #${issue.number}, then run \`mergestorm issue wait ${issue.number}\`.`);
|
|
835
|
+
}
|
|
836
|
+
return new CommandError(lines.join("\n"), REVIEW_EXIT.failed, "issue_not_approved");
|
|
837
|
+
}
|
|
838
|
+
export function issueNotApprovedRefusal(status, body, owner, repo) {
|
|
839
|
+
if (status !== 409 || !body || typeof body !== "object")
|
|
840
|
+
return null;
|
|
841
|
+
const refusal = body;
|
|
842
|
+
if (refusal.error !== "issue_not_approved")
|
|
843
|
+
return null;
|
|
844
|
+
const issues = Array.isArray(refusal.issues)
|
|
845
|
+
? refusal.issues.flatMap((issue) => {
|
|
846
|
+
const n = issue?.number;
|
|
847
|
+
const parsed = parseIssueReviewStatus(issue, { owner, repo, number: isPositiveInteger(n) ? n : 0 });
|
|
848
|
+
return parsed ? [parsed] : [];
|
|
849
|
+
})
|
|
850
|
+
: [];
|
|
851
|
+
return issueNotApprovedError(issues, serverMessage(refusal.message));
|
|
852
|
+
}
|
|
655
853
|
async function stackMutation(stackId, pathSuffix, method, json, failLabel, cfg) {
|
|
656
854
|
const resolved = cfg ?? (await loadConfig());
|
|
657
855
|
const { status, body } = await apiFetch(resolved, `/api/v1/stacks/${encodeURIComponent(stackId)}${pathSuffix}`, method === "PATCH"
|
|
@@ -42,6 +42,10 @@ export declare const BEARER_SETTINGS: readonly [{
|
|
|
42
42
|
readonly key: "vortex_seam_specialist_enabled";
|
|
43
43
|
readonly flag: "--vortex-seam";
|
|
44
44
|
readonly label: "Vortex seam specialist";
|
|
45
|
+
}, {
|
|
46
|
+
readonly key: "issue_review_enabled";
|
|
47
|
+
readonly flag: "--issue-review";
|
|
48
|
+
readonly label: "Vortex issue reviews";
|
|
45
49
|
}, {
|
|
46
50
|
readonly key: "auto_land_default";
|
|
47
51
|
readonly flag: "--auto-land";
|
|
@@ -63,7 +67,17 @@ export declare const BEARER_SETTINGS: readonly [{
|
|
|
63
67
|
readonly label: "Merge queue batch size (pull requests)";
|
|
64
68
|
readonly kind: "count";
|
|
65
69
|
readonly min: 2;
|
|
66
|
-
readonly max:
|
|
70
|
+
readonly max: 10;
|
|
71
|
+
}, {
|
|
72
|
+
readonly key: "merge_queue_lands_alone_paths";
|
|
73
|
+
readonly flag: "--merge-queue-lands-alone";
|
|
74
|
+
readonly label: "Merge queue lands-alone paths";
|
|
75
|
+
readonly kind: "paths";
|
|
76
|
+
readonly defaults: readonly ["**/migrations/**", "db/migrate/**", ".github/workflows/**"];
|
|
77
|
+
}, {
|
|
78
|
+
readonly key: "merge_queue_overdrive";
|
|
79
|
+
readonly flag: "--merge-queue-overdrive";
|
|
80
|
+
readonly label: "Merge queue overdrive (up to 10 per batch)";
|
|
67
81
|
}, {
|
|
68
82
|
readonly key: "ignored_bot_logins";
|
|
69
83
|
readonly flag: "--ignore-bot";
|
|
@@ -95,11 +109,11 @@ export declare const BEARER_SETTINGS_LABELS: Record<BearerSettingsKey, string>;
|
|
|
95
109
|
/** Omitted kind means boolean for the original toggle rows. */
|
|
96
110
|
export type BearerSettingsValues = {
|
|
97
111
|
[Row in (typeof BEARER_SETTINGS)[number] as Row["key"]]: Row extends {
|
|
98
|
-
kind: "logins";
|
|
112
|
+
kind: "logins" | "paths";
|
|
99
113
|
} ? string[] : Row extends {
|
|
100
114
|
kind: "seconds" | "count";
|
|
101
115
|
} ? number : Row extends {
|
|
102
116
|
values: readonly (infer Value)[];
|
|
103
117
|
} ? Value : boolean;
|
|
104
118
|
};
|
|
105
|
-
export declare const BEARER_BOOLEAN_SETTINGS_KEYS: ("auto_review_enabled" | "auto_patch_enabled" | "auto_resolve_conflicts_enabled" | "auto_fix_ci_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "auto_land_default" | "auto_land_settle_seconds" | "merge_queue_batch_enabled" | "merge_queue_batch_size" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check")[];
|
|
119
|
+
export declare const BEARER_BOOLEAN_SETTINGS_KEYS: ("auto_review_enabled" | "auto_patch_enabled" | "auto_resolve_conflicts_enabled" | "auto_fix_ci_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "issue_review_enabled" | "auto_land_default" | "auto_land_settle_seconds" | "merge_queue_batch_enabled" | "merge_queue_batch_size" | "merge_queue_lands_alone_paths" | "merge_queue_overdrive" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check")[];
|
|
@@ -10,10 +10,13 @@ export const BEARER_SETTINGS = [
|
|
|
10
10
|
{ key: "vortex_auto_overflow_enabled", flag: "--vortex-auto-overflow", label: "Vortex auto overflow" },
|
|
11
11
|
{ key: "vortex_skip_all_clear_comments", flag: "--vortex-skip-all-clear", label: "Vortex skip All-clear comments" },
|
|
12
12
|
{ key: "vortex_seam_specialist_enabled", flag: "--vortex-seam", label: "Vortex seam specialist" },
|
|
13
|
+
{ key: "issue_review_enabled", flag: "--issue-review", label: "Vortex issue reviews" },
|
|
13
14
|
{ key: "auto_land_default", flag: "--auto-land", label: "Auto land (new stacks)" },
|
|
14
15
|
{ key: "auto_land_settle_seconds", flag: "--auto-land-settle", label: "Auto land settle (seconds)", kind: "seconds", min: 15, max: 300 },
|
|
15
16
|
{ key: "merge_queue_batch_enabled", flag: "--merge-queue-batch", label: "Merge queue batching" },
|
|
16
|
-
{ key: "merge_queue_batch_size", flag: "--merge-queue-batch-size", label: "Merge queue batch size (pull requests)", kind: "count", min: 2, max:
|
|
17
|
+
{ key: "merge_queue_batch_size", flag: "--merge-queue-batch-size", label: "Merge queue batch size (pull requests)", kind: "count", min: 2, max: 10 },
|
|
18
|
+
{ key: "merge_queue_lands_alone_paths", flag: "--merge-queue-lands-alone", label: "Merge queue lands-alone paths", kind: "paths", defaults: ["**/migrations/**", "db/migrate/**", ".github/workflows/**"] },
|
|
19
|
+
{ key: "merge_queue_overdrive", flag: "--merge-queue-overdrive", label: "Merge queue overdrive (up to 10 per batch)" },
|
|
17
20
|
{ key: "ignored_bot_logins", flag: "--ignore-bot", label: "Ignored bot logins", kind: "logins" },
|
|
18
21
|
{ key: "vortex_bot_skip_check", flag: "--vortex-skip-check", label: "Vortex bot skip check", kind: "enum", values: ["none", "neutral"] },
|
|
19
22
|
{ key: "vortex_findings_check", flag: "--vortex-findings", label: "Vortex findings check", kind: "enum", values: ["failure", "neutral", "success"] },
|