@devflow-one/cli 0.1.6 → 0.1.7

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
@@ -166,6 +166,17 @@ devflow mr create "Title" feat/x --body "desc" --project ABC
166
166
  # pattern is expanded to the newest matching remote branch), or pick one
167
167
  devflow mr create "Title" feat/x --base release/2026.4 --project ABC
168
168
 
169
+ # PR type: the project's first configured type by default — it supplies the
170
+ # description/title templates and the labels the PR is stamped with. Pass its id
171
+ # or its label to pick another; an unrecognized value falls back to that default.
172
+ devflow mr create "Title" feat/x --type Bugfix --project ABC
173
+ devflow mr types --project ABC # configured types: id, label, default flag
174
+
175
+ # issue key: parsed from the head branch by default; --issue sets it explicitly
176
+ # ({parentWithChildIssueKeys}/{issueKey}/{issueTitle} template tokens) for a branch without one.
177
+ # {issueTitle} needs the Jira API token set in Settings, else it renders empty.
178
+ devflow mr create "Title" feat/x --issue ABC-123
179
+
169
180
  # routed by issue key (project key = prefix before the last '-')
170
181
  devflow mr find ABC-123
171
182
  devflow pipeline status 42 deadbeef --issue ABC-9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devflow-one/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
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
@@ -66,6 +66,19 @@ function toBranch(flag, raw) {
66
66
  return branch;
67
67
  }
68
68
 
69
+ /** Parse a value-taking flag as a non-blank string, or fail clearly. */
70
+ function toText(flag, raw) {
71
+ if (raw === undefined) return undefined;
72
+
73
+ const text = String(raw).trim();
74
+
75
+ if (raw === true || text === "") {
76
+ throw new UsageError(`"--${flag}" requires a value.`);
77
+ }
78
+
79
+ return text;
80
+ }
81
+
69
82
  /** A bad-invocation error (wrong/missing args) — printed with the command usage. */
70
83
  export class UsageError extends Error {
71
84
  constructor(message) {
@@ -145,20 +158,27 @@ const COMMANDS = {
145
158
  method: "POST",
146
159
  action: "createPullRequest",
147
160
  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)",
161
+ "mr create <title> <headBranch> [--body <text>] [--base <branch>] [--type <idOrLabel>]" +
162
+ " [--issue KEY] [--project KEY]" +
163
+ " (--base defaults to the repo's configured base branch, expanding a {year}/{number} pattern;" +
164
+ " --type defaults to the project's first configured PR type)",
150
165
  buildArgs: ([title, headBranch], flags) => {
151
166
  if (!title || !headBranch) {
152
167
  throw new UsageError("mr create requires <title> and <headBranch>.");
153
168
  }
154
169
 
155
170
  const baseBranch = toBranch("base", flags.base);
171
+ const prType = toText("type", flags.type);
172
+ // Only an explicit --issue is forwarded; the gateway otherwise parses the head branch.
173
+ const issueKey = toText("issue", flags.issue);
174
+
175
+ // Trailing undefined is trimmed so the gateway resolves the repo's own
176
+ // configured base branch (and expands a pattern) instead of seeing null.
177
+ const args = [title, flags.body ?? "", headBranch, baseBranch, prType, issueKey];
156
178
 
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];
179
+ while (args.length > 3 && args[args.length - 1] === undefined) args.pop();
180
+
181
+ return args;
162
182
  },
163
183
  },
164
184
  "mr ready": {
@@ -247,6 +267,12 @@ const COMMANDS = {
247
267
  return [key];
248
268
  },
249
269
  },
270
+ "mr types": {
271
+ method: "POST",
272
+ action: "listPrTypes",
273
+ usage: "mr types [--issue KEY] [--project KEY]",
274
+ buildArgs: () => [],
275
+ },
250
276
  "mr fix-prompt": {
251
277
  method: "POST",
252
278
  action: "getFixCommentsPrompt",
package/src/context.js CHANGED
@@ -203,10 +203,16 @@ export function offlineProfileError(context) {
203
203
 
204
204
  /**
205
205
  * How the resolved profile looks to the daemon. Shown inline on the profile row
206
- * so `devflow where` answers "would a call work?" without a second lookup.
206
+ * so `devflow where` answers "would a call work?" without a second lookup. A
207
+ * connected profile also names the extension bundle it is running, so a stale
208
+ * `dist/` can be spotted without guessing.
207
209
  */
208
210
  function describeLiveness(context) {
209
- if (context.profileAlive === true) return "connected";
211
+ if (context.profileAlive === true) {
212
+ return context.buildStamp
213
+ ? `connected; extension build ${context.buildStamp}`
214
+ : "connected; extension build unknown (this build predates the stamp)";
215
+ }
210
216
 
211
217
  if (context.profileAlive === false) {
212
218
  return "NOT connected; open this Chrome profile before running actions";
@@ -163,7 +163,8 @@ function fill(resolved, source, origin) {
163
163
  * @param {(q: { cwd?: string, remote?: string, branch?: string }) => Promise<object | null>} [options.queryContext]
164
164
  * @returns {Promise<{
165
165
  * projectKey?: string, issueKey?: string, repo?: string, baseBranch?: string,
166
- * profile?: string, profileAlive?: boolean | null, matchedBy: Record<string, string>,
166
+ * profile?: string, profileAlive?: boolean | null, buildStamp?: string | null,
167
+ * matchedBy: Record<string, string>,
167
168
  * candidates?: object[], ambiguous?: boolean, warnings: string[]
168
169
  * }>}
169
170
  */
@@ -272,8 +273,12 @@ export async function resolveContext({
272
273
 
273
274
  // Liveness is only meaningful for the profile we actually resolved to; it lets
274
275
  // consumers name an offline profile instead of printing a bare NO_PROFILE.
276
+ // The build stamp describes that same profile's running extension bundle, so
277
+ // it is gated identically — reporting one profile's bundle beside another's id
278
+ // is exactly the confusion the stamp exists to end.
275
279
  if (!conflict && resolved.profile && resolved.profile === clean(context.profileId)) {
276
280
  resolved.profileAlive = context.profileAlive ?? null;
281
+ resolved.buildStamp = context.buildStamp ?? null;
277
282
  }
278
283
 
279
284
  // The daemon's issue key describes its matched project too, so it is dropped on