@devflow-one/cli 0.1.3 → 0.1.6
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 +10 -3
- package/package.json +1 -1
- package/src/args.js +9 -1
- package/src/commands.js +53 -3
- package/src/contextResolver.js +5 -2
package/README.md
CHANGED
|
@@ -162,6 +162,10 @@ devflow branch create feat/x
|
|
|
162
162
|
devflow branch create feat/x --project ABC
|
|
163
163
|
devflow mr create "Title" feat/x --body "desc" --project ABC
|
|
164
164
|
|
|
165
|
+
# target branch: the repo's configured base branch by default (a {year}/{number}
|
|
166
|
+
# pattern is expanded to the newest matching remote branch), or pick one
|
|
167
|
+
devflow mr create "Title" feat/x --base release/2026.4 --project ABC
|
|
168
|
+
|
|
165
169
|
# routed by issue key (project key = prefix before the last '-')
|
|
166
170
|
devflow mr find ABC-123
|
|
167
171
|
devflow pipeline status 42 deadbeef --issue ABC-9
|
|
@@ -182,13 +186,16 @@ devflow mr ready 12 --profile prof-1
|
|
|
182
186
|
|
|
183
187
|
### Destructive actions
|
|
184
188
|
|
|
185
|
-
`branch delete` and `mr
|
|
186
|
-
confirm** with `--force` (or `--yes`); the gateway enforces the same
|
|
187
|
-
server-side, so the confirmation can't be bypassed:
|
|
189
|
+
`branch delete`, `mr close` and `mr merge` are destructive. They are **refused
|
|
190
|
+
unless you confirm** with `--force` (or `--yes`); the gateway enforces the same
|
|
191
|
+
gate server-side, so the confirmation can't be bypassed:
|
|
188
192
|
|
|
189
193
|
```sh
|
|
190
194
|
devflow branch delete old --project ABC # refused
|
|
191
195
|
devflow branch delete old --project ABC --force # executes
|
|
196
|
+
|
|
197
|
+
# merge a PR/MR, squashing and cleaning up the source branch
|
|
198
|
+
devflow mr merge 42 --method squash --delete-source-branch --force
|
|
192
199
|
```
|
|
193
200
|
|
|
194
201
|
Inference never softens this gate — a destructive command run with fully
|
package/package.json
CHANGED
package/src/args.js
CHANGED
|
@@ -4,7 +4,15 @@
|
|
|
4
4
|
// positionals.
|
|
5
5
|
|
|
6
6
|
/** Boolean flags never consume the following token. */
|
|
7
|
-
const BOOLEAN_FLAGS = new Set([
|
|
7
|
+
const BOOLEAN_FLAGS = new Set([
|
|
8
|
+
"json",
|
|
9
|
+
"all",
|
|
10
|
+
"help",
|
|
11
|
+
"force",
|
|
12
|
+
"yes",
|
|
13
|
+
"no-push",
|
|
14
|
+
"delete-source-branch",
|
|
15
|
+
]);
|
|
8
16
|
|
|
9
17
|
/**
|
|
10
18
|
* @param {string[]} argv the args after the node/script path
|
package/src/commands.js
CHANGED
|
@@ -20,6 +20,20 @@ function toInt(name, value) {
|
|
|
20
20
|
return n;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** Valid `--method` values for `mr merge`, mirroring the provider merge strategies. */
|
|
24
|
+
const MERGE_METHODS = ["merge", "squash", "rebase"];
|
|
25
|
+
|
|
26
|
+
/** Parse a `--method` flag into a merge strategy, or fail with the allowed set. */
|
|
27
|
+
function toMergeMethod(value) {
|
|
28
|
+
const method = String(value);
|
|
29
|
+
|
|
30
|
+
if (!MERGE_METHODS.includes(method)) {
|
|
31
|
+
throw new UsageError(`"--method" must be one of ${MERGE_METHODS.join(", ")}, got "${method}".`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return method;
|
|
35
|
+
}
|
|
36
|
+
|
|
23
37
|
/** Parse a `--values` JSON-object flag into a plain object, or fail clearly. */
|
|
24
38
|
function toValues(flag, raw) {
|
|
25
39
|
if (raw === undefined) return undefined;
|
|
@@ -39,6 +53,19 @@ function toValues(flag, raw) {
|
|
|
39
53
|
return parsed;
|
|
40
54
|
}
|
|
41
55
|
|
|
56
|
+
/** Parse a value-taking flag as a non-blank string, or fail clearly. */
|
|
57
|
+
function toBranch(flag, raw) {
|
|
58
|
+
if (raw === undefined) return undefined;
|
|
59
|
+
|
|
60
|
+
const branch = String(raw).trim();
|
|
61
|
+
|
|
62
|
+
if (raw === true || branch === "") {
|
|
63
|
+
throw new UsageError(`"--${flag}" requires a branch name.`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return branch;
|
|
67
|
+
}
|
|
68
|
+
|
|
42
69
|
/** A bad-invocation error (wrong/missing args) — printed with the command usage. */
|
|
43
70
|
export class UsageError extends Error {
|
|
44
71
|
constructor(message) {
|
|
@@ -102,7 +129,8 @@ const COMMANDS = {
|
|
|
102
129
|
method: "POST",
|
|
103
130
|
action: "commitByTemplate",
|
|
104
131
|
usage:
|
|
105
|
-
'commit template <worktreePath> [--values \'{"key":"val"}\'] [--no-push] [--issue KEY] [--project KEY]'
|
|
132
|
+
'commit template <worktreePath> [--values \'{"key":"val"}\'] [--no-push] [--issue KEY] [--project KEY]' +
|
|
133
|
+
" (--values keys must match the {key} tokens of the project's configured commit template)",
|
|
106
134
|
buildArgs: ([worktreePath], flags) => {
|
|
107
135
|
if (!worktreePath) throw new UsageError("commit template requires <worktreePath>.");
|
|
108
136
|
|
|
@@ -116,13 +144,21 @@ const COMMANDS = {
|
|
|
116
144
|
"mr create": {
|
|
117
145
|
method: "POST",
|
|
118
146
|
action: "createPullRequest",
|
|
119
|
-
usage:
|
|
147
|
+
usage:
|
|
148
|
+
"mr create <title> <headBranch> [--body <text>] [--base <branch>] [--issue KEY] [--project KEY]" +
|
|
149
|
+
" (--base defaults to the repo's configured base branch, expanding a {year}/{number} pattern)",
|
|
120
150
|
buildArgs: ([title, headBranch], flags) => {
|
|
121
151
|
if (!title || !headBranch) {
|
|
122
152
|
throw new UsageError("mr create requires <title> and <headBranch>.");
|
|
123
153
|
}
|
|
124
154
|
|
|
125
|
-
|
|
155
|
+
const baseBranch = toBranch("base", flags.base);
|
|
156
|
+
|
|
157
|
+
// Omitted rather than trailing-undefined so the gateway resolves the repo's
|
|
158
|
+
// own configured base branch (and expands a pattern) instead of seeing null.
|
|
159
|
+
return baseBranch
|
|
160
|
+
? [title, flags.body ?? "", headBranch, baseBranch]
|
|
161
|
+
: [title, flags.body ?? "", headBranch];
|
|
126
162
|
},
|
|
127
163
|
},
|
|
128
164
|
"mr ready": {
|
|
@@ -144,6 +180,20 @@ const COMMANDS = {
|
|
|
144
180
|
usage: "mr close <prId> [--issue KEY] [--project KEY] --force",
|
|
145
181
|
buildArgs: ([prId]) => [toInt("prId", prId)],
|
|
146
182
|
},
|
|
183
|
+
"mr merge": {
|
|
184
|
+
method: "POST",
|
|
185
|
+
action: "mergePullRequest",
|
|
186
|
+
destructive: true,
|
|
187
|
+
usage:
|
|
188
|
+
"mr merge <prId> [--method merge|squash|rebase] [--delete-source-branch] " +
|
|
189
|
+
"[--issue KEY] [--project KEY] --force",
|
|
190
|
+
buildArgs: ([prId], flags) => {
|
|
191
|
+
const mergeMethod = flags.method === undefined ? undefined : toMergeMethod(flags.method);
|
|
192
|
+
const deleteSourceBranch = flags["delete-source-branch"] ? true : undefined;
|
|
193
|
+
|
|
194
|
+
return [toInt("prId", prId), { mergeMethod, deleteSourceBranch }];
|
|
195
|
+
},
|
|
196
|
+
},
|
|
147
197
|
"mr assign": {
|
|
148
198
|
method: "POST",
|
|
149
199
|
action: "assignPr",
|
package/src/contextResolver.js
CHANGED
|
@@ -162,8 +162,8 @@ function fill(resolved, source, origin) {
|
|
|
162
162
|
* @param {(args: string[]) => string | null} [options.runGit]
|
|
163
163
|
* @param {(q: { cwd?: string, remote?: string, branch?: string }) => Promise<object | null>} [options.queryContext]
|
|
164
164
|
* @returns {Promise<{
|
|
165
|
-
* projectKey?: string, issueKey?: string, repo?: string,
|
|
166
|
-
* profileAlive?: boolean | null, matchedBy: Record<string, string>,
|
|
165
|
+
* projectKey?: string, issueKey?: string, repo?: string, baseBranch?: string,
|
|
166
|
+
* profile?: string, profileAlive?: boolean | null, matchedBy: Record<string, string>,
|
|
167
167
|
* candidates?: object[], ambiguous?: boolean, warnings: string[]
|
|
168
168
|
* }>}
|
|
169
169
|
*/
|
|
@@ -259,6 +259,9 @@ export async function resolveContext({
|
|
|
259
259
|
clean(context.repo?.id) ??
|
|
260
260
|
clean(context.repo?.name) ??
|
|
261
261
|
clean(context.repo?.remoteUrl),
|
|
262
|
+
// The selector `repo` flattens the digest to one string; the configured
|
|
263
|
+
// base branch rides alongside it so a caller can target it explicitly.
|
|
264
|
+
baseBranch: clean(context.repo?.baseBranch),
|
|
262
265
|
profile: clean(context.profileId),
|
|
263
266
|
}),
|
|
264
267
|
},
|