gflows 0.1.11 → 0.1.13
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 +120 -12
- package/package.json +1 -1
- package/src/cli.ts +46 -1
- package/src/commands/bump.ts +2 -1
- package/src/commands/delete.ts +2 -0
- package/src/commands/finish.ts +17 -4
- package/src/commands/help.ts +1 -1
- package/src/commands/init.ts +1 -0
- package/src/commands/list.ts +7 -5
- package/src/commands/start.ts +1 -0
- package/src/commands/status.ts +1 -0
- package/src/commands/switch.ts +10 -3
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# gFlows
|
|
2
2
|
|
|
3
3
|
A lightweight CLI for consistent Git branching workflows: long-lived **main** (production) and **dev** (integration), plus short-lived workflow branches with clear merge targets. Built for [Bun](https://bun.sh) and TypeScript; **scriptable** and **safe by default**—no history rewriting, predictable exit codes, and optional interactive pickers only when running in a TTY.
|
|
4
4
|
|
|
@@ -171,7 +171,7 @@ gflows finish hotfix --push # merge to main, then dev; tag v1.3.1;
|
|
|
171
171
|
| `delete` | `-L` | Delete local workflow branch(es). Never main/dev. |
|
|
172
172
|
| `list` | `-l` | List workflow branches; optional type filter and remote. |
|
|
173
173
|
| `bump` | — | Bump or rollback package version (patch/minor/major). |
|
|
174
|
-
| `completion` | — | Print shell completion script (bash
|
|
174
|
+
| `completion` | — | Print shell completion script (bash/zsh/fish). |
|
|
175
175
|
| `status` | `-t` | Show current branch, type, base, merge target(s), ahead/behind. |
|
|
176
176
|
| `help` | `-h` | Show usage and quick reference. |
|
|
177
177
|
| `version` | `-V` | Show version. |
|
|
@@ -179,13 +179,32 @@ gflows finish hotfix --push # merge to main, then dev; tag v1.3.1;
|
|
|
179
179
|
|
|
180
180
|
**Branch types (for start/finish/list):** `feature` (`-f`), `bugfix` (`-b`), `chore` (`-c`), `release` (`-r`), `hotfix` (`-x`), `spike` (`-e`).
|
|
181
181
|
|
|
182
|
+
**Common flags** (used by multiple commands):
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
| Flag | Short | Description |
|
|
186
|
+
| ----------------- | ----- | ------------------------------------- |
|
|
187
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
188
|
+
| `--dry-run` | `-d` | Log intended actions only; no writes. |
|
|
189
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
190
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
191
|
+
| `--push` | `-p` | Push after init/start/finish. |
|
|
192
|
+
| `--no-push` | `-P` | Do not push. |
|
|
193
|
+
| `--main <name>` | — | Main branch override. |
|
|
194
|
+
| `--dev <name>` | — | Dev branch override. |
|
|
195
|
+
| `--remote <name>` | `-R` | Remote for push. |
|
|
196
|
+
| `--from <branch>` | `-o` | Base branch override (start). |
|
|
197
|
+
| `--branch <name>` | `-B` | Branch name (finish). |
|
|
198
|
+
| `--yes` | `-y` | Skip confirmations. |
|
|
199
|
+
|
|
200
|
+
|
|
182
201
|
---
|
|
183
202
|
|
|
184
203
|
### init
|
|
185
204
|
|
|
186
205
|
Ensures the **main** branch exists (exits with error if not). Creates **dev** from main if it does not exist; does nothing if dev already exists. Does not rewrite or force-push.
|
|
187
206
|
|
|
188
|
-
You can set and persist config with `**--main`**, `**--dev
|
|
207
|
+
You can set and persist config with `**--main`**, `**--dev`**, and `**-R`/`--remote**`. Any of these flags cause init to write or update `.gflows.json` with the given values (after a successful init; skipped with `--dry-run`).
|
|
189
208
|
|
|
190
209
|
**Examples:**
|
|
191
210
|
|
|
@@ -197,7 +216,20 @@ gflows init -C ../other-repo # run in another directory
|
|
|
197
216
|
gflows init --dry-run # log intended actions only
|
|
198
217
|
```
|
|
199
218
|
|
|
200
|
-
**Flags:**
|
|
219
|
+
**Flags:**
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
| Flag | Short | Description |
|
|
223
|
+
| ----------------- | ----- | ------------------------------------------------------------- |
|
|
224
|
+
| `--push` | `-p` | Push dev to remote after creating. |
|
|
225
|
+
| `--main <name>` | — | Main branch name (persisted to `.gflows.json` when provided). |
|
|
226
|
+
| `--dev <name>` | — | Dev branch name (persisted to `.gflows.json` when provided). |
|
|
227
|
+
| `--remote <name>` | `-R` | Remote name (persisted to `.gflows.json` when provided). |
|
|
228
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
229
|
+
| `--dry-run` | `-d` | Log intended actions only; no writes. |
|
|
230
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
231
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
232
|
+
|
|
201
233
|
|
|
202
234
|
---
|
|
203
235
|
|
|
@@ -220,7 +252,20 @@ gflows start feature api-v2 --push # create branch and push to rem
|
|
|
220
252
|
gflows start chore deps-update -C ./backend # run in subdirectory
|
|
221
253
|
```
|
|
222
254
|
|
|
223
|
-
**Flags:**
|
|
255
|
+
**Flags:**
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
| Flag | Short | Description |
|
|
259
|
+
| ----------------- | ----- | ------------------------------------------------- |
|
|
260
|
+
| `--force` | — | Allow dirty working tree. |
|
|
261
|
+
| `--push` | `-p` | Push new branch to remote after creating. |
|
|
262
|
+
| `--from <branch>` | `-o` | Base branch override (e.g. `-o main` for bugfix). |
|
|
263
|
+
| `--remote <name>` | `-R` | Remote for push. |
|
|
264
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
265
|
+
| `--dry-run` | `-d` | Log intended actions only; no writes. |
|
|
266
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
267
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
268
|
+
|
|
224
269
|
|
|
225
270
|
---
|
|
226
271
|
|
|
@@ -245,7 +290,27 @@ gflows finish -y # skip "Delete branch after finish?"
|
|
|
245
290
|
|
|
246
291
|
**Branch resolution:** If you omit the branch name, gflows uses the current branch. With `-B` and no value in a TTY, it shows a picker of workflow branches. Without a TTY, you must pass the branch name explicitly.
|
|
247
292
|
|
|
248
|
-
**Flags:**
|
|
293
|
+
**Flags:**
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
| Flag | Short | Description |
|
|
297
|
+
| --------------------- | ----- | ------------------------------------------------------------------------------------ |
|
|
298
|
+
| `--branch <name>` | `-B` | Branch to finish (current branch if omitted; picker in TTY when `-B` with no value). |
|
|
299
|
+
| `--no-ff` | — | Always create a merge commit. |
|
|
300
|
+
| `--delete` | `-D` | Delete branch after finish. |
|
|
301
|
+
| `--no-delete` | `-N` | Do not delete branch after finish. |
|
|
302
|
+
| `--push` | `-p` | Push after merge (finish prompts "Do you want to push?" when neither `-p` nor `-P`). |
|
|
303
|
+
| `--no-push` | `-P` | Do not push. |
|
|
304
|
+
| `--sign` | `-s` | Sign the tag (release/hotfix; GPG). |
|
|
305
|
+
| `--no-tag` | `-T` | Do not create tag (release/hotfix). |
|
|
306
|
+
| `--tag-message <msg>` | `-M` | Tag message. |
|
|
307
|
+
| `--message <msg>` | `-m` | Merge message. |
|
|
308
|
+
| `--yes` | `-y` | Skip confirmations (e.g. "Delete branch after finish?"). |
|
|
309
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
310
|
+
| `--dry-run` | `-d` | Log intended actions only; no writes. |
|
|
311
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
312
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
313
|
+
|
|
249
314
|
|
|
250
315
|
---
|
|
251
316
|
|
|
@@ -261,7 +326,15 @@ gflows switch feature/auth-refactor
|
|
|
261
326
|
gflows -W feature/auth-refactor # same with short command
|
|
262
327
|
```
|
|
263
328
|
|
|
264
|
-
**Flags:**
|
|
329
|
+
**Flags:**
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
| Flag | Short | Description |
|
|
333
|
+
| -------------- | ----- | --------------------- |
|
|
334
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
335
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
336
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
337
|
+
|
|
265
338
|
|
|
266
339
|
---
|
|
267
340
|
|
|
@@ -277,7 +350,15 @@ gflows delete feature/old-spike
|
|
|
277
350
|
gflows delete feature/one feature/two # delete multiple
|
|
278
351
|
```
|
|
279
352
|
|
|
280
|
-
**Flags:**
|
|
353
|
+
**Flags:**
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
| Flag | Short | Description |
|
|
357
|
+
| -------------- | ----- | --------------------- |
|
|
358
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
359
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
360
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
361
|
+
|
|
281
362
|
|
|
282
363
|
---
|
|
283
364
|
|
|
@@ -295,7 +376,17 @@ gflows list -r feature # remote + local feature branches
|
|
|
295
376
|
gflows list --include-remote
|
|
296
377
|
```
|
|
297
378
|
|
|
298
|
-
**Flags:**
|
|
379
|
+
**Flags:**
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
| Flag | Short | Description |
|
|
383
|
+
| ------------------ | ----- | ------------------------------------------------------- |
|
|
384
|
+
| `--include-remote` | `-r` | Include remote-tracking branches (may run `git fetch`). |
|
|
385
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
386
|
+
| `--dry-run` | `-d` | Log intended actions only. |
|
|
387
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
388
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
389
|
+
|
|
299
390
|
|
|
300
391
|
---
|
|
301
392
|
|
|
@@ -317,7 +408,16 @@ gflows bump # interactive (direction + type) when T
|
|
|
317
408
|
gflows bump --dry-run # print old → new, no file writes
|
|
318
409
|
```
|
|
319
410
|
|
|
320
|
-
**Flags:**
|
|
411
|
+
**Flags:**
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
| Flag | Short | Description |
|
|
415
|
+
| -------------- | ----- | --------------------------------------------- |
|
|
416
|
+
| `--dry-run` | `-d` | Print old → new version only; no file writes. |
|
|
417
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
418
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
419
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
420
|
+
|
|
321
421
|
|
|
322
422
|
---
|
|
323
423
|
|
|
@@ -332,7 +432,15 @@ gflows status
|
|
|
332
432
|
gflows -t
|
|
333
433
|
```
|
|
334
434
|
|
|
335
|
-
**Flags:**
|
|
435
|
+
**Flags:**
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
| Flag | Short | Description |
|
|
439
|
+
| -------------- | ----- | --------------------- |
|
|
440
|
+
| `--path <dir>` | `-C` | Run as if in `<dir>`. |
|
|
441
|
+
| `--verbose` | `-v` | Verbose output. |
|
|
442
|
+
| `--quiet` | `-q` | Minimal output. |
|
|
443
|
+
|
|
336
444
|
|
|
337
445
|
---
|
|
338
446
|
|
|
@@ -386,7 +494,7 @@ Configuration is **optional**. Override branch names, remote, and branch **prefi
|
|
|
386
494
|
**Resolution order** (later overrides earlier):
|
|
387
495
|
|
|
388
496
|
1. Built-in defaults (`main`, `dev`, `origin`, and default prefixes).
|
|
389
|
-
2. Repo config file: `**.gflows.json`** in repo root, or `**gflows
|
|
497
|
+
2. Repo config file: `**.gflows.json`** in repo root, or `**gflows`** key in `**package.json`**.
|
|
390
498
|
3. CLI (e.g. `--main`, `--dev`, `-R`/`--remote`).
|
|
391
499
|
|
|
392
500
|
Only include keys you want to override; the rest stay default. Invalid or malformed config is ignored (with an optional warning when using `-v`).
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -123,6 +123,45 @@ function resolveCwd(pathFlag: string | undefined): string {
|
|
|
123
123
|
return absolute;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Returns the command name closest to `input` by edit distance, or undefined if no close match.
|
|
128
|
+
* Used for "did you mean?" when the user mistypes a command.
|
|
129
|
+
*/
|
|
130
|
+
function closestCommand(input: string): Command | undefined {
|
|
131
|
+
if (!input || input.length < 2) return undefined;
|
|
132
|
+
const target = input.toLowerCase();
|
|
133
|
+
let best: Command | undefined;
|
|
134
|
+
let bestDistance = 3; // only suggest if within 2 edits
|
|
135
|
+
for (const cmd of COMMANDS) {
|
|
136
|
+
const d = editDistance(target, cmd);
|
|
137
|
+
if (d < bestDistance) {
|
|
138
|
+
bestDistance = d;
|
|
139
|
+
best = cmd as Command;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return best;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Levenshtein edit distance between two strings. */
|
|
146
|
+
function editDistance(a: string, b: string): number {
|
|
147
|
+
const m = a.length;
|
|
148
|
+
const n = b.length;
|
|
149
|
+
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;
|
|
152
|
+
for (let i = 1; i <= m; i++) {
|
|
153
|
+
for (let j = 1; j <= n; j++) {
|
|
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
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return dp[m]![n]!;
|
|
163
|
+
}
|
|
164
|
+
|
|
126
165
|
/** Resolve command from positionals and short flags. Short wins if both present. */
|
|
127
166
|
function resolveCommand(
|
|
128
167
|
positionals: string[],
|
|
@@ -240,7 +279,13 @@ export function parse(argv: string[] = Bun.argv.slice(2)): ParsedArgs {
|
|
|
240
279
|
|
|
241
280
|
const command = resolveCommand(positionals, v);
|
|
242
281
|
if (!command) {
|
|
243
|
-
|
|
282
|
+
const first = positionals[0];
|
|
283
|
+
const suggestion = typeof first === "string" ? closestCommand(first) : undefined;
|
|
284
|
+
if (suggestion) {
|
|
285
|
+
console.error(`gflows: unknown command '${first}'. Did you mean '${suggestion}'?`);
|
|
286
|
+
} else {
|
|
287
|
+
console.error("gflows: missing command. Use 'gflows help' for usage.");
|
|
288
|
+
}
|
|
244
289
|
process.exit(EXIT_USER);
|
|
245
290
|
}
|
|
246
291
|
|
package/src/commands/bump.ts
CHANGED
|
@@ -111,7 +111,7 @@ function readPackageVersion(dir: string): { raw: string; semver: Semver } {
|
|
|
111
111
|
const version = data.version;
|
|
112
112
|
if (typeof version !== "string" || version.trim() === "") {
|
|
113
113
|
throw new InvalidVersionError(
|
|
114
|
-
"package.json has no valid 'version' field."
|
|
114
|
+
"package.json has no valid 'version' field. Add a \"version\" field (e.g. \"0.0.0\") to package.json."
|
|
115
115
|
);
|
|
116
116
|
}
|
|
117
117
|
const semver = parseVersion(version);
|
|
@@ -210,6 +210,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
210
210
|
const updated = [PACKAGE_JSON];
|
|
211
211
|
if (jsrUpdated) updated.push(JSR_JSON);
|
|
212
212
|
success(`Updated: ${updated.join(", ")}`);
|
|
213
|
+
// Hint: suggest next step — commit and start release branch
|
|
213
214
|
hint("Commit the change, then run gflows start release vX.Y.Z to release.");
|
|
214
215
|
}
|
|
215
216
|
}
|
package/src/commands/delete.ts
CHANGED
|
@@ -80,6 +80,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
if (!quiet && !dryRun) {
|
|
83
|
+
// Hint: suggest listing remaining branches
|
|
83
84
|
hint("Use gflows list to see remaining workflow branches.");
|
|
84
85
|
}
|
|
85
86
|
return;
|
|
@@ -139,6 +140,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
if (!quiet && !dryRun && chosen.length > 0) {
|
|
143
|
+
// Hint: suggest listing remaining branches
|
|
142
144
|
hint("Use gflows list to see remaining workflow branches.");
|
|
143
145
|
}
|
|
144
146
|
}
|
package/src/commands/finish.ts
CHANGED
|
@@ -242,10 +242,21 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
const
|
|
246
|
-
const didCreateTag = !!(
|
|
245
|
+
const createdTagName =
|
|
247
246
|
meta.mergeTarget === "main-then-dev" && meta.tagOnFinish && version && !args.noTag
|
|
248
|
-
|
|
247
|
+
? normalizeTagVersion(version)
|
|
248
|
+
: undefined;
|
|
249
|
+
const didCreateTag = Boolean(createdTagName);
|
|
250
|
+
|
|
251
|
+
let doPush = args.push && !args.noPush;
|
|
252
|
+
if (!args.push && !args.noPush && isTTY) {
|
|
253
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
254
|
+
doPush = await confirm({
|
|
255
|
+
message: "Do you want to push?",
|
|
256
|
+
default: true,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
249
260
|
if (doPush) {
|
|
250
261
|
const remote = args.remote ?? config.remote;
|
|
251
262
|
const refsToPush: string[] = [config.dev];
|
|
@@ -271,7 +282,9 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
271
282
|
}
|
|
272
283
|
|
|
273
284
|
if (!args.quiet && !args.dryRun) {
|
|
274
|
-
|
|
285
|
+
const tagSuffix = createdTagName ? ` (tag ${createdTagName})` : "";
|
|
286
|
+
success(`gflows: finished '${branchToFinish}' into ${meta.mergeTarget}${tagSuffix}.`);
|
|
287
|
+
// Hint: suggest next step — create a new workflow branch
|
|
275
288
|
hint("Run gflows start <type> <name> to create a new workflow branch.");
|
|
276
289
|
}
|
|
277
290
|
}
|
package/src/commands/help.ts
CHANGED
|
@@ -32,7 +32,7 @@ Types: feature (-f), bugfix (-b), chore (-c), release (-r), hotfix (-x), spike (
|
|
|
32
32
|
|
|
33
33
|
Common flags:
|
|
34
34
|
-p, --push Push after init/start/finish
|
|
35
|
-
-P, --no-push Do not push
|
|
35
|
+
-P, --no-push Do not push (finish: prompts "Do you want to push?" when neither -p nor -P)
|
|
36
36
|
--main <name> Main branch (init: persist to .gflows.json)
|
|
37
37
|
--dev <name> Dev branch (init: persist to .gflows.json)
|
|
38
38
|
-R, --remote <name> Remote for push (init: persist to .gflows.json)
|
package/src/commands/init.ts
CHANGED
package/src/commands/list.ts
CHANGED
|
@@ -82,16 +82,18 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
82
82
|
typeFilter
|
|
83
83
|
);
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
// Show main and dev first (when present), then workflow branches
|
|
86
|
+
const mainAndDev = [config.main, config.dev].filter((b) =>
|
|
87
|
+
allBranches.includes(b)
|
|
88
|
+
);
|
|
89
|
+
const sorted = [...mainAndDev, ...[...workflowBranches].sort()];
|
|
86
90
|
|
|
87
91
|
for (const b of sorted) {
|
|
88
92
|
console.log(b);
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
if (!quiet && sorted.length
|
|
92
|
-
|
|
93
|
-
hint("Run gflows start <type> <name> to create a workflow branch.");
|
|
94
|
-
} else if (!quiet && sorted.length > 0) {
|
|
95
|
+
if (!quiet && sorted.length > 0) {
|
|
96
|
+
// Hint: suggest switching to a listed branch
|
|
95
97
|
hint("Use gflows switch <branch> to switch to a branch.");
|
|
96
98
|
}
|
|
97
99
|
}
|
package/src/commands/start.ts
CHANGED
|
@@ -141,6 +141,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
if (!args.quiet && !args.dryRun) {
|
|
144
|
+
// Hint: suggest next step — merge branch when done
|
|
144
145
|
hint(`When done, run gflows finish ${type} to merge into the target branch.`);
|
|
145
146
|
}
|
|
146
147
|
}
|
package/src/commands/status.ts
CHANGED
|
@@ -138,6 +138,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
138
138
|
|
|
139
139
|
if (!quiet) {
|
|
140
140
|
console.log(`Ahead/behind: ${ahead} ahead, ${behind} behind`);
|
|
141
|
+
// Hint: suggest next step — finish current branch
|
|
141
142
|
hint(`Run gflows finish ${classification} to merge into ${mergeTargetDisplay}.`);
|
|
142
143
|
}
|
|
143
144
|
}
|
package/src/commands/switch.ts
CHANGED
|
@@ -65,6 +65,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
65
65
|
});
|
|
66
66
|
if (!quiet && !dryRun) {
|
|
67
67
|
success(`Switched to branch '${branchName}'.`);
|
|
68
|
+
// Hint: suggest listing branches
|
|
68
69
|
hint("Use gflows list to see all workflow branches.");
|
|
69
70
|
}
|
|
70
71
|
return;
|
|
@@ -80,10 +81,15 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
80
81
|
|
|
81
82
|
const allLocal = await branchList(root, { dryRun, verbose: args.verbose });
|
|
82
83
|
const workflowBranches = getWorkflowBranches(allLocal, config.prefixes);
|
|
84
|
+
// Include main and dev so we always show whatever branches exist
|
|
85
|
+
const mainAndDev = [config.main, config.dev].filter((b) =>
|
|
86
|
+
allLocal.includes(b)
|
|
87
|
+
);
|
|
88
|
+
const choices = [...mainAndDev, ...workflowBranches];
|
|
83
89
|
|
|
84
|
-
if (
|
|
90
|
+
if (choices.length === 0) {
|
|
85
91
|
if (!quiet) {
|
|
86
|
-
console.error("No
|
|
92
|
+
console.error("No branches found.");
|
|
87
93
|
}
|
|
88
94
|
process.exit(EXIT_OK);
|
|
89
95
|
}
|
|
@@ -91,7 +97,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
91
97
|
const { select } = await import("@inquirer/prompts");
|
|
92
98
|
const chosen = await select({
|
|
93
99
|
message: "Switch to branch",
|
|
94
|
-
choices:
|
|
100
|
+
choices: choices.map((b) => ({ name: b, value: b })),
|
|
95
101
|
});
|
|
96
102
|
|
|
97
103
|
if (typeof chosen !== "string") {
|
|
@@ -104,6 +110,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
104
110
|
});
|
|
105
111
|
if (!quiet && !dryRun) {
|
|
106
112
|
success(`Switched to branch '${chosen}'.`);
|
|
113
|
+
// Hint: suggest listing branches
|
|
107
114
|
hint("Use gflows list to see all workflow branches.");
|
|
108
115
|
}
|
|
109
116
|
}
|