@devflow-one/cli 0.1.15 → 0.1.17
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 +46 -12
- package/package.json +1 -1
- package/src/cli.js +6 -14
- package/src/client.js +7 -2
- package/src/commands.js +98 -37
package/README.md
CHANGED
|
@@ -182,9 +182,17 @@ devflow mr create "Title" feat/x --base release/2026.4 --project ABC
|
|
|
182
182
|
# stamped with. Omitted or unrecognized, it is auto-selected by matching configured
|
|
183
183
|
# type names against the title's conventional prefix, branch prefix, Jira issue type
|
|
184
184
|
# and title keywords, falling back to the first type. Pass its id or label to pick one.
|
|
185
|
+
# The template (its own {prMedia} uploads media) writes the description; --body is the
|
|
186
|
+
# fallback, where {prMedia} is filled or removed.
|
|
185
187
|
devflow mr create "Title" feat/x --type Bugfix --project ABC
|
|
186
188
|
devflow mr types --project ABC # configured types: id, label, default flag
|
|
187
189
|
|
|
190
|
+
# ready for review: also applies the PR type's labels, assigns you when unassigned and
|
|
191
|
+
# requests the type's missing reviewers (additive, best-effort). --type switches the
|
|
192
|
+
# type by swapping its labels; to refresh this later, run `mr draft` then `mr ready` again
|
|
193
|
+
devflow mr ready 42 --project ABC
|
|
194
|
+
devflow mr ready 42 --type Bugfix --project ABC
|
|
195
|
+
|
|
188
196
|
# issue key: parsed from the head branch by default; --issue sets it explicitly
|
|
189
197
|
# ({parentWithChildIssueKeys}/{issueKey}/{issueTitle} template tokens) for a branch without one.
|
|
190
198
|
# {issueTitle} needs the Jira API token set in Settings, else it renders empty.
|
|
@@ -198,51 +206,77 @@ devflow mr find ABC-123
|
|
|
198
206
|
devflow pipeline status 42 deadbeef --issue ABC-9
|
|
199
207
|
|
|
200
208
|
# commit a managed worktree with the project's commit-message template; it stages tracked
|
|
201
|
-
# changes only (add --include-untracked for new files)
|
|
202
|
-
devflow commit template /wt/ABC-123 --values '{"summary":"fix login"}' --project ABC
|
|
209
|
+
# changes only (add --include-untracked for new files); pushing is destructive
|
|
210
|
+
devflow commit template /wt/ABC-123 --values '{"summary":"fix login"}' --project ABC
|
|
203
211
|
devflow commit template /wt/ABC-123 --no-push --project ABC # commit locally, skip push
|
|
204
212
|
|
|
205
213
|
# read a PR/MR's title + description, then edit it: --title/--description replace,
|
|
206
214
|
# --append-description adds text after a `---` rule (skipped if already at the end);
|
|
207
|
-
# --base retargets the PR/MR to an existing remote branch
|
|
215
|
+
# --base retargets the PR/MR to an existing remote branch; `{prMedia}` in the description
|
|
216
|
+
# uploads .devflow/media/<ISSUE-KEY>[-n].{mp4,png,jpg,jpeg} and is replaced with the links
|
|
217
|
+
# (removed when the upload fails or the head branch names no issue; media.error says why)
|
|
208
218
|
devflow mr get 42 --project ABC
|
|
209
219
|
devflow mr update 42 --append-description "## Test plan" --project ABC
|
|
210
220
|
devflow mr update 42 --title "Fix login redirect" --project ABC
|
|
211
221
|
devflow mr update 42 --base release/2026.4 --project ABC
|
|
222
|
+
devflow mr update 42 --description "$(cat body.md)" --project ABC # body.md has {prMedia}
|
|
212
223
|
|
|
213
224
|
# assembled fix-review-comments prompt (read-only; runs no AI, makes no writes)
|
|
214
225
|
devflow mr fix-prompt 42 # issue key inferred from the branch
|
|
215
226
|
|
|
227
|
+
# issue status and the repo card's branches and PRs/MRs (read-only; the issue key is
|
|
228
|
+
# inferred from the branch). Status needs the Jira API token set in Settings.
|
|
229
|
+
devflow issue status # the issue's and its parent's status
|
|
230
|
+
devflow issue branches ABC-123 --project ABC
|
|
231
|
+
devflow issue prs # every state, merged and closed included
|
|
232
|
+
|
|
233
|
+
# review comments: counts, then "Reply as Fixed" or an AI "Fix Comments" run, which
|
|
234
|
+
# commits and pushes (so it is destructive). It returns at once: `started`, `prNumber` and
|
|
235
|
+
# `queued` (true when an automation run is busy, so the fix starts on its next tick), or
|
|
236
|
+
# `started: false` with a `reason` — e.g. while the panel or the timer is fixing that PR
|
|
237
|
+
devflow mr comments 42 # unfixed / fixed / resolved / prComments
|
|
238
|
+
devflow mr reply-fixed 42 # success / failed / resolved counts
|
|
239
|
+
devflow mr fix-comments 42 # then re-check the counts
|
|
240
|
+
|
|
216
241
|
# notify the project's configured Slack channel(s)
|
|
217
242
|
devflow slack notify "Review fixes ready on {pr}" --values '{"pr":"#42"}' --project ABC
|
|
218
243
|
devflow slack channels --project ABC # configured channels (read-only, no credentials)
|
|
219
244
|
|
|
245
|
+
# reviewer notify: per channel, were reviewers already notified, and is the next step a
|
|
246
|
+
# send or a ping? `source` says who posted: manual (panel), auto (auto-send), gateway
|
|
247
|
+
# (`slack notify --pr`) or slack (found only in Slack history). `--pr` records the post
|
|
248
|
+
# and threads a ping under the earlier message
|
|
249
|
+
devflow mr notify-status 42
|
|
250
|
+
devflow slack notify "Please review #42" --channel C123 --pr 42
|
|
251
|
+
|
|
220
252
|
# force a specific profile when a key is ambiguous
|
|
221
253
|
devflow mr ready 12 --profile prof-1
|
|
222
254
|
```
|
|
223
255
|
|
|
224
256
|
### Destructive actions
|
|
225
257
|
|
|
226
|
-
`branch delete`, `mr close`
|
|
227
|
-
|
|
228
|
-
|
|
258
|
+
`branch delete`, `mr close`, `mr merge`, `mr fix-comments` (its AI run commits and
|
|
259
|
+
pushes) and `commit template` (unless `--no-push`) are destructive. By default they
|
|
260
|
+
run without confirmation. Turn on **Require confirmation for destructive CLI/MCP
|
|
261
|
+
actions** in a repo's DevFlow Git settings, and that repo then refuses them with
|
|
262
|
+
`DESTRUCTIVE_BLOCKED` unless you confirm with `--force` (or `--yes`). The extension
|
|
263
|
+
enforces the setting, so it can't be bypassed:
|
|
229
264
|
|
|
230
265
|
```sh
|
|
231
|
-
devflow branch delete old --project ABC # refused
|
|
232
|
-
devflow branch delete old --project ABC --force #
|
|
266
|
+
devflow branch delete old --project ABC # runs; refused if confirmation is on
|
|
267
|
+
devflow branch delete old --project ABC --force # always runs
|
|
233
268
|
|
|
234
269
|
# merge a PR/MR, squashing and cleaning up the source branch
|
|
235
270
|
devflow mr merge 42 --method squash --delete-source-branch --force
|
|
236
271
|
```
|
|
237
272
|
|
|
238
|
-
Inference never
|
|
239
|
-
|
|
240
|
-
to give explicitly, precisely because inference makes mis-targeting easier.
|
|
273
|
+
Inference never confirms — even with fully inferred routing, only an explicit
|
|
274
|
+
`--force` counts, precisely because inference makes mis-targeting easier.
|
|
241
275
|
|
|
242
276
|
## Verify
|
|
243
277
|
|
|
244
278
|
```sh
|
|
245
|
-
npm test # unit: arg parsing, command resolution, routing, destructive
|
|
279
|
+
npm test # unit: arg parsing, command resolution, routing, destructive confirmation
|
|
246
280
|
```
|
|
247
281
|
|
|
248
282
|
## Releasing
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -35,9 +35,9 @@ below is an override, applied ahead of anything inferred:
|
|
|
35
35
|
|
|
36
36
|
Run \`devflow where\` to see what the current directory resolves to.
|
|
37
37
|
|
|
38
|
-
Destructive actions (branch delete, mr close):
|
|
39
|
-
--force, --yes Confirm a destructive action
|
|
40
|
-
|
|
38
|
+
Destructive actions (branch delete, mr close, mr merge, pushing commits):
|
|
39
|
+
--force, --yes Confirm a destructive action. Needed only when confirmation
|
|
40
|
+
is enabled for the repo in DevFlow settings. Never inferred.
|
|
41
41
|
|
|
42
42
|
Output:
|
|
43
43
|
--json Print machine-readable JSON (envelope) for scripts.
|
|
@@ -199,16 +199,8 @@ export async function run(argv, io = {}) {
|
|
|
199
199
|
|
|
200
200
|
assertRoutable(key, command, context, flags);
|
|
201
201
|
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
const confirmed = Boolean(flags.force || flags.yes);
|
|
205
|
-
const destructive = isDestructiveCommand(command, flags);
|
|
206
|
-
|
|
207
|
-
if (destructive && !confirmed) {
|
|
208
|
-
throw new UsageError(
|
|
209
|
-
`"${key}" is destructive — re-run with --force (or --yes) to confirm. Usage: ${command.usage}`,
|
|
210
|
-
);
|
|
211
|
-
}
|
|
202
|
+
// Only an explicit --force/--yes confirms; inference makes mis-targeting easier.
|
|
203
|
+
const confirmed = Boolean(flags.force || flags.yes) && isDestructiveCommand(command, flags);
|
|
212
204
|
|
|
213
205
|
// The owning profile is known but closed: say so by name rather than letting
|
|
214
206
|
// the gateway answer with a bare NO_PROFILE.
|
|
@@ -220,7 +212,7 @@ export async function run(argv, io = {}) {
|
|
|
220
212
|
const args = command.buildArgs ? command.buildArgs(rest, flags, context) : [];
|
|
221
213
|
const body = {
|
|
222
214
|
...buildRoutingBody(context),
|
|
223
|
-
...(
|
|
215
|
+
...(confirmed ? { allowDestructive: true } : {}),
|
|
224
216
|
payload: {
|
|
225
217
|
projectKey: projectKeyFrom(context),
|
|
226
218
|
...(context.repo ? { repo: context.repo } : {}),
|
package/src/client.js
CHANGED
|
@@ -27,13 +27,18 @@ export class GatewayError extends Error {
|
|
|
27
27
|
export const RETRY_HINT =
|
|
28
28
|
"The action may have completed — verify (e.g. findPrByIssueKey) before retrying.";
|
|
29
29
|
|
|
30
|
+
export const DESTRUCTIVE_HINT =
|
|
31
|
+
"Confirmation is enabled in DevFlow settings for this repo — re-run with --force (or --yes).";
|
|
32
|
+
|
|
30
33
|
/**
|
|
31
|
-
*
|
|
32
|
-
* that may already have run; undefined when a retry is safe.
|
|
34
|
+
* How to retry a failed call: confirm a DESTRUCTIVE_BLOCKED action, or verify a
|
|
35
|
+
* retry-unsafe TIMEOUT that may already have run; undefined when a retry is safe.
|
|
33
36
|
* @param {unknown} err
|
|
34
37
|
* @param {boolean} [retryUnsafe]
|
|
35
38
|
*/
|
|
36
39
|
export function retryHint(err, retryUnsafe) {
|
|
40
|
+
if (err instanceof GatewayError && err.code === "DESTRUCTIVE_BLOCKED") return DESTRUCTIVE_HINT;
|
|
41
|
+
|
|
37
42
|
const mayHaveRun =
|
|
38
43
|
err instanceof GatewayError && err.code === "TIMEOUT" && err.delivered !== false;
|
|
39
44
|
|
package/src/commands.js
CHANGED
|
@@ -123,8 +123,8 @@ export class UsageError extends Error {
|
|
|
123
123
|
* @property {"GET" | "POST" | "LOCAL"} method `LOCAL` runs in-process, no action call.
|
|
124
124
|
* @property {string} [path] For GET commands.
|
|
125
125
|
* @property {string} [action] Gateway action name for POST commands.
|
|
126
|
-
* @property {boolean | ((flags: Record<string, unknown>) => boolean)} [destructive]
|
|
127
|
-
* --force/--yes
|
|
126
|
+
* @property {boolean | ((flags: Record<string, unknown>) => boolean)} [destructive] Accepts
|
|
127
|
+
* --force/--yes as confirmation; a predicate when that depends on the flags.
|
|
128
128
|
* @property {boolean} [retryUnsafe] A retry may repeat its side effect; a TIMEOUT warns to verify first.
|
|
129
129
|
* @property {(pos: string[], flags: Record<string, unknown>, context?: object) => unknown[]} [buildArgs]
|
|
130
130
|
* @property {string} usage One-line usage string.
|
|
@@ -182,7 +182,7 @@ const COMMANDS = {
|
|
|
182
182
|
retryUnsafe: true,
|
|
183
183
|
action: "deleteBranch",
|
|
184
184
|
destructive: true,
|
|
185
|
-
usage: "branch delete <branchName> [--issue KEY] [--project KEY] --force",
|
|
185
|
+
usage: "branch delete <branchName> [--issue KEY] [--project KEY] [--force]",
|
|
186
186
|
buildArgs: ([branchName]) => {
|
|
187
187
|
if (!branchName) throw new UsageError("branch delete requires <branchName>.");
|
|
188
188
|
|
|
@@ -198,7 +198,7 @@ const COMMANDS = {
|
|
|
198
198
|
destructive: (flags) => !flags["no-push"],
|
|
199
199
|
usage:
|
|
200
200
|
'commit template <worktreePath> [--values \'{"key":"val"}\'] [--include-untracked]' +
|
|
201
|
-
" [--issue KEY] [--project KEY]
|
|
201
|
+
" [--issue KEY] [--project KEY] [--no-push | --force]" +
|
|
202
202
|
" (stages every modified/deleted tracked file and pushes, refused from the base branch;" +
|
|
203
203
|
" untracked files are refused unless --include-untracked;" +
|
|
204
204
|
" --values keys fill the project's commit template, default" +
|
|
@@ -218,6 +218,32 @@ const COMMANDS = {
|
|
|
218
218
|
},
|
|
219
219
|
},
|
|
220
220
|
|
|
221
|
+
"issue status": {
|
|
222
|
+
method: "POST",
|
|
223
|
+
action: "getIssueStatus",
|
|
224
|
+
usage:
|
|
225
|
+
"issue status [issueKey] [--issue KEY] [--project KEY]" +
|
|
226
|
+
" (the issue's and its parent's Jira status; needs the Jira API token source; read-only)",
|
|
227
|
+
buildArgs: issueKeyArgs("issue status"),
|
|
228
|
+
},
|
|
229
|
+
"issue branches": {
|
|
230
|
+
method: "POST",
|
|
231
|
+
action: "listIssueBranches",
|
|
232
|
+
usage:
|
|
233
|
+
"issue branches [issueKey] [--issue KEY] [--project KEY]" +
|
|
234
|
+
" (each repo's remote branches for the issue and its parent, as the repo card lists them; read-only)",
|
|
235
|
+
buildArgs: issueKeyArgs("issue branches"),
|
|
236
|
+
},
|
|
237
|
+
"issue prs": {
|
|
238
|
+
method: "POST",
|
|
239
|
+
action: "listIssuePullRequests",
|
|
240
|
+
usage:
|
|
241
|
+
"issue prs [issueKey] [--issue KEY] [--project KEY]" +
|
|
242
|
+
" (each repo's PRs/MRs for the issue in every state, merged and closed included, with" +
|
|
243
|
+
" approvers on open ones; read-only)",
|
|
244
|
+
buildArgs: issueKeyArgs("issue prs"),
|
|
245
|
+
},
|
|
246
|
+
|
|
221
247
|
"mr create": {
|
|
222
248
|
method: "POST",
|
|
223
249
|
retryUnsafe: true,
|
|
@@ -228,7 +254,8 @@ const COMMANDS = {
|
|
|
228
254
|
" (a headBranch that exists on the remote is used as is; otherwise the repo's branch" +
|
|
229
255
|
" template picks the head branch from the issue; --base defaults to the repo's configured base branch, expanding a {year}/{number} pattern;" +
|
|
230
256
|
" --type is auto-selected by matching PR type names against the title prefix, branch prefix," +
|
|
231
|
-
" Jira issue type and title keywords, falling back to the first configured type
|
|
257
|
+
" Jira issue type and title keywords, falling back to the first configured type;" +
|
|
258
|
+
" the description comes from the PR type template, --body is the fallback where {prMedia} is filled or removed)",
|
|
232
259
|
buildArgs: ([title, headBranch], flags, context = {}) => {
|
|
233
260
|
const issueKey = templateIssueKey(headBranch, flags, context);
|
|
234
261
|
|
|
@@ -253,8 +280,18 @@ const COMMANDS = {
|
|
|
253
280
|
"mr ready": {
|
|
254
281
|
method: "POST",
|
|
255
282
|
action: "markPrReadyForReview",
|
|
256
|
-
usage:
|
|
257
|
-
|
|
283
|
+
usage:
|
|
284
|
+
"mr ready <prId> [--type <idOrLabel>] [--issue KEY] [--project KEY]" +
|
|
285
|
+
" (also applies the PR type's labels, assigns you when unassigned and requests the" +
|
|
286
|
+
" type's missing reviewers; --type switches the type by swapping its labels;" +
|
|
287
|
+
" run `mr draft` then `mr ready` again to refresh them)",
|
|
288
|
+
buildArgs: ([prId], flags) => {
|
|
289
|
+
const id = toInt("prId", prId);
|
|
290
|
+
const prType = toText("type", flags.type);
|
|
291
|
+
|
|
292
|
+
// An omitted prType is left out (not sent as null) so the gateway keeps the current type.
|
|
293
|
+
return prType === undefined ? [id] : [id, prType];
|
|
294
|
+
},
|
|
258
295
|
},
|
|
259
296
|
"mr draft": {
|
|
260
297
|
method: "POST",
|
|
@@ -267,7 +304,7 @@ const COMMANDS = {
|
|
|
267
304
|
retryUnsafe: true,
|
|
268
305
|
action: "closePullRequest",
|
|
269
306
|
destructive: true,
|
|
270
|
-
usage: "mr close <prId> [--issue KEY] [--project KEY] --force",
|
|
307
|
+
usage: "mr close <prId> [--issue KEY] [--project KEY] [--force]",
|
|
271
308
|
buildArgs: ([prId]) => [toInt("prId", prId)],
|
|
272
309
|
},
|
|
273
310
|
"mr merge": {
|
|
@@ -277,7 +314,7 @@ const COMMANDS = {
|
|
|
277
314
|
destructive: true,
|
|
278
315
|
usage:
|
|
279
316
|
"mr merge <prId> [--method merge|squash|rebase] [--delete-source-branch] " +
|
|
280
|
-
"[--issue KEY] [--project KEY] --force",
|
|
317
|
+
"[--issue KEY] [--project KEY] [--force]",
|
|
281
318
|
buildArgs: ([prId], flags) => {
|
|
282
319
|
const mergeMethod = flags.method === undefined ? undefined : toMergeMethod(flags.method);
|
|
283
320
|
const deleteSourceBranch = flags["delete-source-branch"] ? true : undefined;
|
|
@@ -295,30 +332,6 @@ const COMMANDS = {
|
|
|
295
332
|
return [toInt("prId", prId), assignee];
|
|
296
333
|
},
|
|
297
334
|
},
|
|
298
|
-
"mr labels": {
|
|
299
|
-
method: "POST",
|
|
300
|
-
action: "addPrLabels",
|
|
301
|
-
usage: "mr labels <prId> <label...> [--issue KEY] [--project KEY]",
|
|
302
|
-
buildArgs: ([prId, ...labels]) => {
|
|
303
|
-
if (labels.length === 0) {
|
|
304
|
-
throw new UsageError("mr labels requires <prId> and at least one <label>.");
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return [toInt("prId", prId), labels];
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
"mr reviewers": {
|
|
311
|
-
method: "POST",
|
|
312
|
-
action: "requestPrReviewers",
|
|
313
|
-
usage: "mr reviewers <prId> <reviewer...> [--issue KEY] [--project KEY]",
|
|
314
|
-
buildArgs: ([prId, ...reviewers]) => {
|
|
315
|
-
if (reviewers.length === 0) {
|
|
316
|
-
throw new UsageError("mr reviewers requires <prId> and at least one <reviewer>.");
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return [toInt("prId", prId), reviewers];
|
|
320
|
-
},
|
|
321
|
-
},
|
|
322
335
|
"mr find": {
|
|
323
336
|
method: "POST",
|
|
324
337
|
action: "findPrByIssueKey",
|
|
@@ -341,7 +354,10 @@ const COMMANDS = {
|
|
|
341
354
|
" [--base <branch>] [--issue KEY] [--project KEY]" +
|
|
342
355
|
" (--title/--description replace; --append-description adds text after a `---` rule" +
|
|
343
356
|
" and is skipped if it already ends the description after that rule; --base retargets" +
|
|
344
|
-
" the PR/MR to an existing remote branch
|
|
357
|
+
" the PR/MR to an existing remote branch; `{prMedia}` in --description or" +
|
|
358
|
+
" --append-description uploads .devflow/media/<ISSUE-KEY>[-n].{mp4,png,jpg,jpeg} and is" +
|
|
359
|
+
" replaced with the links, so swap old media links for `{prMedia}` to refresh them." +
|
|
360
|
+
" It is removed if the upload fails or the head branch names no issue; media.error says why)",
|
|
345
361
|
buildArgs: ([prId], flags) => {
|
|
346
362
|
const title = toText("title", flags.title);
|
|
347
363
|
const body = toRawText("description", flags.description);
|
|
@@ -377,6 +393,46 @@ const COMMANDS = {
|
|
|
377
393
|
return [parsedPrId, context.issueKey];
|
|
378
394
|
},
|
|
379
395
|
},
|
|
396
|
+
"mr comments": {
|
|
397
|
+
method: "POST",
|
|
398
|
+
action: "getPrReviewCommentCounts",
|
|
399
|
+
usage:
|
|
400
|
+
"mr comments <prId> [--issue KEY] [--project KEY]" +
|
|
401
|
+
" (unfixed / fixed / resolved review-thread and PR-level comment counts; read-only)",
|
|
402
|
+
buildArgs: ([prId]) => [toInt("prId", prId)],
|
|
403
|
+
},
|
|
404
|
+
"mr notify-status": {
|
|
405
|
+
method: "POST",
|
|
406
|
+
action: "getReviewerNotifyStatus",
|
|
407
|
+
usage:
|
|
408
|
+
"mr notify-status <prId> [--issue KEY] [--project KEY]" +
|
|
409
|
+
" (per reviewer channel: whether reviewers were already notified on Slack and whether the" +
|
|
410
|
+
" next step is a send or a ping, and who posted; follow up with `slack notify` for that" +
|
|
411
|
+
" PR; read-only)",
|
|
412
|
+
buildArgs: ([prId]) => [toInt("prId", prId)],
|
|
413
|
+
},
|
|
414
|
+
"mr fix-comments": {
|
|
415
|
+
method: "POST",
|
|
416
|
+
retryUnsafe: true,
|
|
417
|
+
action: "fixPrComments",
|
|
418
|
+
destructive: true,
|
|
419
|
+
usage:
|
|
420
|
+
"mr fix-comments <prId> [--issue KEY] [--project KEY] [--force]" +
|
|
421
|
+
" (starts an AI fix of the PR's unresolved review comments, which commits and pushes;" +
|
|
422
|
+
" returns at once: started / prNumber / queued, or a reason it was refused, e.g. that PR" +
|
|
423
|
+
" is already being fixed; re-check with `mr comments`)",
|
|
424
|
+
buildArgs: ([prId]) => [toInt("prId", prId)],
|
|
425
|
+
},
|
|
426
|
+
"mr reply-fixed": {
|
|
427
|
+
method: "POST",
|
|
428
|
+
retryUnsafe: true,
|
|
429
|
+
action: "replyPrCommentsFixed",
|
|
430
|
+
usage:
|
|
431
|
+
"mr reply-fixed <prId> [--issue KEY] [--project KEY]" +
|
|
432
|
+
' (replies "Fixed" to every unanswered review thread, resolving it when the repo is set to;' +
|
|
433
|
+
" prints success / failed counts)",
|
|
434
|
+
buildArgs: ([prId]) => [toInt("prId", prId)],
|
|
435
|
+
},
|
|
380
436
|
"pipeline status": {
|
|
381
437
|
method: "POST",
|
|
382
438
|
action: "getPipelineStatus",
|
|
@@ -423,14 +479,19 @@ const COMMANDS = {
|
|
|
423
479
|
retryUnsafe: true,
|
|
424
480
|
action: "notifySlack",
|
|
425
481
|
usage:
|
|
426
|
-
'slack notify <message> [--channel ID] [--values \'{"key":"val"}\'] [--
|
|
482
|
+
'slack notify <message> [--channel ID] [--values \'{"key":"val"}\'] [--pr <prId>]' +
|
|
483
|
+
" [--issue KEY] [--project KEY]" +
|
|
484
|
+
" (--pr records the post as that PR's reviewer notify; a channel already notified for it" +
|
|
485
|
+
" gets a ping threaded under the earlier message)",
|
|
427
486
|
buildArgs: ([message], flags) => {
|
|
428
487
|
if (!message) throw new UsageError("slack notify requires <message>.");
|
|
429
488
|
|
|
430
489
|
const channel = flags.channel ? String(flags.channel) : undefined;
|
|
431
490
|
const values = toValues("values", flags.values);
|
|
432
491
|
|
|
433
|
-
return [message, channel, values];
|
|
492
|
+
if (flags.pr === undefined) return [message, channel, values];
|
|
493
|
+
|
|
494
|
+
return [message, channel, values, { prNumber: toInt("pr", String(flags.pr)) }];
|
|
434
495
|
},
|
|
435
496
|
},
|
|
436
497
|
"slack channels": {
|
|
@@ -442,7 +503,7 @@ const COMMANDS = {
|
|
|
442
503
|
};
|
|
443
504
|
|
|
444
505
|
/**
|
|
445
|
-
*
|
|
506
|
+
* Is this invocation of `command` destructive?
|
|
446
507
|
* @param {Command} command
|
|
447
508
|
* @param {Record<string, unknown>} flags
|
|
448
509
|
*/
|