gflows 0.1.13 → 0.1.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gflows",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "A lightweight CLI for consistent Git branching workflows (main + dev, feature/bugfix/chore/release/hotfix).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,7 +10,10 @@
10
10
  },
11
11
  "scripts": {
12
12
  "test": "bun test",
13
- "lint": "tsc --noEmit",
13
+ "typecheck": "tsc --noEmit",
14
+ "lint": "biome check .",
15
+ "lint:fix": "biome check . --write",
16
+ "format": "biome format . --write",
14
17
  "gflows": "bun run src/cli.ts",
15
18
  "publish:all": "bun run scripts/publish.ts",
16
19
  "publish:npm": "bun run scripts/publish.ts -- --npm-only",
@@ -33,6 +36,7 @@
33
36
  "@inquirer/prompts": "^8"
34
37
  },
35
38
  "devDependencies": {
39
+ "@biomejs/biome": "^2.4.4",
36
40
  "@types/bun": "latest",
37
41
  "typescript": "^5"
38
42
  }
package/src/cli.ts CHANGED
@@ -9,9 +9,9 @@
9
9
  import { existsSync, statSync } from "node:fs";
10
10
  import { resolve } from "node:path";
11
11
  import { parseArgs } from "node:util";
12
- import type { Command, BranchType, ParsedArgs } from "./types.js";
13
- import { EXIT_OK, EXIT_USER, EXIT_GIT } from "./constants.js";
12
+ import { EXIT_GIT, EXIT_OK, EXIT_USER } from "./constants.js";
14
13
  import { exitCodeForError } from "./errors.js";
14
+ import type { BranchType, Command, ParsedArgs } from "./types.js";
15
15
 
16
16
  /** Last parsed args, set at start of run(); used by catch/rejection to respect -v for stack trace. */
17
17
  let lastParsedArgs: ParsedArgs | null = null;
@@ -30,14 +30,7 @@ const COMMANDS: Command[] = [
30
30
  "version",
31
31
  ];
32
32
 
33
- const BRANCH_TYPES: BranchType[] = [
34
- "feature",
35
- "bugfix",
36
- "chore",
37
- "release",
38
- "hotfix",
39
- "spike",
40
- ];
33
+ const BRANCH_TYPES: BranchType[] = ["feature", "bugfix", "chore", "release", "hotfix", "spike"];
41
34
 
42
35
  /** Short flag → command (when used as main command). */
