@devflow-one/cli 0.1.11 → 0.1.12

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 CHANGED
@@ -156,7 +156,12 @@ devflow where # what this directory resolves to
156
156
 
157
157
  # inside a configured repo, routing is inferred — no flags
158
158
  devflow mr find # issue key from the branch
159
- devflow branch create feat/x
159
+
160
+ # the issue's branch, from the repo's branch template (incl. {parentIssueKey}: a
161
+ # sub-issue may resolve to its parent's branch). Check it out before `commit template`.
162
+ devflow branch resolve ABC-124 # read-only: branch name + whether it exists
163
+ devflow branch create --issue ABC-124 # creates it, or reuses an existing one
164
+ devflow branch create feat/x # a name is only a fallback when no issue key is known
160
165
 
161
166
  # or route explicitly, overriding whatever the directory implies
162
167
  devflow branch create feat/x --project ABC
@@ -177,6 +182,8 @@ devflow mr types --project ABC # configured types: id, label, def
177
182
  # ({parentWithChildIssueKeys}/{issueKey}/{issueTitle} template tokens) for a branch without one.
178
183
  # {issueTitle} needs the Jira API token set in Settings, else it renders empty.
179
184
  devflow mr create "Title" feat/x --issue ABC-123
185
+ devflow mr create "Title" --issue ABC-123 # head branch from the branch template
186
+ # the template always picks the head branch; a different one is replaced (warning on stderr)
180
187
 
181
188
  # routed by issue key (project key = prefix before the last '-')
182
189
  devflow mr find ABC-123
@@ -186,6 +193,12 @@ devflow pipeline status 42 deadbeef --issue ABC-9
186
193
  devflow commit template /wt/ABC-123 --values '{"summary":"fix login"}' --project ABC
187
194
  devflow commit template /wt/ABC-123 --no-push --project ABC # commit locally, skip push
188
195
 
196
+ # read a PR/MR's title + description, then edit it: --title/--description replace,
197
+ # --append-description adds text after a `---` rule (skipped if already at the end)
198
+ devflow mr get 42 --project ABC
199
+ devflow mr update 42 --append-description "## Test plan" --project ABC
200
+ devflow mr update 42 --title "Fix login redirect" --project ABC
201
+
189
202
  # assembled fix-review-comments prompt (read-only; runs no AI, makes no writes)
190
203
  devflow mr fix-prompt 42 # issue key inferred from the branch
