@devflow-one/cli 0.1.16 → 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 +20 -13
- package/package.json +1 -1
- package/src/cli.js +6 -14
- package/src/client.js +7 -2
- package/src/commands.js +20 -34
package/README.md
CHANGED
|
@@ -187,6 +187,12 @@ devflow mr create "Title" feat/x --base release/2026.4 --project ABC
|
|
|
187
187
|
devflow mr create "Title" feat/x --type Bugfix --project ABC
|
|
188
188
|
devflow mr types --project ABC # configured types: id, label, default flag
|
|
189
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
|
+
|
|
190
196
|
# issue key: parsed from the head branch by default; --issue sets it explicitly
|
|
191
197
|
# ({parentWithChildIssueKeys}/{issueKey}/{issueTitle} template tokens) for a branch without one.
|
|
192
198
|
# {issueTitle} needs the Jira API token set in Settings, else it renders empty.
|
|
@@ -200,8 +206,8 @@ devflow mr find ABC-123
|
|
|
200
206
|
devflow pipeline status 42 deadbeef --issue ABC-9
|
|
201
207
|
|
|
202
208
|
# commit a managed worktree with the project's commit-message template; it stages tracked
|
|
203
|
-
# changes only (add --include-untracked for new files)
|
|
204
|
-
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
|
|
205
211
|
devflow commit template /wt/ABC-123 --no-push --project ABC # commit locally, skip push
|
|
206
212
|
|
|
207
213
|
# read a PR/MR's title + description, then edit it: --title/--description replace,
|
|
@@ -225,12 +231,12 @@ devflow issue branches ABC-123 --project ABC
|
|
|
225
231
|
devflow issue prs # every state, merged and closed included
|
|
226
232
|
|
|
227
233
|
# review comments: counts, then "Reply as Fixed" or an AI "Fix Comments" run, which
|
|
228
|
-
# commits and pushes (so it
|
|
234
|
+
# commits and pushes (so it is destructive). It returns at once: `started`, `prNumber` and
|
|
229
235
|
# `queued` (true when an automation run is busy, so the fix starts on its next tick), or
|
|
230
236
|
# `started: false` with a `reason` — e.g. while the panel or the timer is fixing that PR
|
|
231
237
|
devflow mr comments 42 # unfixed / fixed / resolved / prComments
|
|
232
238
|
devflow mr reply-fixed 42 # success / failed / resolved counts
|
|
233
|
-
devflow mr fix-comments 42
|
|
239
|
+
devflow mr fix-comments 42 # then re-check the counts
|
|
234
240
|
|
|
235
241
|
# notify the project's configured Slack channel(s)
|
|
236
242
|
devflow slack notify "Review fixes ready on {pr}" --values '{"pr":"#42"}' --project ABC
|
|
@@ -250,26 +256,27 @@ devflow mr ready 12 --profile prof-1
|
|
|
250
256
|
### Destructive actions
|
|
251
257
|
|
|
252
258
|
`branch delete`, `mr close`, `mr merge`, `mr fix-comments` (its AI run commits and
|
|
253
|
-
pushes) and `commit template` (unless `--no-push`) are destructive.
|
|
254
|
-
|
|
255
|
-
|
|
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:
|
|
256
264
|
|
|
257
265
|
```sh
|
|
258
|
-
devflow branch delete old --project ABC # refused
|
|
259
|
-
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
|
|
260
268
|
|
|
261
269
|
# merge a PR/MR, squashing and cleaning up the source branch
|
|
262
270
|
devflow mr merge 42 --method squash --delete-source-branch --force
|
|
263
271
|
```
|
|
264
272
|
|
|
265
|
-
Inference never
|
|
266
|
-
|
|
267
|
-
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.
|
|
268
275
|
|
|
269
276
|
## Verify
|
|
270
277
|
|
|
271
278
|
```sh
|
|
272
|
-
npm test # unit: arg parsing, command resolution, routing, destructive
|
|
279
|
+
npm test # unit: arg parsing, command resolution, routing, destructive confirmation
|
|
273
280
|
```
|
|
274
281
|
|
|
275
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" +
|
|
@@ -280,8 +280,18 @@ const COMMANDS = {
|
|
|
280
280
|
"mr ready": {
|
|
281
281
|
method: "POST",
|
|
282
282
|
action: "markPrReadyForReview",
|
|
283
|
-
usage:
|
|
284
|
-
|
|
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
|
+
},
|
|
285
295
|
},
|
|
286
296
|
"mr draft": {
|
|
287
297
|
method: "POST",
|
|
@@ -294,7 +304,7 @@ const COMMANDS = {
|
|
|
294
304
|
retryUnsafe: true,
|
|
295
305
|
action: "closePullRequest",
|
|
296
306
|
destructive: true,
|
|
297
|
-
usage: "mr close <prId> [--issue KEY] [--project KEY] --force",
|
|
307
|
+
usage: "mr close <prId> [--issue KEY] [--project KEY] [--force]",
|
|
298
308
|
buildArgs: ([prId]) => [toInt("prId", prId)],
|
|
299
309
|
},
|
|
300
310
|
"mr merge": {
|
|
@@ -304,7 +314,7 @@ const COMMANDS = {
|
|
|
304
314
|
destructive: true,
|
|
305
315
|
usage:
|
|
306
316
|
"mr merge <prId> [--method merge|squash|rebase] [--delete-source-branch] " +
|
|
307
|
-
"[--issue KEY] [--project KEY] --force",
|
|
317
|
+
"[--issue KEY] [--project KEY] [--force]",
|
|
308
318
|
buildArgs: ([prId], flags) => {
|
|
309
319
|
const mergeMethod = flags.method === undefined ? undefined : toMergeMethod(flags.method);
|
|
310
320
|
const deleteSourceBranch = flags["delete-source-branch"] ? true : undefined;
|
|
@@ -322,30 +332,6 @@ const COMMANDS = {
|
|
|
322
332
|
return [toInt("prId", prId), assignee];
|
|
323
333
|
},
|
|
324
334
|
},
|
|
325
|
-
"mr labels": {
|
|
326
|
-
method: "POST",
|
|
327
|
-
action: "addPrLabels",
|
|
328
|
-
usage: "mr labels <prId> <label...> [--issue KEY] [--project KEY]",
|
|
329
|
-
buildArgs: ([prId, ...labels]) => {
|
|
330
|
-
if (labels.length === 0) {
|
|
331
|
-
throw new UsageError("mr labels requires <prId> and at least one <label>.");
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return [toInt("prId", prId), labels];
|
|
335
|
-
},
|
|
336
|
-
},
|
|
337
|
-
"mr reviewers": {
|
|
338
|
-
method: "POST",
|
|
339
|
-
action: "requestPrReviewers",
|
|
340
|
-
usage: "mr reviewers <prId> <reviewer...> [--issue KEY] [--project KEY]",
|
|
341
|
-
buildArgs: ([prId, ...reviewers]) => {
|
|
342
|
-
if (reviewers.length === 0) {
|
|
343
|
-
throw new UsageError("mr reviewers requires <prId> and at least one <reviewer>.");
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return [toInt("prId", prId), reviewers];
|
|
347
|
-
},
|
|
348
|
-
},
|
|
349
335
|
"mr find": {
|
|
350
336
|
method: "POST",
|
|
351
337
|
action: "findPrByIssueKey",
|
|
@@ -431,7 +417,7 @@ const COMMANDS = {
|
|
|
431
417
|
action: "fixPrComments",
|
|
432
418
|
destructive: true,
|
|
433
419
|
usage:
|
|
434
|
-
"mr fix-comments <prId> [--issue KEY] [--project KEY] --force" +
|
|
420
|
+
"mr fix-comments <prId> [--issue KEY] [--project KEY] [--force]" +
|
|
435
421
|
" (starts an AI fix of the PR's unresolved review comments, which commits and pushes;" +
|
|
436
422
|
" returns at once: started / prNumber / queued, or a reason it was refused, e.g. that PR" +
|
|
437
423
|
" is already being fixed; re-check with `mr comments`)",
|
|
@@ -517,7 +503,7 @@ const COMMANDS = {
|
|
|
517
503
|
};
|
|
518
504
|
|
|
519
505
|
/**
|
|
520
|
-
*
|
|
506
|
+
* Is this invocation of `command` destructive?
|
|
521
507
|
* @param {Command} command
|
|
522
508
|
* @param {Record<string, unknown>} flags
|
|
523
509
|
*/
|