43
36
  const SHORT_TO_COMMAND: Record<string, Command> = {
@@ -80,6 +73,7 @@ function buildParseArgsOptions() {
80
73
  // Common
81
74
  push: { type: "boolean" as const, short: "p" },
82
75
  noPush: { type: "boolean" as const, short: "P" },
76
+ "no-push": { type: "boolean" as const },
83
77
  main: { type: "string" as const },
84
78
  dev: { type: "string" as const },
85
79
  remote: { type: "string" as const, short: "R" },
@@ -147,27 +141,33 @@ function editDistance(a: string, b: string): number {
147
141
  const m = a.length;
148
142
  const n = b.length;
149
143
  const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
150
- for (let i = 0; i <= m; i++) dp[i]![0] = i;
151
- for (let j = 0; j <= n; j++) dp[0]![j] = j;
144
+ for (let i = 0; i <= m; i++) {
145
+ const row = dp[i];
146
+ if (row) row[0] = i;
147
+ }
148
+ for (let j = 0; j <= n; j++) {
149
+ const row = dp[0];
150
+ if (row) row[j] = j;
151
+ }
152
152
  for (let i = 1; i <= m; i++) {
153
153
  for (let j = 1; j <= n; j++) {
154
154
  const cost = a[i - 1] === b[j - 1] ? 0 : 1;
155
- dp[i]![j] = Math.min(
156
- dp[i - 1]![j]! + 1,
157
- dp[i]![j - 1]! + 1,
158
- dp[i - 1]![j - 1]! + cost
159
- );
155
+ const v1 = dp[i - 1]?.[j] ?? 0;
156
+ const v2 = dp[i]?.[j - 1] ?? 0;
157
+ const v3 = dp[i - 1]?.[j - 1] ?? 0;
158
+ const rowI = dp[i];
159
+ if (rowI) rowI[j] = Math.min(v1 + 1, v2 + 1, v3 + cost);
160
160
  }
161
161
  }
162
- return dp[m]![n]!;
162
+ return dp[m]?.[n] ?? 0;
163
163
  }
164
164
 
165
165
  /** Resolve command from positionals and short flags. Short wins if both present. */
166
166
  function resolveCommand(
167
167
  positionals: string[],
168
- values: Record<string, string | boolean | undefined>
168
+ values: Record<string, string | boolean | undefined>,
169
169
  ): Command | undefined {
170
- for (const [short, cmd] of Object.entries(SHORT_TO_COMMAND)) {
170
+ for (const [_short, cmd] of Object.entries(SHORT_TO_COMMAND)) {
171
171
  const key = cmd === "delete" ? "delete" : cmd;
172
172
  if (values[key] === true) return cmd as Command;
173
173
  }
@@ -182,7 +182,7 @@ function resolveCommand(
182
182
  function resolveType(
183
183
  command: Command,
184
184
  positionals: string[],
185
- values: Record<string, string | boolean | undefined>
185
+ values: Record<string, string | boolean | undefined>,
186
186
  ): BranchType | undefined {
187
187
  if (command !== "start" && command !== "finish" && command !== "list") {
188
188
  return undefined;
@@ -218,7 +218,7 @@ function resolveType(
218
218
  function resolveName(
219
219
  command: Command,
220
220
  positionals: string[],
221
- values: Record<string, string | boolean | undefined>
221
+ values: Record<string, string | boolean | undefined>,
222
222
  ): string | undefined {
223
223
  const branch = values.branch;
224
224
  if (typeof branch === "string" && branch.trim() !== "") {
@@ -239,7 +239,7 @@ function resolveName(
239
239
  }
240
240
  if (command === "bump") {
241
241
  const dir = positionals[skip];
242
- const typ = positionals[skip + 1];
242
+ const _typ = positionals[skip + 1];
243
243
  if (dir === "up" || dir === "down") {
244
244
  return dir;
245
245
  }
@@ -254,7 +254,7 @@ function resolveName(
254
254
  /** Resolve bump direction and type from positionals (bump [up|down] [patch|minor|major]). */
255
255
  function resolveBump(
256
256
  positionals: string[],
257
- values: Record<string, string | boolean | undefined>
257
+ _values: Record<string, string | boolean | undefined>,
258
258
  ): { direction?: "up" | "down"; type?: "patch" | "minor" | "major" } {
259
259
  const skip = positionals[0] === "bump" ? 1 : 0;
260
260
  const a = positionals[skip];
@@ -304,9 +304,7 @@ export function parse(argv: string[] = Bun.argv.slice(2)): ParsedArgs {
304
304
  // -r context: for list → includeRemote; for start/finish → already used as type release
305
305
  const includeRemote =
306
306
  command === "list"
307
- ? (v.includeRemote === true ||
308
- v["include-remote"] === true ||
309
- v.release === true)
307
+ ? v.includeRemote === true || v["include-remote"] === true || v.release === true
310
308
  : false;
311
309
 
312
310
  let completionShell: "bash" | "zsh" | "fish" | undefined;
@@ -324,7 +322,7 @@ export function parse(argv: string[] = Bun.argv.slice(2)): ParsedArgs {
324
322
  bumpDirection,
325
323
  bumpType,
326
324
  push: v.push === true,
327
- noPush: v.noPush === true,
325
+ noPush: v.noPush === true || v["no-push"] === true,
328
326
  main: typeof v.main === "string" && v.main.trim() !== "" ? v.main.trim() : undefined,
329
327
  dev: typeof v.dev === "string" && v.dev.trim() !== "" ? v.dev.trim() : undefined,
330
328
  remote: typeof v.remote === "string" ? v.remote : undefined,
@@ -1,20 +1,51 @@
1
1
  /**
2
- * Bump command: bump or rollback root package version (patch/minor/major).
2
+ * Bump command: bump or rollback package version (patch/minor/major).
3
+ * Supports monorepos: discovers all package.json and jsr.json under cwd and bumps them to the same version.
3
4
  * Keeps package.json and jsr.json in sync; no git operations.
4
5
  * @module commands/bump
5
6
  */
6
7
 
7
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
8
- import { join } from "node:path";
9
- import type { ParsedArgs } from "../types.js";
10
- import type { BumpDirection, BumpType } from "../types.js";
8
+ import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
9
+ import { join, relative } from "node:path";
11
10
  import { EXIT_OK, EXIT_USER } from "../constants.js";
12
11
  import { InvalidVersionError } from "../errors.js";
13
12
  import { hint, success } from "../out.js";
13
+ import type { BumpDirection, BumpType, ParsedArgs } from "../types.js";
14
14
 
15
15
  const PACKAGE_JSON = "package.json";
16
16
  const JSR_JSON = "jsr.json";
17
17
 
18
+ /** Directory names to skip when discovering package roots (monorepo). */
19
+ const SKIP_DIRS = new Set(["node_modules", ".git"]);
20
+
21
+ /**
22
+ * Recursively finds all directories under `root` that contain a package.json.
23
+ * Skips node_modules and .git.
24
+ */
25
+ function findPackageRoots(root: string): string[] {
26
+ const acc: string[] = [];
27
+ if (!existsSync(root) || !statSync(root, { throwIfNoEntry: false })?.isDirectory()) {
28
+ return acc;
29
+ }
30
+ if (existsSync(join(root, PACKAGE_JSON))) {
31
+ acc.push(root);
32
+ }
33
+ let entries: Array<{ isDirectory(): boolean; name: string }>;
34
+ try {
35
+ entries = readdirSync(root, { withFileTypes: true }) as Array<{
36
+ isDirectory(): boolean;
37
+ name: string;
38
+ }>;
39
+ } catch {
40
+ return acc;
41
+ }
42
+ for (const e of entries) {
43
+ if (!e.isDirectory() || SKIP_DIRS.has(e.name)) continue;
44
+ acc.push(...findPackageRoots(join(root, e.name)));
45
+ }
46
+ return acc;
47
+ }
48
+
18
49
  /** Semver triplet. */
19
50
  interface Semver {
20
51
  major: number;
@@ -30,18 +61,16 @@ function parseVersion(version: string): Semver {
30
61
  const trimmed = version.trim();
31
62
  const normalized = trimmed.startsWith("v") ? trimmed.slice(1) : trimmed;
32
63
  const parts = normalized.split(".");
33
- if (
34
- parts.length !== 3 ||
35
- parts.some((p) => !/^\d+$/.test(p))
36
- ) {
64
+ if (parts.length !== 3 || parts.some((p) => !/^\d+$/.test(p))) {
37
65
  throw new InvalidVersionError(
38
- `Invalid version '${version}'. Expected format: X.Y.Z or vX.Y.Z (e.g. 1.2.3 or v1.2.3).`
66
+ `Invalid version '${version}'. Expected format: X.Y.Z or vX.Y.Z (e.g. 1.2.3 or v1.2.3).`,
39
67
  );
40
68
  }
69
+ const [p0, p1, p2] = parts;
41
70
  return {
42
- major: parseInt(parts[0]!, 10),
43
- minor: parseInt(parts[1]!, 10),
44
- patch: parseInt(parts[2]!, 10),
71
+ major: parseInt(p0 ?? "0", 10),
72
+ minor: parseInt(p1 ?? "0", 10),
73
+ patch: parseInt(p2 ?? "0", 10),
45
74
  };
46
75
  }
47
76
 
@@ -103,7 +132,7 @@ function readPackageVersion(dir: string): { raw: string; semver: Semver } {
103
132
  const path = join(dir, PACKAGE_JSON);
104
133
  if (!existsSync(path)) {
105
134
  throw new InvalidVersionError(
106
- `No package.json found at ${path}. Run from project root or use -C <dir>.`
135
+ `No package.json found at ${path}. Run from project root or use -C <dir>.`,
107
136
  );
108
137
  }
109
138
  const raw = readFileSync(path, "utf-8");
@@ -111,7 +140,7 @@ function readPackageVersion(dir: string): { raw: string; semver: Semver } {
111
140
  const version = data.version;
112
141
  if (typeof version !== "string" || version.trim() === "") {
113
142
  throw new InvalidVersionError(
114
- "package.json has no valid 'version' field. Add a \"version\" field (e.g. \"0.0.0\") to package.json."
143
+ 'package.json has no valid \'version\' field. Add a "version" field (e.g. "0.0.0") to package.json.',
115
144
  );
116
145
  }
117
146
  const semver = parseVersion(version);
@@ -127,7 +156,7 @@ function writePackageVersion(dir: string, newVersion: string): void {
127
156
  const raw = readFileSync(path, "utf-8");
128
157
  const data = JSON.parse(raw) as Record<string, unknown>;
129
158
  data.version = newVersion;
130
- writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
159
+ writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
131
160
  }
132
161
 
133
162
  /**
@@ -139,7 +168,7 @@ function syncJsrVersion(dir: string, newVersion: string): boolean {
139
168
  const raw = readFileSync(path, "utf-8");
140
169
  const data = JSON.parse(raw) as Record<string, unknown>;
141
170
  data.version = newVersion;
142
- writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
171
+ writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
143
172
  return true;
144
173
  }
145
174
 
@@ -154,15 +183,14 @@ export async function run(args: ParsedArgs): Promise<void> {
154
183
  let direction: BumpDirection;
155
184
  let type: BumpType;
156
185
 
157
- const isTTY =
158
- typeof process.stdin.isTTY === "boolean" && process.stdin.isTTY;
186
+ const isTTY = typeof process.stdin.isTTY === "boolean" && process.stdin.isTTY;
159
187
 
160
188
  if (bumpDirection && bumpType) {
161
189
  direction = bumpDirection;
162
190
  type = bumpType;
163
191
  } else if (!isTTY) {
164
192
  console.error(
165
- "gflows bump: when not in a TTY, both direction and type are required. Example: gflows bump up patch"
193
+ "gflows bump: when not in a TTY, both direction and type are required. Example: gflows bump up patch",
166
194
  );
167
195
  process.exit(EXIT_USER);
168
196
  } else {
@@ -184,14 +212,33 @@ export async function run(args: ParsedArgs): Promise<void> {
184
212
  });
185
213
  }
186
214
 
187
- const { raw: oldVersion, semver } = readPackageVersion(cwd);
188
- const newSemver =
189
- direction === "up" ? bumpUp(semver, type) : bumpDown(semver, type);
215
+ let roots = findPackageRoots(cwd);
216
+ roots = [...roots].sort((a, b) => {
217
+ if (a === cwd && b !== cwd) return -1;
218
+ if (a !== cwd && b === cwd) return 1;
219
+ return a.localeCompare(b);
220
+ });
221
+ if (roots.length === 0) {
222
+ throw new InvalidVersionError(
223
+ `No package.json found under ${cwd}. Run from project root or use -C <dir>.`,
224
+ );
225
+ }
226
+ const primaryRoot = roots[0];
227
+ if (primaryRoot === undefined) {
228
+ throw new InvalidVersionError(
229
+ `No package.json found under ${cwd}. Run from project root or use -C <dir>.`,
230
+ );
231
+ }
232
+ const { raw: oldVersion, semver } = readPackageVersion(primaryRoot);
233
+ const newSemver = direction === "up" ? bumpUp(semver, type) : bumpDown(semver, type);
190
234
  const newVersion = formatVersion(newSemver);
191
235
 
192
- const filesToUpdate: string[] = [PACKAGE_JSON];
193
- if (existsSync(join(cwd, JSR_JSON))) {
194
- filesToUpdate.push(JSR_JSON);
236
+ const filesToUpdate: string[] = [];
237
+ for (const dir of roots) {
238
+ filesToUpdate.push(relative(cwd, join(dir, PACKAGE_JSON)) || PACKAGE_JSON);
239
+ if (existsSync(join(dir, JSR_JSON))) {
240
+ filesToUpdate.push(relative(cwd, join(dir, JSR_JSON)) || JSR_JSON);
241
+ }
195
242
  }
196
243
 
197
244
  if (dryRun) {
@@ -202,15 +249,18 @@ export async function run(args: ParsedArgs): Promise<void> {
202
249
  process.exit(EXIT_OK);
203
250
  }
204
251
 
205
- writePackageVersion(cwd, newVersion);
206
- const jsrUpdated = syncJsrVersion(cwd, newVersion);
252
+ const updated: string[] = [];
253
+ for (const dir of roots) {
254
+ writePackageVersion(dir, newVersion);
255
+ updated.push(relative(cwd, join(dir, PACKAGE_JSON)) || PACKAGE_JSON);
256
+ if (syncJsrVersion(dir, newVersion)) {
257
+ updated.push(relative(cwd, join(dir, JSR_JSON)) || JSR_JSON);
258
+ }
259
+ }
207
260
 
208
261
  if (!quiet) {
209
262
  success(`Bumped version: ${oldVersion} → ${newVersion}`);
210
- const updated = [PACKAGE_JSON];
211
- if (jsrUpdated) updated.push(JSR_JSON);
212
263
  success(`Updated: ${updated.join(", ")}`);
213
- // Hint: suggest next step — commit and start release branch
214
264
  hint("Commit the change, then run gflows start release vX.Y.Z to release.");
215
265
  }
216
266
  }
@@ -5,8 +5,8 @@
5
5
  * @module commands/completion
6
6
  */
7
7
 
8
- import type { ParsedArgs } from "../types.js";
9
8
  import { EXIT_USER } from "../constants.js";
9
+ import type { ParsedArgs } from "../types.js";
10
10
 
11
11
  const COMMANDS = [
12
12
  "init",
@@ -22,14 +22,7 @@ const COMMANDS = [
22
22
  "version",
23
23
  ];
24
24
 
25
- const BRANCH_TYPES = [
26
- "feature",
27
- "bugfix",
28
- "chore",
29
- "release",
30
- "hotfix",
31
- "spike",
32
- ];
25
+ const BRANCH_TYPES = ["feature", "bugfix", "chore", "release", "hotfix", "spike"];
33
26
 
34
27
  const COMPLETION_SHELLS = ["bash", "zsh", "fish"] as const;
35
28
 
@@ -46,7 +39,7 @@ function bashScript(): string {
46
39
  _gflows() {
47
40
  local cur prev words cword cmd_idx cmd
48
41
  words=(${D}COMP_WORDS[@]})
49
- cword=\$COMP_CWORD
42
+ cword=$COMP_CWORD
50
43
  cur="${D}words[cword]:-}"
51
44
  prev="${D}words[cword-1]:-}"
52
45
 
@@ -72,58 +65,58 @@ _gflows() {
72
65
  break
73
66
  fi
74
67
  done
75
- if [[ -n "\$path" ]]; then
76
- gflows -C "\$path" list 2>/dev/null
68
+ if [[ -n "$path" ]]; then
69
+ gflows -C "$path" list 2>/dev/null
77
70
  else
78
71
  gflows list 2>/dev/null
79
72
  fi
80
73
  }
81
74
 
82
75
  # Completing -C/--path value: suggest directories
83
- if [[ "\$prev" == "-C" ]] || [[ "\$prev" == "--path" ]]; then
76
+ if [[ "$prev" == "-C" ]] || [[ "$prev" == "--path" ]]; then
84
77
  compopt -o dirnames 2>/dev/null
85
- COMPREPLY=($(compgen -d -S / -- "\$cur"))
78
+ COMPREPLY=($(compgen -d -S / -- "$cur"))
86
79
  return
87
80
  fi
88
81
 
89
82
  # First positional: command
90
83
  if (( cword == cmd_idx )); then
91
- COMPREPLY=($(compgen -W "${COMMANDS.join(" ")}" -- "\$cur"))
84
+ COMPREPLY=($(compgen -W "${COMMANDS.join(" ")}" -- "$cur"))
92
85
  return
93
86
  fi
94
87
 
95
88
  # After command: type/name/branches/shell/bump by command
96
89
  case "$cmd" in
97
90
  completion)
98
- COMPREPLY=($(compgen -W "${COMPLETION_SHELLS.join(" ")}" -- "\$cur"))
91
+ COMPREPLY=($(compgen -W "${COMPLETION_SHELLS.join(" ")}" -- "$cur"))
99
92
  ;;
100
93
  start)
101
94
  if (( cword == cmd_idx + 1 )); then
102
- COMPREPLY=($(compgen -W "${BRANCH_TYPES.join(" ")}" -- "\$cur"))
95
+ COMPREPLY=($(compgen -W "${BRANCH_TYPES.join(" ")}" -- "$cur"))
103
96
  else
104
97
  COMPREPLY=()
105
98
  fi
106
99
  ;;
107
100
  finish)
108
- if [[ "\$prev" == "-B" ]] || [[ "\$prev" == "--branch" ]]; then
109
- COMPREPLY=($(compgen -W "$(_gflows_path)" -- "\$cur"))
101
+ if [[ "$prev" == "-B" ]] || [[ "$prev" == "--branch" ]]; then
102
+ COMPREPLY=($(compgen -W "$(_gflows_path)" -- "$cur"))
110
103
  elif (( cword == cmd_idx + 1 )); then
111
- COMPREPLY=($(compgen -W "${BRANCH_TYPES.join(" ")}" -- "\$cur"))
104
+ COMPREPLY=($(compgen -W "${BRANCH_TYPES.join(" ")}" -- "$cur"))
112
105
  else
113
106
  COMPREPLY=()
114
107
  fi
115
108
  ;;
116
109
  list)
117
- COMPREPLY=($(compgen -W "${BRANCH_TYPES.join(" ")}" -- "\$cur"))
110
+ COMPREPLY=($(compgen -W "${BRANCH_TYPES.join(" ")}" -- "$cur"))
118
111
  ;;
119
112
  switch|delete)
120
- COMPREPLY=($(compgen -W "$(_gflows_path)" -- "\$cur"))
113
+ COMPREPLY=($(compgen -W "$(_gflows_path)" -- "$cur"))
121
114
  ;;
122
115
  bump)
123
116
  if (( cword == cmd_idx + 1 )); then
124
- COMPREPLY=($(compgen -W "${BUMP_DIRECTIONS.join(" ")}" -- "\$cur"))
117
+ COMPREPLY=($(compgen -W "${BUMP_DIRECTIONS.join(" ")}" -- "$cur"))
125
118
  elif (( cword == cmd_idx + 2 )); then
126
- COMPREPLY=($(compgen -W "${BUMP_TYPES.join(" ")}" -- "\$cur"))
119
+ COMPREPLY=($(compgen -W "${BUMP_TYPES.join(" ")}" -- "$cur"))
127
120
  else
128
121
  COMPREPLY=()
129
122
  fi
@@ -169,7 +162,7 @@ _gflows() {
169
162
 
170
163
  case $state in
171
164
  args)
172
- cur=\$words[CURRENT]
165
+ cur=$words[CURRENT]
173
166
  cmd_idx=1
174
167
  while (( cmd_idx < CURRENT )); do
175
168
  if [[ "${D}words[cmd_idx]}" == "-C" ]] || [[ "${D}words[cmd_idx]}" == "--path" ]]; then
@@ -190,7 +183,7 @@ _gflows() {
190
183
  ;;
191
184
  finish)
192
185
  if [[ "${D}words[CURRENT-1]}" == "-B" ]] || [[ "${D}words[CURRENT-1]}" == "--branch" ]]; then
193
- _values "branch" \$(_gflows_list_branches)
186
+ _values "branch" $(_gflows_list_branches)
194
187
  else
195
188
  _values "type" ${BRANCH_TYPES.map((t) => `"${t}"`).join(" ")}
196
189
  fi
@@ -199,7 +192,7 @@ _gflows() {
199
192
  _values "type" ${BRANCH_TYPES.map((t) => `"${t}"`).join(" ")}
200
193
  ;;
201
194
  switch|delete)
202
- _values "branch" \$(_gflows_list_branches)
195
+ _values "branch" $(_gflows_list_branches)
203
196
  ;;
204
197
  bump)
205
198
  if [[ "${D}words[CURRENT-1]}" == "up" ]] || [[ "${D}words[CURRENT-1]}" == "down" ]]; then
@@ -326,9 +319,7 @@ complete -c gflows -f -n "__fish_seen_subcommand_from ${COMMANDS.join(" ")}" -l
326
319
  export async function run(args: ParsedArgs): Promise<void> {
327
320
  const shell = args.completionShell;
328
321
  if (!shell) {
329
- console.error(
330
- "gflows: completion requires a shell. Use: gflows completion bash | zsh | fish"
331
- );
322
+ console.error("gflows: completion requires a shell. Use: gflows completion bash | zsh | fish");
332
323
  process.exit(EXIT_USER);
333
324
  }
334
325
 
@@ -3,38 +3,24 @@
3
3
  * @module commands/delete
4
4
  */
5
5
 
6
- import type { BranchType } from "../types.js";
7
- import type { ParsedArgs } from "../types.js";
8
6
  import { resolveConfig } from "../config.js";
9
7
  import { EXIT_OK, EXIT_USER } from "../constants.js";
10
8
  import { CannotDeleteMainOrDevError, NotRepoError } from "../errors.js";
11
- import {
12
- branchList,
13
- deleteBranch,
14
- resolveRepoRoot,
15
- } from "../git.js";
9
+ import { branchList, deleteBranch, resolveRepoRoot } from "../git.js";
16
10
  import { hint, success } from "../out.js";
11
+ import type { BranchType, ParsedArgs } from "../types.js";
17
12
 
18
- const BRANCH_TYPES: BranchType[] = [
19
- "feature",
20
- "bugfix",
21
- "chore",
22
- "release",
23
- "hotfix",
24
- "spike",
25
- ];
13
+ const BRANCH_TYPES: BranchType[] = ["feature", "bugfix", "chore", "release", "hotfix", "spike"];
26
14
 
27
15
  /**
28
16
  * Returns local branch names that match any workflow prefix (feature/, bugfix/, etc.).
29
17
  */
30
18
  function getWorkflowBranches(
31
19
  allBranches: string[],
32
- prefixes: Record<BranchType, string>
20
+ prefixes: Record<BranchType, string>,
33
21
  ): string[] {
34
22
  const prefixed = BRANCH_TYPES.map((t) => prefixes[t]).filter(Boolean);
35
- return allBranches.filter((b) =>
36
- prefixed.some((p) => p && b.startsWith(p))
37
- );
23
+ return allBranches.filter((b) => prefixed.some((p) => p && b.startsWith(p)));
38
24
  }
39
25
 
40
26
  /**
@@ -58,16 +44,12 @@ export async function run(args: ParsedArgs): Promise<void> {
58
44
  });
59
45
  const { main, dev, prefixes } = config;
60
46
 
61
- const fromPositionals = (rawBranchNames ?? [])
62
- .map((s) => s.trim())
63
- .filter(Boolean);
47
+ const fromPositionals = (rawBranchNames ?? []).map((s) => s.trim()).filter(Boolean);
64
48
 
65
49
  if (fromPositionals.length > 0) {
66
50
  for (const branch of fromPositionals) {
67
51
  if (branch === main || branch === dev) {
68
- throw new CannotDeleteMainOrDevError(
69
- `Cannot delete the long-lived branch '${branch}'.`
70
- );
52
+ throw new CannotDeleteMainOrDevError(`Cannot delete the long-lived branch '${branch}'.`);
71
53
  }
72
54
  }
73
55
  for (const branch of fromPositionals) {
@@ -89,7 +71,7 @@ export async function run(args: ParsedArgs): Promise<void> {
89
71
  const isTTY = typeof process.stdin.isTTY === "boolean" && process.stdin.isTTY;
90
72
  if (!isTTY) {
91
73
  console.error(
92
- "gflows delete: no branch name(s) given and stdin is not a TTY. Pass branch name(s) (e.g. gflows delete feature/my-branch) or run from an interactive terminal."
74
+ "gflows delete: no branch name(s) given and stdin is not a TTY. Pass branch name(s) (e.g. gflows delete feature/my-branch) or run from an interactive terminal.",
93
75
  );
94
76
  process.exit(EXIT_USER);
95
77
  }
@@ -103,7 +85,7 @@ export async function run(args: ParsedArgs): Promise<void> {
103
85
  if (workflowBranches.length === 0) {
104
86
  if (!quiet) {
105
87
  console.error(
106
- "No workflow branches to delete. Create one with 'gflows start <type> <name>'."
88
+ "No workflow branches to delete. Create one with 'gflows start <type> <name>'.",
107
89
  );
108
90
  }
109
91
  process.exit(EXIT_USER);
@@ -124,9 +106,7 @@ export async function run(args: ParsedArgs): Promise<void> {
124
106
 
125
107
  for (const branch of chosen) {
126
108
  if (branch === main || branch === dev) {
127
- throw new CannotDeleteMainOrDevError(
128
- `Cannot delete the long-lived branch '${branch}'.`
129
- );
109
+ throw new CannotDeleteMainOrDevError(`Cannot delete the long-lived branch '${branch}'.`);
130
110
  }
131
111
  }
132
112