191
204
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devflow-one/cli",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Terminal CLI wrapping the local DevFlow gateway so shell scripts can trigger repo actions.",
5
5
  "keywords": [
6
6
  "devflow",
package/src/commands.js CHANGED
@@ -79,6 +79,37 @@ function toText(flag, raw) {
79
79
  return text;
80
80
  }
81
81
 
82
+ /** Read a value-taking flag verbatim (no trimming), failing when it was given no value. */
83
+ function toRawText(flag, raw) {
84
+ if (raw === true) throw new UsageError(`"--${flag}" requires a value.`);
85
+
86
+ return raw === undefined ? undefined : String(raw);
87
+ }
88
+
89
+ /**
90
+ * The issue key whose branch template names `branch`: --issue, else — only when no
91
+ * branch was named — the inferred key, so an unrelated checkout never overrides it.
92
+ */
93
+ function templateIssueKey(branch, flags, context) {
94
+ return toText("issue", flags.issue) ?? (branch ? undefined : context.issueKey);
95
+ }
96
+
97
+ /** buildArgs for a command whose only arg is an issue key: positional, else --issue / inferred. */
98
+ function issueKeyArgs(name) {
99
+ return ([issueKey], _flags, context = {}) => {
100
+ const key = issueKey ?? context.issueKey;
101
+
102
+ if (!key) {
103
+ throw new UsageError(
104
+ `${name} needs an issue key: pass it positionally or with --issue ` +
105
+ "(none could be inferred from the current branch or directory).",
106
+ );
107
+ }
108
+
109
+ return [key];
110
+ };
111
+ }
112
+
82
113
  /** A bad-invocation error (wrong/missing args) — printed with the command usage. */
83
114
  export class UsageError extends Error {
84
115
  constructor(message) {
@@ -121,13 +152,28 @@ const COMMANDS = {
121
152
  method: "POST",
122
153
  retryUnsafe: true,
123
154
  action: "createBranch",
124
- usage: "branch create <branchName> [--issue KEY] [--project KEY]",
125
- buildArgs: ([branchName]) => {
126
- if (!branchName) throw new UsageError("branch create requires <branchName>.");
155
+ usage:
156
+ "branch create [branchName] [--issue KEY] [--project KEY]" +
157
+ " (the repo's branch template names the branch from the issue, incl. {parentIssueKey};" +
158
+ " branchName is only a fallback; an existing branch is reused)",
159
+ buildArgs: ([branchName], flags, context = {}) => {
160
+ const issueKey = templateIssueKey(branchName, flags, context);
161
+
162
+ if (!branchName && !issueKey) {
163
+ throw new UsageError("branch create needs an issue key (--issue) or a <branchName>.");
164
+ }
127
165
 
128
- return [branchName];
166
+ return [branchName, issueKey];
129
167
  },
130
168
  },
169
+ "branch resolve": {
170
+ method: "POST",
171
+ action: "resolveBranchName",
172
+ usage:
173
+ "branch resolve [issueKey] [--issue KEY] [--project KEY]" +
174
+ " (prints the issue's configured branch name and whether it exists; read-only)",
175
+ buildArgs: issueKeyArgs("branch resolve"),
176
+ },
131
177
  "branch delete": {
132
178
  method: "POST",
133
179
  retryUnsafe: true,
@@ -150,7 +196,8 @@ const COMMANDS = {
150
196
  " (--values keys fill the project's commit template, default" +
151
197
  ' "{summary}\\n\\n{changesList}\\n\\n{issueSlug}": summary, changesList (string array,' +
152
198
  " rendered as bullets), issueSlug, prNumber; missing keys are left empty and reported" +
153
- " as warnings on stderr)",
199
+ " as warnings on stderr; commits the worktree's checked-out branch, so check out the" +
200
+ " one `branch resolve` prints first)",
154
201
  buildArgs: ([worktreePath], flags) => {
155
202
  if (!worktreePath) throw new UsageError("commit template requires <worktreePath>.");
156
203
 
@@ -166,20 +213,23 @@ const COMMANDS = {
166
213
  retryUnsafe: true,
167
214
  action: "createPullRequest",
168
215
  usage:
169
- "mr create <title> <headBranch> [--body <text>] [--base <branch>] [--type <idOrLabel>]" +
216
+ "mr create <title> [headBranch] [--body <text>] [--base <branch>] [--type <idOrLabel>]" +
170
217
  " [--issue KEY] [--project KEY]" +
171
- " (--base defaults to the repo's configured base branch, expanding a {year}/{number} pattern;" +
218
+ " (the repo's branch template picks the head branch from the issue; headBranch is only" +
219
+ " a fallback; --base defaults to the repo's configured base branch, expanding a {year}/{number} pattern;" +
172
220
  " --type is auto-selected by matching PR type names against the title prefix, branch prefix," +
173
221
  " Jira issue type and title keywords, falling back to the first configured type)",
174
- buildArgs: ([title, headBranch], flags) => {
175
- if (!title || !headBranch) {
176
- throw new UsageError("mr create requires <title> and <headBranch>.");
222
+ buildArgs: ([title, headBranch], flags, context = {}) => {
223
+ const issueKey = templateIssueKey(headBranch, flags, context);
224
+
225
+ if (!title || (!headBranch && !issueKey)) {
226
+ throw new UsageError(
227
+ "mr create requires <title> and an issue key (--issue) or <headBranch>.",
228
+ );
177
229
  }
178
230
 
179
231
  const baseBranch = toBranch("base", flags.base);
180
232
  const prType = toText("type", flags.type);
181
- // Only an explicit --issue is forwarded; the gateway otherwise parses the head branch.
182
- const issueKey = toText("issue", flags.issue);
183
233
 
184
234
  // Trailing undefined is trimmed so the gateway resolves the repo's own
185
235
  // configured base branch (and expands a pattern) instead of seeing null.
@@ -265,17 +315,36 @@ const COMMANDS = {
265
315
  usage: "mr find [issueKey] [--issue KEY] [--project KEY]",
266
316
  // The positional wins, then --issue, then the issue key inferred from the
267
317
  // branch — so `devflow mr find` on `ABC-123-login` needs no argument at all.
268
- buildArgs: ([issueKey], _flags, context = {}) => {
269
- const key = issueKey ?? context.issueKey;
318
+ buildArgs: issueKeyArgs("mr find"),
319
+ },
320
+ "mr get": {
321
+ method: "POST",
322
+ action: "getPullRequest",
323
+ usage: "mr get <prId> [--issue KEY] [--project KEY] (title, description, state; read-only)",
324
+ buildArgs: ([prId]) => [toInt("prId", prId)],
325
+ },
326
+ "mr update": {
327
+ method: "POST",
328
+ action: "updatePullRequest",
329
+ usage:
330
+ "mr update <prId> [--title <text>] [--description <text> | --append-description <text>]" +
331
+ " [--issue KEY] [--project KEY]" +
332
+ " (--title/--description replace; --append-description adds text after a `---` rule" +
333
+ " and is skipped if it already ends the description after that rule)",
334
+ buildArgs: ([prId], flags) => {
335
+ const title = toText("title", flags.title);
336
+ const body = toRawText("description", flags.description);
337
+ const appendBody = toRawText("append-description", flags["append-description"]);
270
338
 
271
- if (!key) {
272
- throw new UsageError(
273
- "mr find needs an issue key: pass it positionally or with --issue " +
274
- "(none could be inferred from the current branch or directory).",
275
- );
339
+ if (title === undefined && body === undefined && appendBody === undefined) {
340
+ throw new UsageError("mr update needs --title, --description or --append-description.");
341
+ }
342
+
343
+ if (body !== undefined && appendBody !== undefined) {
344
+ throw new UsageError("mr update takes --description or --append-description, not both.");
276
345
  }
277
346
 
278
- return [key];
347
+ return [toInt("prId", prId), { title, body, appendBody }];
279
348
  },
280
349
  },
281
350
  "mr types": {