contribute-now 0.2.0-dev.2621ffa → 0.2.0-dev.69b11fd

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.
Files changed (2) hide show
  1. package/dist/index.js +630 -1067
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3,11 +3,27 @@ import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // src/index.ts
6
- import { defineCommand as defineCommand14, runMain } from "citty";
6
+ import { defineCommand as defineCommand12, runMain } from "citty";
7
7
 
8
- // src/commands/branch.ts
8
+ // src/commands/clean.ts
9
9
  import { defineCommand } from "citty";
10
- import pc2 from "picocolors";
10
+ import pc4 from "picocolors";
11
+
12
+ // src/utils/branch.ts
13
+ var DEFAULT_PREFIXES = ["feature", "fix", "docs", "chore", "test", "refactor"];
14
+ function hasPrefix(branchName, prefixes = DEFAULT_PREFIXES) {
15
+ return prefixes.some((p) => branchName.startsWith(`${p}/`));
16
+ }
17
+ function formatBranchName(prefix, name) {
18
+ const sanitized = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
19
+ return `${prefix}/${sanitized}`;
20
+ }
21
+ function isValidBranchName(name) {
22
+ return /^[a-zA-Z0-9._/-]+$/.test(name) && !name.startsWith("/") && !name.endsWith("/");
23
+ }
24
+ function looksLikeNaturalLanguage(input) {
25
+ return input.includes(" ") && !input.includes("/");
26
+ }
11
27
 
12
28
  // src/utils/config.ts
13
29
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
@@ -64,623 +80,9 @@ function getDefaultConfig() {
64
80
  };
65
81
  }
66
82
 
67
- // src/utils/git.ts
68
- import { execFile as execFileCb } from "node:child_process";
69
- import { readFileSync as readFileSync2 } from "node:fs";
70
- import { join as join2 } from "node:path";
71
- function run(args) {
72
- return new Promise((resolve) => {
73
- execFileCb("git", args, (error, stdout, stderr) => {
74
- resolve({
75
- exitCode: error ? error.code === "ENOENT" ? 127 : error.status ?? 1 : 0,
76
- stdout: stdout ?? "",
77
- stderr: stderr ?? ""
78
- });
79
- });
80
- });
81
- }
82
- async function isGitRepo() {
83
- const { exitCode } = await run(["rev-parse", "--is-inside-work-tree"]);
84
- return exitCode === 0;
85
- }
86
- async function getCurrentBranch() {
87
- const { exitCode, stdout } = await run(["rev-parse", "--abbrev-ref", "HEAD"]);
88
- if (exitCode !== 0)
89
- return null;
90
- return stdout.trim() || null;
91
- }
92
- async function getRemotes() {
93
- const { exitCode, stdout } = await run(["remote"]);
94
- if (exitCode !== 0)
95
- return [];
96
- return stdout.trim().split(`
97
- `).map((r) => r.trim()).filter(Boolean);
98
- }
99
- async function getRemoteUrl(remote) {
100
- const { exitCode, stdout } = await run(["remote", "get-url", remote]);
101
- if (exitCode !== 0)
102
- return null;
103
- return stdout.trim() || null;
104
- }
105
- async function hasUncommittedChanges() {
106
- const { exitCode, stdout } = await run(["status", "--porcelain"]);
107
- if (exitCode !== 0)
108
- return false;
109
- return stdout.trim().length > 0;
110
- }
111
- async function fetchRemote(remote) {
112
- return run(["fetch", remote]);
113
- }
114
- async function fetchAll() {
115
- return run(["fetch", "--all", "--quiet"]);
116
- }
117
- async function checkoutBranch2(branch) {
118
- return run(["checkout", branch]);
119
- }
120
- async function createBranch(branch, from) {
121
- const args = from ? ["checkout", "-b", branch, from] : ["checkout", "-b", branch];
122
- return run(args);
123
- }
124
- async function resetHard(ref) {
125
- return run(["reset", "--hard", ref]);
126
- }
127
- async function updateLocalBranch(branch, target) {
128
- const current = await getCurrentBranch();
129
- if (current === branch) {
130
- return resetHard(target);
131
- }
132
- return run(["branch", "-f", branch, target]);
133
- }
134
- async function pushSetUpstream(remote, branch) {
135
- return run(["push", "-u", remote, branch]);
136
- }
137
- async function rebase(branch) {
138
- return run(["rebase", branch]);
139
- }
140
- async function getUpstreamRef() {
141
- const { exitCode, stdout } = await run([
142
- "rev-parse",
143
- "--abbrev-ref",
144
- "--symbolic-full-name",
145
- "@{u}"
146
- ]);
147
- if (exitCode !== 0)
148
- return null;
149
- return stdout.trim() || null;
150
- }
151
- async function unsetUpstream() {
152
- return run(["branch", "--unset-upstream"]);
153
- }
154
- async function rebaseOnto(newBase, oldBase) {
155
- return run(["rebase", "--onto", newBase, oldBase]);
156
- }
157
- async function getMergeBase(ref1, ref2) {
158
- const { exitCode, stdout } = await run(["merge-base", ref1, ref2]);
159
- if (exitCode !== 0)
160
- return null;
161
- return stdout.trim() || null;
162
- }
163
- async function getCommitHash(ref) {
164
- const { exitCode, stdout } = await run(["rev-parse", ref]);
165
- if (exitCode !== 0)
166
- return null;
167
- return stdout.trim() || null;
168
- }
169
- async function determineRebaseStrategy(currentBranch, syncRef) {
170
- const upstreamRef = await getUpstreamRef();
171
- if (!upstreamRef) {
172
- return { strategy: "plain" };
173
- }
174
- const upstreamHash = await getCommitHash(upstreamRef);
175
- if (!upstreamHash) {
176
- return { strategy: "plain" };
177
- }
178
- const slashIdx = upstreamRef.indexOf("/");
179
- const upstreamBranchName = slashIdx !== -1 ? upstreamRef.slice(slashIdx + 1) : upstreamRef;
180
- if (upstreamBranchName === currentBranch) {
181
- return { strategy: "plain" };
182
- }
183
- const [forkFromUpstream, forkFromSync] = await Promise.all([
184
- getMergeBase("HEAD", upstreamRef),
185
- getMergeBase("HEAD", syncRef)
186
- ]);
187
- if (forkFromUpstream && forkFromSync && forkFromUpstream === forkFromSync) {
188
- return { strategy: "plain" };
189
- }
190
- if (forkFromUpstream) {
191
- return { strategy: "onto", ontoOldBase: forkFromUpstream };
192
- }
193
- return { strategy: "plain" };
194
- }
195
- async function getStagedDiff() {
196
- const { stdout } = await run(["diff", "--cached"]);
197
- return stdout;
198
- }
199
- async function getStagedFiles() {
200
- const { exitCode, stdout } = await run(["diff", "--cached", "--name-only"]);
201
- if (exitCode !== 0)
202
- return [];
203
- return stdout.trim().split(`
204
- `).filter(Boolean);
205
- }
206
- async function getChangedFiles() {
207
- const { exitCode, stdout } = await run(["status", "--porcelain"]);
208
- if (exitCode !== 0)
209
- return [];
210
- return stdout.trimEnd().split(`
211
- `).filter(Boolean).map((l) => {
212
- const line = l.replace(/\r$/, "");
213
- const match = line.match(/^..\s+(.*)/);
214
- if (!match)
215
- return "";
216
- const file = match[1];
217
- const renameIdx = file.indexOf(" -> ");
218
- return renameIdx !== -1 ? file.slice(renameIdx + 4) : file;
219
- }).filter(Boolean);
220
- }
221
- async function getDivergence(branch, base) {
222
- const { exitCode, stdout } = await run([
223
- "rev-list",
224
- "--left-right",
225
- "--count",
226
- `${base}...${branch}`
227
- ]);
228
- if (exitCode !== 0)
229
- return { ahead: 0, behind: 0 };
230
- const parts = stdout.trim().split(/\s+/);
231
- return {
232
- behind: Number.parseInt(parts[0] ?? "0", 10),
233
- ahead: Number.parseInt(parts[1] ?? "0", 10)
234
- };
235
- }
236
- async function getMergedBranches(base) {
237
- const { exitCode, stdout } = await run(["branch", "--merged", base]);
238
- if (exitCode !== 0)
239
- return [];
240
- return stdout.trim().split(`
241
- `).map((b) => b.replace(/^\*?\s+/, "").trim()).filter(Boolean);
242
- }
243
- async function getGoneBranches() {
244
- const { exitCode, stdout } = await run(["branch", "-vv"]);
245
- if (exitCode !== 0)
246
- return [];
247
- return stdout.trimEnd().split(`
248
- `).filter((line) => line.includes(": gone]")).map((line) => line.replace(/^\*?\s+/, "").split(/\s+/)[0]).filter(Boolean);
249
- }
250
- async function deleteBranch(branch) {
251
- return run(["branch", "-d", branch]);
252
- }
253
- async function forceDeleteBranch(branch) {
254
- return run(["branch", "-D", branch]);
255
- }
256
- async function renameBranch(oldName, newName) {
257
- return run(["branch", "-m", oldName, newName]);
258
- }
259
- async function hasLocalWork(remote, branch) {
260
- const uncommitted = await hasUncommittedChanges();
261
- const trackingRef = `${remote}/${branch}`;
262
- const { exitCode, stdout } = await run(["rev-list", "--count", `${trackingRef}..${branch}`]);
263
- const unpushedCommits = exitCode === 0 ? Number.parseInt(stdout.trim(), 10) || 0 : 0;
264
- return { uncommitted, unpushedCommits };
265
- }
266
- async function deleteRemoteBranch(remote, branch) {
267
- return run(["push", remote, "--delete", branch]);
268
- }
269
- async function mergeSquash(branch) {
270
- return run(["merge", "--squash", branch]);
271
- }
272
- async function pushBranch(remote, branch) {
273
- return run(["push", remote, branch]);
274
- }
275
- async function pruneRemote(remote) {
276
- return run(["remote", "prune", remote]);
277
- }
278
- async function commitWithMessage(message) {
279
- return run(["commit", "-m", message]);
280
- }
281
- async function getLogDiff(base, head) {
282
- const { stdout } = await run(["diff", `${base}...${head}`]);
283
- return stdout;
284
- }
285
- async function getLog(base, head) {
286
- const { exitCode, stdout } = await run(["log", `${base}..${head}`, "--oneline"]);
287
- if (exitCode !== 0)
288
- return [];
289
- return stdout.trim().split(`
290
- `).filter(Boolean);
291
- }
292
- async function pullBranch(remote, branch) {
293
- return run(["pull", remote, branch]);
294
- }
295
- async function stageFiles(files) {
296
- return run(["add", "--", ...files]);
297
- }
298
- async function unstageFiles(files) {
299
- return run(["reset", "HEAD", "--", ...files]);
300
- }
301
- async function stageAll() {
302
- return run(["add", "-A"]);
303
- }
304
- async function getFullDiffForFiles(files) {
305
- const [unstaged, staged, untracked] = await Promise.all([
306
- run(["diff", "--", ...files]),
307
- run(["diff", "--cached", "--", ...files]),
308
- getUntrackedFiles()
309
- ]);
310
- const parts = [staged.stdout, unstaged.stdout].filter(Boolean);
311
- const untrackedSet = new Set(untracked);
312
- const MAX_FILE_CONTENT = 2000;
313
- for (const file of files) {
314
- if (untrackedSet.has(file)) {
315
- try {
316
- const content = readFileSync2(join2(process.cwd(), file), "utf-8");
317
- const truncated = content.length > MAX_FILE_CONTENT ? `${content.slice(0, MAX_FILE_CONTENT)}
318
- ... (truncated)` : content;
319
- const lines = truncated.split(`
320
- `).map((l) => `+${l}`);
321
- parts.push(`diff --git a/${file} b/${file}
322
- new file
323
- --- /dev/null
324
- +++ b/${file}
325
- ${lines.join(`
326
- `)}`);
327
- } catch {}
328
- }
329
- }
330
- return parts.join(`
331
- `);
332
- }
333
- async function getUntrackedFiles() {
334
- const { exitCode, stdout } = await run(["ls-files", "--others", "--exclude-standard"]);
335
- if (exitCode !== 0)
336
- return [];
337
- return stdout.trim().split(`
338
- `).filter(Boolean);
339
- }
340
- async function getFileStatus() {
341
- const { exitCode, stdout } = await run(["status", "--porcelain"]);
342
- if (exitCode !== 0)
343
- return { staged: [], modified: [], untracked: [] };
344
- const result = { staged: [], modified: [], untracked: [] };
345
- const STATUS_LABELS = {
346
- A: "new file",
347
- M: "modified",
348
- D: "deleted",
349
- R: "renamed",
350
- C: "copied",
351
- T: "type changed"
352
- };
353
- for (const raw of stdout.trimEnd().split(`
354
- `).filter(Boolean)) {
355
- const line = raw.replace(/\r$/, "");
356
- const indexStatus = line[0];
357
- const workTreeStatus = line[1];
358
- const pathPart = line.slice(3);
359
- const renameIdx = pathPart.indexOf(" -> ");
360
- const file = renameIdx !== -1 ? pathPart.slice(renameIdx + 4) : pathPart;
361
- if (indexStatus === "?" && workTreeStatus === "?") {
362
- result.untracked.push(file);
363
- continue;
364
- }
365
- if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
366
- result.staged.push({ file, status: STATUS_LABELS[indexStatus] ?? indexStatus });
367
- }
368
- if (workTreeStatus && workTreeStatus !== " " && workTreeStatus !== "?") {
369
- result.modified.push({ file, status: STATUS_LABELS[workTreeStatus] ?? workTreeStatus });
370
- }
371
- }
372
- return result;
373
- }
374
- async function getLogGraph(options) {
375
- const count = options?.count ?? 20;
376
- const args = [
377
- "log",
378
- "--oneline",
379
- "--graph",
380
- "--decorate",
381
- `--max-count=${count}`,
382
- "--color=never"
383
- ];
384
- if (options?.all) {
385
- args.push("--all");
386
- }
387
- if (options?.branch) {
388
- args.push(options.branch);
389
- }
390
- const { exitCode, stdout } = await run(args);
391
- if (exitCode !== 0)
392
- return [];
393
- return stdout.trimEnd().split(`
394
- `);
395
- }
396
- async function getLogEntries(options) {
397
- const count = options?.count ?? 20;
398
- const args = [
399
- "log",
400
- `--format=%h||%s||%D`,
401
- `--max-count=${count}`
402
- ];
403
- if (options?.all) {
404
- args.push("--all");
405
- }
406
- if (options?.branch) {
407
- args.push(options.branch);
408
- }
409
- const { exitCode, stdout } = await run(args);
410
- if (exitCode !== 0)
411
- return [];
412
- return stdout.trimEnd().split(`
413
- `).filter(Boolean).map((line) => {
414
- const [hash = "", subject = "", refs = ""] = line.split("||");
415
- return { hash: hash.trim(), subject: subject.trim(), refs: refs.trim() };
416
- });
417
- }
418
- async function getLocalBranches() {
419
- const { exitCode, stdout } = await run(["branch", "-vv", "--no-color"]);
420
- if (exitCode !== 0)
421
- return [];
422
- return stdout.trimEnd().split(`
423
- `).filter(Boolean).map((line) => {
424
- const isCurrent = line.startsWith("*");
425
- const trimmed = line.slice(2);
426
- const nameMatch = trimmed.match(/^(\S+)/);
427
- const name = nameMatch?.[1] ?? "";
428
- const upstreamMatch = trimmed.match(/\[([^\]]+)\]/);
429
- let upstream = null;
430
- let gone = false;
431
- if (upstreamMatch) {
432
- const bracketContent = upstreamMatch[1];
433
- gone = bracketContent.includes(": gone");
434
- upstream = bracketContent.split(":")[0].trim();
435
- }
436
- return { name, isCurrent, upstream, gone };
437
- }).filter((b) => b.name.length > 0);
438
- }
439
- async function getRemoteBranches() {
440
- const { exitCode, stdout } = await run(["branch", "-r", "--no-color"]);
441
- if (exitCode !== 0)
442
- return [];
443
- return stdout.trimEnd().split(`
444
- `).map((line) => line.trim()).filter((line) => line.length > 0 && !line.includes(" -> "));
445
- }
446
-
447
- // src/utils/logger.ts
448
- import { LogEngine, LogMode } from "@wgtechlabs/log-engine";
449
- import pc from "picocolors";
450
- LogEngine.configure({
451
- mode: LogMode.INFO,
452
- format: {
453
- includeIsoTimestamp: false,
454
- includeLocalTime: true,
455
- includeEmoji: true
456
- }
457
- });
458
- function success(msg) {
459
- LogEngine.log(msg);
460
- }
461
- function error(msg) {
462
- LogEngine.error(msg);
463
- }
464
- function warn(msg) {
465
- LogEngine.warn(msg);
466
- }
467
- function info(msg) {
468
- LogEngine.info(msg);
469
- }
470
- function heading(msg) {
471
- console.log(`
472
- ${pc.bold(msg)}`);
473
- }
474
-
475
- // src/utils/workflow.ts
476
- var WORKFLOW_DESCRIPTIONS = {
477
- "clean-flow": "Clean Flow — main + dev, squash features into dev, merge dev into main",
478
- "github-flow": "GitHub Flow — main + feature branches, squash/merge into main",
479
- "git-flow": "Git Flow — main + develop + release + hotfix branches"
480
- };
481
- function getBaseBranch(config) {
482
- switch (config.workflow) {
483
- case "clean-flow":
484
- case "git-flow":
485
- return config.devBranch ?? "dev";
486
- case "github-flow":
487
- return config.mainBranch;
488
- }
489
- }
490
- function hasDevBranch(workflow) {
491
- return workflow === "clean-flow" || workflow === "git-flow";
492
- }
493
- function getSyncSource(config) {
494
- const { workflow, role, mainBranch, origin, upstream } = config;
495
- const devBranch = config.devBranch ?? "dev";
496
- switch (workflow) {
497
- case "clean-flow":
498
- if (role === "contributor") {
499
- return { remote: upstream, ref: `${upstream}/${devBranch}`, strategy: "pull" };
500
- }
501
- return { remote: origin, ref: `${origin}/${devBranch}`, strategy: "pull" };
502
- case "github-flow":
503
- if (role === "contributor") {
504
- return { remote: upstream, ref: `${upstream}/${mainBranch}`, strategy: "pull" };
505
- }
506
- return { remote: origin, ref: `${origin}/${mainBranch}`, strategy: "pull" };
507
- case "git-flow":
508
- if (role === "contributor") {
509
- return { remote: upstream, ref: `${upstream}/${devBranch}`, strategy: "pull" };
510
- }
511
- return { remote: origin, ref: `${origin}/${devBranch}`, strategy: "pull" };
512
- }
513
- }
514
- function getProtectedBranches(config) {
515
- const branches = [config.mainBranch];
516
- if (hasDevBranch(config.workflow) && config.devBranch) {
517
- branches.push(config.devBranch);
518
- }
519
- return branches;
520
- }
521
-
522
- // src/commands/branch.ts
523
- var branch_default = defineCommand({
524
- meta: {
525
- name: "branch",
526
- description: "List branches with workflow-aware labels and status"
527
- },
528
- args: {
529
- all: {
530
- type: "boolean",
531
- alias: "a",
532
- description: "Show both local and remote branches",
533
- default: false
534
- },
535
- remote: {
536
- type: "boolean",
537
- alias: "r",
538
- description: "Show only remote branches",
539
- default: false
540
- }
541
- },
542
- async run({ args }) {
543
- if (!await isGitRepo()) {
544
- error("Not inside a git repository.");
545
- process.exit(1);
546
- }
547
- const config = readConfig();
548
- const protectedBranches = config ? getProtectedBranches(config) : ["main", "master"];
549
- const currentBranch = await getCurrentBranch();
550
- const showRemoteOnly = args.remote;
551
- const showAll = args.all;
552
- heading("\uD83C\uDF3F branches");
553
- console.log();
554
- if (!showRemoteOnly) {
555
- const localBranches = await getLocalBranches();
556
- if (localBranches.length === 0) {
557
- console.log(pc2.dim(" No local branches found."));
558
- } else {
559
- console.log(` ${pc2.bold("Local")}`);
560
- console.log();
561
- for (const branch of localBranches) {
562
- const parts = [];
563
- if (branch.isCurrent) {
564
- parts.push(pc2.green("* "));
565
- } else {
566
- parts.push(" ");
567
- }
568
- const nameStr = colorBranchName(branch.name, protectedBranches, currentBranch);
569
- parts.push(nameStr.padEnd(30));
570
- if (branch.gone) {
571
- parts.push(pc2.red(" ✗ remote gone"));
572
- } else if (branch.upstream) {
573
- parts.push(pc2.dim(` → ${branch.upstream}`));
574
- } else {
575
- parts.push(pc2.dim(" (no remote)"));
576
- }
577
- const labels = getBranchLabels(branch.name, protectedBranches, config);
578
- if (labels.length > 0) {
579
- parts.push(` ${labels.join(" ")}`);
580
- }
581
- console.log(` ${parts.join("")}`);
582
- }
583
- }
584
- }
585
- if (showRemoteOnly || showAll) {
586
- const remoteBranches = await getRemoteBranches();
587
- if (!showRemoteOnly) {
588
- console.log();
589
- }
590
- if (remoteBranches.length === 0) {
591
- console.log(pc2.dim(" No remote branches found."));
592
- } else {
593
- const grouped = groupByRemote(remoteBranches);
594
- for (const [remote, branches] of Object.entries(grouped)) {
595
- console.log(` ${pc2.bold(`Remote: ${remote}`)}`);
596
- console.log();
597
- for (const fullRef of branches) {
598
- const branchName = fullRef.slice(remote.length + 1);
599
- const nameStr = colorBranchName(branchName, protectedBranches, currentBranch);
600
- const remotePrefix = pc2.dim(`${remote}/`);
601
- console.log(` ${remotePrefix}${nameStr}`);
602
- }
603
- console.log();
604
- }
605
- }
606
- }
607
- const tips = [];
608
- if (!showAll && !showRemoteOnly) {
609
- tips.push(`Use ${pc2.bold("contrib branch -a")} to include remote branches`);
610
- }
611
- if (!showRemoteOnly) {
612
- tips.push(`Use ${pc2.bold("contrib start")} to create a new feature branch`);
613
- tips.push(`Use ${pc2.bold("contrib clean")} to remove merged/stale branches`);
614
- }
615
- if (tips.length > 0) {
616
- console.log(` ${pc2.dim("\uD83D\uDCA1 Tip:")}`);
617
- for (const tip of tips) {
618
- console.log(` ${pc2.dim(tip)}`);
619
- }
620
- }
621
- console.log();
622
- }
623
- });
624
- function colorBranchName(name, protectedBranches, currentBranch) {
625
- if (name === currentBranch) {
626
- return pc2.bold(pc2.green(name));
627
- }
628
- if (protectedBranches.includes(name)) {
629
- return pc2.bold(pc2.red(name));
630
- }
631
- return name;
632
- }
633
- function getBranchLabels(name, protectedBranches, config) {
634
- const labels = [];
635
- if (protectedBranches.includes(name)) {
636
- labels.push(pc2.dim(pc2.red("[protected]")));
637
- }
638
- if (config) {
639
- if (name === config.mainBranch) {
640
- labels.push(pc2.dim(pc2.cyan("[main]")));
641
- }
642
- if (config.devBranch && name === config.devBranch) {
643
- labels.push(pc2.dim(pc2.cyan("[dev]")));
644
- }
645
- }
646
- return labels;
647
- }
648
- function groupByRemote(branches) {
649
- const grouped = {};
650
- for (const ref of branches) {
651
- const slashIdx = ref.indexOf("/");
652
- const remote = slashIdx !== -1 ? ref.slice(0, slashIdx) : "unknown";
653
- if (!grouped[remote]) {
654
- grouped[remote] = [];
655
- }
656
- grouped[remote].push(ref);
657
- }
658
- return grouped;
659
- }
660
-
661
- // src/commands/clean.ts
662
- import { defineCommand as defineCommand2 } from "citty";
663
- import pc5 from "picocolors";
664
-
665
- // src/utils/branch.ts
666
- var DEFAULT_PREFIXES = ["feature", "fix", "docs", "chore", "test", "refactor"];
667
- function hasPrefix(branchName, prefixes = DEFAULT_PREFIXES) {
668
- return prefixes.some((p) => branchName.startsWith(`${p}/`));
669
- }
670
- function formatBranchName(prefix, name) {
671
- const sanitized = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
672
- return `${prefix}/${sanitized}`;
673
- }
674
- function isValidBranchName(name) {
675
- return /^[a-zA-Z0-9._/-]+$/.test(name) && !name.startsWith("/") && !name.endsWith("/");
676
- }
677
- function looksLikeNaturalLanguage(input) {
678
- return input.includes(" ") && !input.includes("/");
679
- }
680
-
681
83
  // src/utils/confirm.ts
682
84
  import * as clack from "@clack/prompts";
683
- import pc3 from "picocolors";
85
+ import pc from "picocolors";
684
86
  function handleCancel(value) {
685
87
  if (clack.isCancel(value)) {
686
88
  clack.cancel("Cancelled.");
@@ -711,7 +113,7 @@ async function inputPrompt(message, defaultValue) {
711
113
  }
712
114
  async function multiSelectPrompt(message, choices) {
713
115
  const result = await clack.multiselect({
714
- message: `${message} ${pc3.dim("(space to toggle, enter to confirm)")}`,
116
+ message: `${message} ${pc.dim("(space to toggle, enter to confirm)")}`,
715
117
  options: choices.map((choice) => ({ value: choice, label: choice })),
716
118
  required: false
717
119
  });
@@ -1030,12 +432,12 @@ ${diffs.slice(0, 4000)}`;
1030
432
  }
1031
433
 
1032
434
  // src/utils/gh.ts
1033
- import { execFile as execFileCb2 } from "node:child_process";
1034
- function run2(args) {
435
+ import { execFile as execFileCb } from "node:child_process";
436
+ function run(args) {
1035
437
  return new Promise((resolve) => {
1036
- execFileCb2("gh", args, (error2, stdout, stderr) => {
438
+ execFileCb("gh", args, (error, stdout, stderr) => {
1037
439
  resolve({
1038
- exitCode: error2 ? error2.code === "ENOENT" ? 127 : error2.status ?? 1 : 0,
440
+ exitCode: error ? error.code === "ENOENT" ? 127 : error.status ?? 1 : 0,
1039
441
  stdout: stdout ?? "",
1040
442
  stderr: stderr ?? ""
1041
443
  });
@@ -1044,7 +446,7 @@ function run2(args) {
1044
446
  }
1045
447
  async function checkGhInstalled() {
1046
448
  try {
1047
- const { exitCode } = await run2(["--version"]);
449
+ const { exitCode } = await run(["--version"]);
1048
450
  return exitCode === 0;
1049
451
  } catch {
1050
452
  return false;
@@ -1052,7 +454,7 @@ async function checkGhInstalled() {
1052
454
  }
1053
455
  async function checkGhAuth() {
1054
456
  try {
1055
- const { exitCode } = await run2(["auth", "status"]);
457
+ const { exitCode } = await run(["auth", "status"]);
1056
458
  return exitCode === 0;
1057
459
  } catch {
1058
460
  return false;
@@ -1062,7 +464,7 @@ var SAFE_SLUG = /^[\w.-]+$/;
1062
464
  async function checkRepoPermissions(owner, repo) {
1063
465
  if (!SAFE_SLUG.test(owner) || !SAFE_SLUG.test(repo))
1064
466
  return null;
1065
- const { exitCode, stdout } = await run2(["api", `repos/${owner}/${repo}`, "--jq", ".permissions"]);
467
+ const { exitCode, stdout } = await run(["api", `repos/${owner}/${repo}`, "--jq", ".permissions"]);
1066
468
  if (exitCode !== 0)
1067
469
  return null;
1068
470
  try {
@@ -1072,7 +474,7 @@ async function checkRepoPermissions(owner, repo) {
1072
474
  }
1073
475
  }
1074
476
  async function isRepoFork() {
1075
- const { exitCode, stdout } = await run2(["repo", "view", "--json", "isFork", "-q", ".isFork"]);
477
+ const { exitCode, stdout } = await run(["repo", "view", "--json", "isFork", "-q", ".isFork"]);
1076
478
  if (exitCode !== 0)
1077
479
  return null;
1078
480
  const val = stdout.trim();
@@ -1083,7 +485,7 @@ async function isRepoFork() {
1083
485
  return null;
1084
486
  }
1085
487
  async function getCurrentRepoInfo() {
1086
- const { exitCode, stdout } = await run2([
488
+ const { exitCode, stdout } = await run([
1087
489
  "repo",
1088
490
  "view",
1089
491
  "--json",
@@ -1114,16 +516,16 @@ async function createPR(options) {
1114
516
  ];
1115
517
  if (options.draft)
1116
518
  args.push("--draft");
1117
- return run2(args);
519
+ return run(args);
1118
520
  }
1119
521
  async function createPRFill(base, draft) {
1120
522
  const args = ["pr", "create", "--base", base, "--fill"];
1121
523
  if (draft)
1122
524
  args.push("--draft");
1123
- return run2(args);
525
+ return run(args);
1124
526
  }
1125
527
  async function getPRForBranch(headBranch) {
1126
- const { exitCode, stdout } = await run2([
528
+ const { exitCode, stdout } = await run([
1127
529
  "pr",
1128
530
  "list",
1129
531
  "--head",
@@ -1144,31 +546,367 @@ async function getPRForBranch(headBranch) {
1144
546
  return null;
1145
547
  }
1146
548
  }
1147
- async function getMergedPRForBranch(headBranch) {
1148
- const { exitCode, stdout } = await run2([
1149
- "pr",
1150
- "list",
1151
- "--head",
1152
- headBranch,
1153
- "--state",
1154
- "merged",
1155
- "--json",
1156
- "number,url,title,state",
1157
- "--limit",
1158
- "1"
1159
- ]);
549
+ async function getMergedPRForBranch(headBranch) {
550
+ const { exitCode, stdout } = await run([
551
+ "pr",
552
+ "list",
553
+ "--head",
554
+ headBranch,
555
+ "--state",
556
+ "merged",
557
+ "--json",
558
+ "number,url,title,state",
559
+ "--limit",
560
+ "1"
561
+ ]);
562
+ if (exitCode !== 0)
563
+ return null;
564
+ try {
565
+ const prs = JSON.parse(stdout.trim());
566
+ return prs.length > 0 ? prs[0] : null;
567
+ } catch {
568
+ return null;
569
+ }
570
+ }
571
+
572
+ // src/utils/git.ts
573
+ import { execFile as execFileCb2 } from "node:child_process";
574
+ import { readFileSync as readFileSync2 } from "node:fs";
575
+ import { join as join2 } from "node:path";
576
+ function run2(args) {
577
+ return new Promise((resolve) => {
578
+ execFileCb2("git", args, (error, stdout, stderr) => {
579
+ resolve({
580
+ exitCode: error ? error.code === "ENOENT" ? 127 : error.status ?? 1 : 0,
581
+ stdout: stdout ?? "",
582
+ stderr: stderr ?? ""
583
+ });
584
+ });
585
+ });
586
+ }
587
+ async function isGitRepo() {
588
+ const { exitCode } = await run2(["rev-parse", "--is-inside-work-tree"]);
589
+ return exitCode === 0;
590
+ }
591
+ async function getCurrentBranch() {
592
+ const { exitCode, stdout } = await run2(["rev-parse", "--abbrev-ref", "HEAD"]);
593
+ if (exitCode !== 0)
594
+ return null;
595
+ return stdout.trim() || null;
596
+ }
597
+ async function getRemotes() {
598
+ const { exitCode, stdout } = await run2(["remote"]);
599
+ if (exitCode !== 0)
600
+ return [];
601
+ return stdout.trim().split(`
602
+ `).map((r) => r.trim()).filter(Boolean);
603
+ }
604
+ async function getRemoteUrl(remote) {
605
+ const { exitCode, stdout } = await run2(["remote", "get-url", remote]);
606
+ if (exitCode !== 0)
607
+ return null;
608
+ return stdout.trim() || null;
609
+ }
610
+ async function hasUncommittedChanges() {
611
+ const { exitCode, stdout } = await run2(["status", "--porcelain"]);
612
+ if (exitCode !== 0)
613
+ return false;
614
+ return stdout.trim().length > 0;
615
+ }
616
+ async function fetchRemote(remote) {
617
+ return run2(["fetch", remote]);
618
+ }
619
+ async function fetchAll() {
620
+ return run2(["fetch", "--all", "--quiet"]);
621
+ }
622
+ async function checkoutBranch2(branch) {
623
+ return run2(["checkout", branch]);
624
+ }
625
+ async function createBranch(branch, from) {
626
+ const args = from ? ["checkout", "-b", branch, from] : ["checkout", "-b", branch];
627
+ return run2(args);
628
+ }
629
+ async function resetHard(ref) {
630
+ return run2(["reset", "--hard", ref]);
631
+ }
632
+ async function updateLocalBranch(branch, target) {
633
+ const current = await getCurrentBranch();
634
+ if (current === branch) {
635
+ return resetHard(target);
636
+ }
637
+ return run2(["branch", "-f", branch, target]);
638
+ }
639
+ async function pushSetUpstream(remote, branch) {
640
+ return run2(["push", "-u", remote, branch]);
641
+ }
642
+ async function rebase(branch) {
643
+ return run2(["rebase", branch]);
644
+ }
645
+ async function getUpstreamRef() {
646
+ const { exitCode, stdout } = await run2([
647
+ "rev-parse",
648
+ "--abbrev-ref",
649
+ "--symbolic-full-name",
650
+ "@{u}"
651
+ ]);
652
+ if (exitCode !== 0)
653
+ return null;
654
+ return stdout.trim() || null;
655
+ }
656
+ async function unsetUpstream() {
657
+ return run2(["branch", "--unset-upstream"]);
658
+ }
659
+ async function rebaseOnto(newBase, oldBase) {
660
+ return run2(["rebase", "--onto", newBase, oldBase]);
661
+ }
662
+ async function getMergeBase(ref1, ref2) {
663
+ const { exitCode, stdout } = await run2(["merge-base", ref1, ref2]);
664
+ if (exitCode !== 0)
665
+ return null;
666
+ return stdout.trim() || null;
667
+ }
668
+ async function getCommitHash(ref) {
669
+ const { exitCode, stdout } = await run2(["rev-parse", ref]);
670
+ if (exitCode !== 0)
671
+ return null;
672
+ return stdout.trim() || null;
673
+ }
674
+ async function determineRebaseStrategy(currentBranch, syncRef) {
675
+ const upstreamRef = await getUpstreamRef();
676
+ if (!upstreamRef) {
677
+ return { strategy: "plain" };
678
+ }
679
+ const upstreamHash = await getCommitHash(upstreamRef);
680
+ if (!upstreamHash) {
681
+ return { strategy: "plain" };
682
+ }
683
+ const slashIdx = upstreamRef.indexOf("/");
684
+ const upstreamBranchName = slashIdx !== -1 ? upstreamRef.slice(slashIdx + 1) : upstreamRef;
685
+ if (upstreamBranchName === currentBranch) {
686
+ return { strategy: "plain" };
687
+ }
688
+ const [forkFromUpstream, forkFromSync] = await Promise.all([
689
+ getMergeBase("HEAD", upstreamRef),
690
+ getMergeBase("HEAD", syncRef)
691
+ ]);
692
+ if (forkFromUpstream && forkFromSync && forkFromUpstream === forkFromSync) {
693
+ return { strategy: "plain" };
694
+ }
695
+ if (forkFromUpstream) {
696
+ return { strategy: "onto", ontoOldBase: forkFromUpstream };
697
+ }
698
+ return { strategy: "plain" };
699
+ }
700
+ async function getStagedDiff() {
701
+ const { stdout } = await run2(["diff", "--cached"]);
702
+ return stdout;
703
+ }
704
+ async function getStagedFiles() {
705
+ const { exitCode, stdout } = await run2(["diff", "--cached", "--name-only"]);
706
+ if (exitCode !== 0)
707
+ return [];
708
+ return stdout.trim().split(`
709
+ `).filter(Boolean);
710
+ }
711
+ async function getChangedFiles() {
712
+ const { exitCode, stdout } = await run2(["status", "--porcelain"]);
713
+ if (exitCode !== 0)
714
+ return [];
715
+ return stdout.trimEnd().split(`
716
+ `).filter(Boolean).map((l) => {
717
+ const line = l.replace(/\r$/, "");
718
+ const match = line.match(/^..\s+(.*)/);
719
+ if (!match)
720
+ return "";
721
+ const file = match[1];
722
+ const renameIdx = file.indexOf(" -> ");
723
+ return renameIdx !== -1 ? file.slice(renameIdx + 4) : file;
724
+ }).filter(Boolean);
725
+ }
726
+ async function getDivergence(branch, base) {
727
+ const { exitCode, stdout } = await run2([
728
+ "rev-list",
729
+ "--left-right",
730
+ "--count",
731
+ `${base}...${branch}`
732
+ ]);
733
+ if (exitCode !== 0)
734
+ return { ahead: 0, behind: 0 };
735
+ const parts = stdout.trim().split(/\s+/);
736
+ return {
737
+ behind: Number.parseInt(parts[0] ?? "0", 10),
738
+ ahead: Number.parseInt(parts[1] ?? "0", 10)
739
+ };
740
+ }
741
+ async function getMergedBranches(base) {
742
+ const { exitCode, stdout } = await run2(["branch", "--merged", base]);
743
+ if (exitCode !== 0)
744
+ return [];
745
+ return stdout.trim().split(`
746
+ `).map((b) => b.replace(/^\*?\s+/, "").trim()).filter(Boolean);
747
+ }
748
+ async function getGoneBranches() {
749
+ const { exitCode, stdout } = await run2(["branch", "-vv"]);
750
+ if (exitCode !== 0)
751
+ return [];
752
+ return stdout.trimEnd().split(`
753
+ `).filter((line) => line.includes(": gone]")).map((line) => line.replace(/^\*?\s+/, "").split(/\s+/)[0]).filter(Boolean);
754
+ }
755
+ async function deleteBranch(branch) {
756
+ return run2(["branch", "-d", branch]);
757
+ }
758
+ async function forceDeleteBranch(branch) {
759
+ return run2(["branch", "-D", branch]);
760
+ }
761
+ async function renameBranch(oldName, newName) {
762
+ return run2(["branch", "-m", oldName, newName]);
763
+ }
764
+ async function hasLocalWork(remote, branch) {
765
+ const uncommitted = await hasUncommittedChanges();
766
+ const trackingRef = `${remote}/${branch}`;
767
+ const { exitCode, stdout } = await run2(["rev-list", "--count", `${trackingRef}..${branch}`]);
768
+ const unpushedCommits = exitCode === 0 ? Number.parseInt(stdout.trim(), 10) || 0 : 0;
769
+ return { uncommitted, unpushedCommits };
770
+ }
771
+ async function deleteRemoteBranch(remote, branch) {
772
+ return run2(["push", remote, "--delete", branch]);
773
+ }
774
+ async function mergeSquash(branch) {
775
+ return run2(["merge", "--squash", branch]);
776
+ }
777
+ async function pushBranch(remote, branch) {
778
+ return run2(["push", remote, branch]);
779
+ }
780
+ async function pruneRemote(remote) {
781
+ return run2(["remote", "prune", remote]);
782
+ }
783
+ async function commitWithMessage(message) {
784
+ return run2(["commit", "-m", message]);
785
+ }
786
+ async function getLogDiff(base, head) {
787
+ const { stdout } = await run2(["diff", `${base}...${head}`]);
788
+ return stdout;
789
+ }
790
+ async function getLog(base, head) {
791
+ const { exitCode, stdout } = await run2(["log", `${base}..${head}`, "--oneline"]);
792
+ if (exitCode !== 0)
793
+ return [];
794
+ return stdout.trim().split(`
795
+ `).filter(Boolean);
796
+ }
797
+ async function pullBranch(remote, branch) {
798
+ return run2(["pull", remote, branch]);
799
+ }
800
+ async function stageFiles(files) {
801
+ return run2(["add", "--", ...files]);
802
+ }
803
+ async function unstageFiles(files) {
804
+ return run2(["reset", "HEAD", "--", ...files]);
805
+ }
806
+ async function stageAll() {
807
+ return run2(["add", "-A"]);
808
+ }
809
+ async function getFullDiffForFiles(files) {
810
+ const [unstaged, staged, untracked] = await Promise.all([
811
+ run2(["diff", "--", ...files]),
812
+ run2(["diff", "--cached", "--", ...files]),
813
+ getUntrackedFiles()
814
+ ]);
815
+ const parts = [staged.stdout, unstaged.stdout].filter(Boolean);
816
+ const untrackedSet = new Set(untracked);
817
+ const MAX_FILE_CONTENT = 2000;
818
+ for (const file of files) {
819
+ if (untrackedSet.has(file)) {
820
+ try {
821
+ const content = readFileSync2(join2(process.cwd(), file), "utf-8");
822
+ const truncated = content.length > MAX_FILE_CONTENT ? `${content.slice(0, MAX_FILE_CONTENT)}
823
+ ... (truncated)` : content;
824
+ const lines = truncated.split(`
825
+ `).map((l) => `+${l}`);
826
+ parts.push(`diff --git a/${file} b/${file}
827
+ new file
828
+ --- /dev/null
829
+ +++ b/${file}
830
+ ${lines.join(`
831
+ `)}`);
832
+ } catch {}
833
+ }
834
+ }
835
+ return parts.join(`
836
+ `);
837
+ }
838
+ async function getUntrackedFiles() {
839
+ const { exitCode, stdout } = await run2(["ls-files", "--others", "--exclude-standard"]);
1160
840
  if (exitCode !== 0)
1161
- return null;
1162
- try {
1163
- const prs = JSON.parse(stdout.trim());
1164
- return prs.length > 0 ? prs[0] : null;
1165
- } catch {
1166
- return null;
841
+ return [];
842
+ return stdout.trim().split(`
843
+ `).filter(Boolean);
844
+ }
845
+ async function getFileStatus() {
846
+ const { exitCode, stdout } = await run2(["status", "--porcelain"]);
847
+ if (exitCode !== 0)
848
+ return { staged: [], modified: [], untracked: [] };
849
+ const result = { staged: [], modified: [], untracked: [] };
850
+ const STATUS_LABELS = {
851
+ A: "new file",
852
+ M: "modified",
853
+ D: "deleted",
854
+ R: "renamed",
855
+ C: "copied",
856
+ T: "type changed"
857
+ };
858
+ for (const raw of stdout.trimEnd().split(`
859
+ `).filter(Boolean)) {
860
+ const line = raw.replace(/\r$/, "");
861
+ const indexStatus = line[0];
862
+ const workTreeStatus = line[1];
863
+ const pathPart = line.slice(3);
864
+ const renameIdx = pathPart.indexOf(" -> ");
865
+ const file = renameIdx !== -1 ? pathPart.slice(renameIdx + 4) : pathPart;
866
+ if (indexStatus === "?" && workTreeStatus === "?") {
867
+ result.untracked.push(file);
868
+ continue;
869
+ }
870
+ if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
871
+ result.staged.push({ file, status: STATUS_LABELS[indexStatus] ?? indexStatus });
872
+ }
873
+ if (workTreeStatus && workTreeStatus !== " " && workTreeStatus !== "?") {
874
+ result.modified.push({ file, status: STATUS_LABELS[workTreeStatus] ?? workTreeStatus });
875
+ }
876
+ }
877
+ return result;
878
+ }
879
+
880
+ // src/utils/logger.ts
881
+ import { LogEngine, LogMode } from "@wgtechlabs/log-engine";
882
+ import pc2 from "picocolors";
883
+ LogEngine.configure({
884
+ mode: LogMode.INFO,
885
+ format: {
886
+ includeIsoTimestamp: false,
887
+ includeLocalTime: true,
888
+ includeEmoji: true
1167
889
  }
890
+ });
891
+ function success(msg) {
892
+ LogEngine.log(msg);
893
+ }
894
+ function error(msg) {
895
+ LogEngine.error(msg);
896
+ }
897
+ function warn(msg) {
898
+ LogEngine.warn(msg);
899
+ }
900
+ function info(msg) {
901
+ LogEngine.info(msg);
902
+ }
903
+ function heading(msg) {
904
+ console.log(`
905
+ ${pc2.bold(msg)}`);
1168
906
  }
1169
907
 
1170
908
  // src/utils/spinner.ts
1171
- import pc4 from "picocolors";
909
+ import pc3 from "picocolors";
1172
910
  var FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
1173
911
  function createSpinner(text2) {
1174
912
  let frameIdx = 0;
@@ -1180,7 +918,7 @@ function createSpinner(text2) {
1180
918
  const render = () => {
1181
919
  if (stopped)
1182
920
  return;
1183
- const frame = pc4.cyan(FRAMES[frameIdx % FRAMES.length]);
921
+ const frame = pc3.cyan(FRAMES[frameIdx % FRAMES.length]);
1184
922
  clearLine();
1185
923
  process.stderr.write(`${frame} ${currentText}`);
1186
924
  frameIdx++;
@@ -1200,12 +938,12 @@ function createSpinner(text2) {
1200
938
  },
1201
939
  success(msg) {
1202
940
  stop();
1203
- process.stderr.write(`${pc4.green("✔")} ${msg}
941
+ process.stderr.write(`${pc3.green("✔")} ${msg}
1204
942
  `);
1205
943
  },
1206
944
  fail(msg) {
1207
945
  stop();
1208
- process.stderr.write(`${pc4.red("✖")} ${msg}
946
+ process.stderr.write(`${pc3.red("✖")} ${msg}
1209
947
  `);
1210
948
  },
1211
949
  stop() {
@@ -1214,6 +952,53 @@ function createSpinner(text2) {
1214
952
  };
1215
953
  }
1216
954
 
955
+ // src/utils/workflow.ts
956
+ var WORKFLOW_DESCRIPTIONS = {
957
+ "clean-flow": "Clean Flow — main + dev, squash features into dev, merge dev into main",
958
+ "github-flow": "GitHub Flow — main + feature branches, squash/merge into main",
959
+ "git-flow": "Git Flow — main + develop + release + hotfix branches"
960
+ };
961
+ function getBaseBranch(config) {
962
+ switch (config.workflow) {
963
+ case "clean-flow":
964
+ case "git-flow":
965
+ return config.devBranch ?? "dev";
966
+ case "github-flow":
967
+ return config.mainBranch;
968
+ }
969
+ }
970
+ function hasDevBranch(workflow) {
971
+ return workflow === "clean-flow" || workflow === "git-flow";
972
+ }
973
+ function getSyncSource(config) {
974
+ const { workflow, role, mainBranch, origin, upstream } = config;
975
+ const devBranch = config.devBranch ?? "dev";
976
+ switch (workflow) {
977
+ case "clean-flow":
978
+ if (role === "contributor") {
979
+ return { remote: upstream, ref: `${upstream}/${devBranch}`, strategy: "pull" };
980
+ }
981
+ return { remote: origin, ref: `${origin}/${devBranch}`, strategy: "pull" };
982
+ case "github-flow":
983
+ if (role === "contributor") {
984
+ return { remote: upstream, ref: `${upstream}/${mainBranch}`, strategy: "pull" };
985
+ }
986
+ return { remote: origin, ref: `${origin}/${mainBranch}`, strategy: "pull" };
987
+ case "git-flow":
988
+ if (role === "contributor") {
989
+ return { remote: upstream, ref: `${upstream}/${devBranch}`, strategy: "pull" };
990
+ }
991
+ return { remote: origin, ref: `${origin}/${devBranch}`, strategy: "pull" };
992
+ }
993
+ }
994
+ function getProtectedBranches(config) {
995
+ const branches = [config.mainBranch];
996
+ if (hasDevBranch(config.workflow) && config.devBranch) {
997
+ branches.push(config.devBranch);
998
+ }
999
+ return branches;
1000
+ }
1001
+
1217
1002
  // src/commands/clean.ts
1218
1003
  async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
1219
1004
  if (!config)
@@ -1226,18 +1011,18 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
1226
1011
  warn("You have uncommitted changes in your working tree.");
1227
1012
  }
1228
1013
  if (localWork.unpushedCommits > 0) {
1229
- warn(`You have ${pc5.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not pushed.`);
1014
+ warn(`You have ${pc4.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not pushed.`);
1230
1015
  }
1231
1016
  const SAVE_NEW_BRANCH = "Save changes to a new branch";
1232
1017
  const DISCARD = "Discard all changes and clean up";
1233
1018
  const CANCEL = "Skip this branch";
1234
- const action = await selectPrompt(`${pc5.bold(currentBranch)} has local changes. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
1019
+ const action = await selectPrompt(`${pc4.bold(currentBranch)} has local changes. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
1235
1020
  if (action === CANCEL)
1236
1021
  return "skipped";
1237
1022
  if (action === SAVE_NEW_BRANCH) {
1238
1023
  if (!config)
1239
1024
  return "skipped";
1240
- info(pc5.dim("Tip: Describe what you're working on in plain English and we'll generate a branch name."));
1025
+ info(pc4.dim("Tip: Describe what you're working on in plain English and we'll generate a branch name."));
1241
1026
  const description = await inputPrompt("What are you working on?");
1242
1027
  let newBranchName = description;
1243
1028
  if (looksLikeNaturalLanguage(description)) {
@@ -1246,8 +1031,8 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
1246
1031
  if (suggested) {
1247
1032
  spinner.success("Branch name suggestion ready.");
1248
1033
  console.log(`
1249
- ${pc5.dim("AI suggestion:")} ${pc5.bold(pc5.cyan(suggested))}`);
1250
- const accepted = await confirmPrompt(`Use ${pc5.bold(suggested)} as your branch name?`);
1034
+ ${pc4.dim("AI suggestion:")} ${pc4.bold(pc4.cyan(suggested))}`);
1035
+ const accepted = await confirmPrompt(`Use ${pc4.bold(suggested)} as your branch name?`);
1251
1036
  newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
1252
1037
  } else {
1253
1038
  spinner.fail("AI did not return a suggestion.");
@@ -1255,7 +1040,7 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
1255
1040
  }
1256
1041
  }
1257
1042
  if (!hasPrefix(newBranchName, config.branchPrefixes)) {
1258
- const prefix = await selectPrompt(`Choose a branch type for ${pc5.bold(newBranchName)}:`, config.branchPrefixes);
1043
+ const prefix = await selectPrompt(`Choose a branch type for ${pc4.bold(newBranchName)}:`, config.branchPrefixes);
1259
1044
  newBranchName = formatBranchName(prefix, newBranchName);
1260
1045
  }
1261
1046
  if (!isValidBranchName(newBranchName)) {
@@ -1267,16 +1052,16 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
1267
1052
  error(`Failed to rename branch: ${renameResult.stderr}`);
1268
1053
  return "skipped";
1269
1054
  }
1270
- success(`Renamed ${pc5.bold(currentBranch)} → ${pc5.bold(newBranchName)}`);
1055
+ success(`Renamed ${pc4.bold(currentBranch)} → ${pc4.bold(newBranchName)}`);
1271
1056
  const syncSource2 = getSyncSource(config);
1272
1057
  await fetchRemote(syncSource2.remote);
1273
1058
  const savedUpstreamRef = await getUpstreamRef();
1274
1059
  const rebaseResult = savedUpstreamRef && savedUpstreamRef !== syncSource2.ref ? await rebaseOnto(syncSource2.ref, savedUpstreamRef) : await rebase(syncSource2.ref);
1275
1060
  if (rebaseResult.exitCode !== 0) {
1276
1061
  warn("Rebase encountered conflicts. Resolve them after cleanup:");
1277
- info(` ${pc5.bold(`git checkout ${newBranchName} && git rebase --continue`)}`);
1062
+ info(` ${pc4.bold(`git checkout ${newBranchName} && git rebase --continue`)}`);
1278
1063
  } else {
1279
- success(`Rebased ${pc5.bold(newBranchName)} onto ${pc5.bold(syncSource2.ref)}.`);
1064
+ success(`Rebased ${pc4.bold(newBranchName)} onto ${pc4.bold(syncSource2.ref)}.`);
1280
1065
  }
1281
1066
  const coResult2 = await checkoutBranch(baseBranch);
1282
1067
  if (coResult2.exitCode !== 0) {
@@ -1284,12 +1069,12 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
1284
1069
  return "saved";
1285
1070
  }
1286
1071
  await updateLocalBranch(baseBranch, syncSource2.ref);
1287
- success(`Synced ${pc5.bold(baseBranch)} with ${pc5.bold(syncSource2.ref)}.`);
1072
+ success(`Synced ${pc4.bold(baseBranch)} with ${pc4.bold(syncSource2.ref)}.`);
1288
1073
  return "saved";
1289
1074
  }
1290
1075
  }
1291
1076
  const syncSource = getSyncSource(config);
1292
- info(`Switching to ${pc5.bold(baseBranch)} and syncing...`);
1077
+ info(`Switching to ${pc4.bold(baseBranch)} and syncing...`);
1293
1078
  await fetchRemote(syncSource.remote);
1294
1079
  const coResult = await checkoutBranch(baseBranch);
1295
1080
  if (coResult.exitCode !== 0) {
@@ -1297,10 +1082,10 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
1297
1082
  return "skipped";
1298
1083
  }
1299
1084
  await updateLocalBranch(baseBranch, syncSource.ref);
1300
- success(`Synced ${pc5.bold(baseBranch)} with ${pc5.bold(syncSource.ref)}.`);
1085
+ success(`Synced ${pc4.bold(baseBranch)} with ${pc4.bold(syncSource.ref)}.`);
1301
1086
  return "switched";
1302
1087
  }
1303
- var clean_default = defineCommand2({
1088
+ var clean_default = defineCommand({
1304
1089
  meta: {
1305
1090
  name: "clean",
1306
1091
  description: "Delete merged branches and prune remote refs"
@@ -1345,21 +1130,21 @@ var clean_default = defineCommand2({
1345
1130
  if (ghInstalled && ghAuthed) {
1346
1131
  const mergedPR = await getMergedPRForBranch(currentBranch);
1347
1132
  if (mergedPR) {
1348
- warn(`PR #${mergedPR.number} (${pc5.bold(mergedPR.title)}) has already been merged.`);
1349
- info(`Link: ${pc5.underline(mergedPR.url)}`);
1133
+ warn(`PR #${mergedPR.number} (${pc4.bold(mergedPR.title)}) has already been merged.`);
1134
+ info(`Link: ${pc4.underline(mergedPR.url)}`);
1350
1135
  goneCandidates.push(currentBranch);
1351
1136
  }
1352
1137
  }
1353
1138
  }
1354
1139
  if (mergedCandidates.length > 0) {
1355
1140
  console.log(`
1356
- ${pc5.bold("Merged branches to delete:")}`);
1141
+ ${pc4.bold("Merged branches to delete:")}`);
1357
1142
  for (const b of mergedCandidates) {
1358
- const marker = b === currentBranch ? pc5.yellow(" (current)") : "";
1359
- console.log(` ${pc5.dim("•")} ${b}${marker}`);
1143
+ const marker = b === currentBranch ? pc4.yellow(" (current)") : "";
1144
+ console.log(` ${pc4.dim("•")} ${b}${marker}`);
1360
1145
  }
1361
1146
  console.log();
1362
- const ok = args.yes || await confirmPrompt(`Delete ${pc5.bold(String(mergedCandidates.length))} merged branch${mergedCandidates.length !== 1 ? "es" : ""}?`);
1147
+ const ok = args.yes || await confirmPrompt(`Delete ${pc4.bold(String(mergedCandidates.length))} merged branch${mergedCandidates.length !== 1 ? "es" : ""}?`);
1363
1148
  if (ok) {
1364
1149
  for (const branch of mergedCandidates) {
1365
1150
  if (branch === currentBranch) {
@@ -1376,7 +1161,7 @@ ${pc5.bold("Merged branches to delete:")}`);
1376
1161
  }
1377
1162
  const result = await deleteBranch(branch);
1378
1163
  if (result.exitCode === 0) {
1379
- success(` Deleted ${pc5.bold(branch)}`);
1164
+ success(` Deleted ${pc4.bold(branch)}`);
1380
1165
  } else {
1381
1166
  warn(` Failed to delete ${branch}: ${result.stderr.trim()}`);
1382
1167
  }
@@ -1387,13 +1172,13 @@ ${pc5.bold("Merged branches to delete:")}`);
1387
1172
  }
1388
1173
  if (goneCandidates.length > 0) {
1389
1174
  console.log(`
1390
- ${pc5.bold("Stale branches (remote deleted, likely squash-merged):")}`);
1175
+ ${pc4.bold("Stale branches (remote deleted, likely squash-merged):")}`);
1391
1176
  for (const b of goneCandidates) {
1392
- const marker = b === currentBranch ? pc5.yellow(" (current)") : "";
1393
- console.log(` ${pc5.dim("•")} ${b}${marker}`);
1177
+ const marker = b === currentBranch ? pc4.yellow(" (current)") : "";
1178
+ console.log(` ${pc4.dim("•")} ${b}${marker}`);
1394
1179
  }
1395
1180
  console.log();
1396
- const ok = args.yes || await confirmPrompt(`Delete ${pc5.bold(String(goneCandidates.length))} stale branch${goneCandidates.length !== 1 ? "es" : ""}?`);
1181
+ const ok = args.yes || await confirmPrompt(`Delete ${pc4.bold(String(goneCandidates.length))} stale branch${goneCandidates.length !== 1 ? "es" : ""}?`);
1397
1182
  if (ok) {
1398
1183
  for (const branch of goneCandidates) {
1399
1184
  if (branch === currentBranch) {
@@ -1410,7 +1195,7 @@ ${pc5.bold("Stale branches (remote deleted, likely squash-merged):")}`);
1410
1195
  }
1411
1196
  const result = await forceDeleteBranch(branch);
1412
1197
  if (result.exitCode === 0) {
1413
- success(` Deleted ${pc5.bold(branch)}`);
1198
+ success(` Deleted ${pc4.bold(branch)}`);
1414
1199
  } else {
1415
1200
  warn(` Failed to delete ${branch}: ${result.stderr.trim()}`);
1416
1201
  }
@@ -1425,14 +1210,14 @@ ${pc5.bold("Stale branches (remote deleted, likely squash-merged):")}`);
1425
1210
  const finalBranch = await getCurrentBranch();
1426
1211
  if (finalBranch && protectedBranches.has(finalBranch)) {
1427
1212
  console.log();
1428
- info(`You're on ${pc5.bold(finalBranch)}. Run ${pc5.bold("contrib start")} to begin a new feature.`);
1213
+ info(`You're on ${pc4.bold(finalBranch)}. Run ${pc4.bold("contrib start")} to begin a new feature.`);
1429
1214
  }
1430
1215
  }
1431
1216
  });
1432
1217
 
1433
1218
  // src/commands/commit.ts
1434
- import { defineCommand as defineCommand3 } from "citty";
1435
- import pc6 from "picocolors";
1219
+ import { defineCommand as defineCommand2 } from "citty";
1220
+ import pc5 from "picocolors";
1436
1221
 
1437
1222
  // src/utils/convention.ts
1438
1223
  var CLEAN_COMMIT_PATTERN = /^(📦|🔧|🗑\uFE0F?|🔒|⚙\uFE0F?|☕|🧪|📖|🚀) (new|update|remove|security|setup|chore|test|docs|release)(!?)( \([a-zA-Z0-9][a-zA-Z0-9-]*\))?: .{1,72}$/u;
@@ -1478,7 +1263,7 @@ function getValidationError(convention) {
1478
1263
  }
1479
1264
 
1480
1265
  // src/commands/commit.ts
1481
- var commit_default = defineCommand3({
1266
+ var commit_default = defineCommand2({
1482
1267
  meta: {
1483
1268
  name: "commit",
1484
1269
  description: "Stage changes and create a commit message (AI-powered)"
@@ -1522,9 +1307,9 @@ var commit_default = defineCommand3({
1522
1307
  process.exit(1);
1523
1308
  }
1524
1309
  console.log(`
1525
- ${pc6.bold("Changed files:")}`);
1310
+ ${pc5.bold("Changed files:")}`);
1526
1311
  for (const f of changedFiles) {
1527
- console.log(` ${pc6.dim("•")} ${f}`);
1312
+ console.log(` ${pc5.dim("•")} ${f}`);
1528
1313
  }
1529
1314
  const stageAction = await selectPrompt("No staged changes. How would you like to stage?", [
1530
1315
  "Stage all changes",
@@ -1574,7 +1359,7 @@ ${pc6.bold("Changed files:")}`);
1574
1359
  if (commitMessage) {
1575
1360
  spinner.success("AI commit message generated.");
1576
1361
  console.log(`
1577
- ${pc6.dim("AI suggestion:")} ${pc6.bold(pc6.cyan(commitMessage))}`);
1362
+ ${pc5.dim("AI suggestion:")} ${pc5.bold(pc5.cyan(commitMessage))}`);
1578
1363
  } else {
1579
1364
  spinner.fail("AI did not return a commit message.");
1580
1365
  warn("Falling back to manual entry.");
@@ -1600,7 +1385,7 @@ ${pc6.bold("Changed files:")}`);
1600
1385
  if (regen) {
1601
1386
  spinner.success("Commit message regenerated.");
1602
1387
  console.log(`
1603
- ${pc6.dim("AI suggestion:")} ${pc6.bold(pc6.cyan(regen))}`);
1388
+ ${pc5.dim("AI suggestion:")} ${pc5.bold(pc5.cyan(regen))}`);
1604
1389
  const ok = await confirmPrompt("Use this message?");
1605
1390
  finalMessage = ok ? regen : await inputPrompt("Enter commit message manually");
1606
1391
  } else {
@@ -1615,7 +1400,7 @@ ${pc6.bold("Changed files:")}`);
1615
1400
  if (convention2 !== "none") {
1616
1401
  console.log();
1617
1402
  for (const hint of CONVENTION_FORMAT_HINTS[convention2]) {
1618
- console.log(pc6.dim(hint));
1403
+ console.log(pc5.dim(hint));
1619
1404
  }
1620
1405
  console.log();
1621
1406
  }
@@ -1639,7 +1424,7 @@ ${pc6.bold("Changed files:")}`);
1639
1424
  error(`Failed to commit: ${result.stderr}`);
1640
1425
  process.exit(1);
1641
1426
  }
1642
- success(`✅ Committed: ${pc6.bold(finalMessage)}`);
1427
+ success(`✅ Committed: ${pc5.bold(finalMessage)}`);
1643
1428
  }
1644
1429
  });
1645
1430
  async function runGroupCommit(model, config) {
@@ -1656,9 +1441,9 @@ async function runGroupCommit(model, config) {
1656
1441
  process.exit(1);
1657
1442
  }
1658
1443
  console.log(`
1659
- ${pc6.bold("Changed files:")}`);
1444
+ ${pc5.bold("Changed files:")}`);
1660
1445
  for (const f of changedFiles) {
1661
- console.log(` ${pc6.dim("•")} ${f}`);
1446
+ console.log(` ${pc5.dim("•")} ${f}`);
1662
1447
  }
1663
1448
  const spinner = createSpinner(`Asking AI to group ${changedFiles.length} file(s) into logical commits...`);
1664
1449
  const diffs = await getFullDiffForFiles(changedFiles);
@@ -1696,13 +1481,13 @@ ${pc6.bold("Changed files:")}`);
1696
1481
  let commitAll = false;
1697
1482
  while (!proceedToCommit) {
1698
1483
  console.log(`
1699
- ${pc6.bold(`AI suggested ${validGroups.length} commit group(s):`)}
1484
+ ${pc5.bold(`AI suggested ${validGroups.length} commit group(s):`)}
1700
1485
  `);
1701
1486
  for (let i = 0;i < validGroups.length; i++) {
1702
1487
  const g = validGroups[i];
1703
- console.log(` ${pc6.cyan(`Group ${i + 1}:`)} ${pc6.bold(g.message)}`);
1488
+ console.log(` ${pc5.cyan(`Group ${i + 1}:`)} ${pc5.bold(g.message)}`);
1704
1489
  for (const f of g.files) {
1705
- console.log(` ${pc6.dim("•")} ${f}`);
1490
+ console.log(` ${pc5.dim("•")} ${f}`);
1706
1491
  }
1707
1492
  console.log();
1708
1493
  }
@@ -1746,16 +1531,16 @@ ${pc6.bold(`AI suggested ${validGroups.length} commit group(s):`)}
1746
1531
  continue;
1747
1532
  }
1748
1533
  committed++;
1749
- success(`✅ Committed group ${i + 1}: ${pc6.bold(group.message)}`);
1534
+ success(`✅ Committed group ${i + 1}: ${pc5.bold(group.message)}`);
1750
1535
  }
1751
1536
  } else {
1752
1537
  for (let i = 0;i < validGroups.length; i++) {
1753
1538
  const group = validGroups[i];
1754
- console.log(pc6.bold(`
1539
+ console.log(pc5.bold(`
1755
1540
  ── Group ${i + 1}/${validGroups.length} ──`));
1756
- console.log(` ${pc6.cyan(group.message)}`);
1541
+ console.log(` ${pc5.cyan(group.message)}`);
1757
1542
  for (const f of group.files) {
1758
- console.log(` ${pc6.dim("•")} ${f}`);
1543
+ console.log(` ${pc5.dim("•")} ${f}`);
1759
1544
  }
1760
1545
  let message = group.message;
1761
1546
  let actionDone = false;
@@ -1777,7 +1562,7 @@ ${pc6.bold(`AI suggested ${validGroups.length} commit group(s):`)}
1777
1562
  if (newMsg) {
1778
1563
  message = newMsg;
1779
1564
  group.message = newMsg;
1780
- regenSpinner.success(`New message: ${pc6.bold(message)}`);
1565
+ regenSpinner.success(`New message: ${pc5.bold(message)}`);
1781
1566
  } else {
1782
1567
  regenSpinner.fail("AI could not generate a new message. Keeping current one.");
1783
1568
  }
@@ -1817,7 +1602,7 @@ ${pc6.bold(`AI suggested ${validGroups.length} commit group(s):`)}
1817
1602
  continue;
1818
1603
  }
1819
1604
  committed++;
1820
- success(`✅ Committed group ${i + 1}: ${pc6.bold(message)}`);
1605
+ success(`✅ Committed group ${i + 1}: ${pc5.bold(message)}`);
1821
1606
  actionDone = true;
1822
1607
  }
1823
1608
  }
@@ -1833,12 +1618,12 @@ ${pc6.bold(`AI suggested ${validGroups.length} commit group(s):`)}
1833
1618
 
1834
1619
  // src/commands/doctor.ts
1835
1620
  import { execFile as execFileCb3 } from "node:child_process";
1836
- import { defineCommand as defineCommand4 } from "citty";
1837
- import pc7 from "picocolors";
1621
+ import { defineCommand as defineCommand3 } from "citty";
1622
+ import pc6 from "picocolors";
1838
1623
  // package.json
1839
1624
  var package_default = {
1840
1625
  name: "contribute-now",
1841
- version: "0.2.0-dev.2621ffa",
1626
+ version: "0.2.0-dev.69b11fd",
1842
1627
  description: "Git workflow CLI for squash-merge two-branch models. Keeps dev in sync with main after squash merges.",
1843
1628
  type: "module",
1844
1629
  bin: {
@@ -1927,16 +1712,16 @@ async function getRepoInfoFromRemote(remote = "origin") {
1927
1712
  }
1928
1713
 
1929
1714
  // src/commands/doctor.ts
1930
- var PASS = ` ${pc7.green("✔")} `;
1931
- var FAIL = ` ${pc7.red("✗")} `;
1932
- var WARN = ` ${pc7.yellow("⚠")} `;
1715
+ var PASS = ` ${pc6.green("✔")} `;
1716
+ var FAIL = ` ${pc6.red("✗")} `;
1717
+ var WARN = ` ${pc6.yellow("⚠")} `;
1933
1718
  function printReport(report) {
1934
1719
  for (const section of report.sections) {
1935
1720
  console.log(`
1936
- ${pc7.bold(pc7.underline(section.title))}`);
1721
+ ${pc6.bold(pc6.underline(section.title))}`);
1937
1722
  for (const check of section.checks) {
1938
1723
  const prefix = check.ok ? check.warning ? WARN : PASS : FAIL;
1939
- const text2 = check.detail ? `${check.label} ${pc7.dim(`— ${check.detail}`)}` : check.label;
1724
+ const text2 = check.detail ? `${check.label} ${pc6.dim(`— ${check.detail}`)}` : check.label;
1940
1725
  console.log(`${prefix}${text2}`);
1941
1726
  }
1942
1727
  }
@@ -2144,7 +1929,7 @@ function envSection() {
2144
1929
  }
2145
1930
  return { title: "Environment", checks };
2146
1931
  }
2147
- var doctor_default = defineCommand4({
1932
+ var doctor_default = defineCommand3({
2148
1933
  meta: {
2149
1934
  name: "doctor",
2150
1935
  description: "Diagnose the contribute-now CLI environment and configuration"
@@ -2180,14 +1965,14 @@ var doctor_default = defineCommand4({
2180
1965
  const failures = total.filter((c) => !c.ok);
2181
1966
  const warnings = total.filter((c) => c.ok && c.warning);
2182
1967
  if (failures.length === 0 && warnings.length === 0) {
2183
- console.log(` ${pc7.green("All checks passed!")} No issues detected.
1968
+ console.log(` ${pc6.green("All checks passed!")} No issues detected.
2184
1969
  `);
2185
1970
  } else {
2186
1971
  if (failures.length > 0) {
2187
- console.log(` ${pc7.red(`${failures.length} issue${failures.length !== 1 ? "s" : ""} found.`)}`);
1972
+ console.log(` ${pc6.red(`${failures.length} issue${failures.length !== 1 ? "s" : ""} found.`)}`);
2188
1973
  }
2189
1974
  if (warnings.length > 0) {
2190
- console.log(` ${pc7.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}.`)}`);
1975
+ console.log(` ${pc6.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}.`)}`);
2191
1976
  }
2192
1977
  console.log();
2193
1978
  }
@@ -2197,8 +1982,8 @@ var doctor_default = defineCommand4({
2197
1982
  // src/commands/hook.ts
2198
1983
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
2199
1984
  import { join as join3 } from "node:path";
2200
- import { defineCommand as defineCommand5 } from "citty";
2201
- import pc8 from "picocolors";
1985
+ import { defineCommand as defineCommand4 } from "citty";
1986
+ import pc7 from "picocolors";
2202
1987
  var HOOK_MARKER = "# managed by contribute-now";
2203
1988
  function getHooksDir(cwd = process.cwd()) {
2204
1989
  return join3(cwd, ".git", "hooks");
@@ -2236,7 +2021,7 @@ else
2236
2021
  fi
2237
2022
  `;
2238
2023
  }
2239
- var hook_default = defineCommand5({
2024
+ var hook_default = defineCommand4({
2240
2025
  meta: {
2241
2026
  name: "hook",
2242
2027
  description: "Install or uninstall the commit-msg git hook"
@@ -2294,8 +2079,8 @@ async function installHook() {
2294
2079
  }
2295
2080
  writeFileSync2(hookPath, generateHookScript(), { mode: 493 });
2296
2081
  success(`commit-msg hook installed.`);
2297
- info(`Convention: ${pc8.bold(CONVENTION_LABELS[config.commitConvention])}`);
2298
- info(`Path: ${pc8.dim(hookPath)}`);
2082
+ info(`Convention: ${pc7.bold(CONVENTION_LABELS[config.commitConvention])}`);
2083
+ info(`Path: ${pc7.dim(hookPath)}`);
2299
2084
  }
2300
2085
  async function uninstallHook() {
2301
2086
  heading("\uD83E\uDE9D hook uninstall");
@@ -2313,165 +2098,10 @@ async function uninstallHook() {
2313
2098
  success("commit-msg hook removed.");
2314
2099
  }
2315
2100
 
2316
- // src/commands/log.ts
2317
- import { defineCommand as defineCommand6 } from "citty";
2318
- import pc9 from "picocolors";
2319
- var log_default = defineCommand6({
2320
- meta: {
2321
- name: "log",
2322
- description: "Show a colorized, workflow-aware commit log with graph"
2323
- },
2324
- args: {
2325
- count: {
2326
- type: "string",
2327
- alias: "n",
2328
- description: "Number of commits to show (default: 20)"
2329
- },
2330
- all: {
2331
- type: "boolean",
2332
- alias: "a",
2333
- description: "Show all branches, not just current",
2334
- default: false
2335
- },
2336
- graph: {
2337
- type: "boolean",
2338
- alias: "g",
2339
- description: "Show graph view with branch lines",
2340
- default: true
2341
- },
2342
- branch: {
2343
- type: "string",
2344
- alias: "b",
2345
- description: "Show log for a specific branch"
2346
- }
2347
- },
2348
- async run({ args }) {
2349
- if (!await isGitRepo()) {
2350
- error("Not inside a git repository.");
2351
- process.exit(1);
2352
- }
2353
- const config = readConfig();
2354
- const count = args.count ? Number.parseInt(args.count, 10) : 20;
2355
- const showAll = args.all;
2356
- const showGraph = args.graph;
2357
- const targetBranch = args.branch;
2358
- const protectedBranches = config ? getProtectedBranches(config) : ["main", "master"];
2359
- const currentBranch = await getCurrentBranch();
2360
- heading("\uD83D\uDCDC commit log");
2361
- if (showGraph) {
2362
- const lines = await getLogGraph({ count, all: showAll, branch: targetBranch });
2363
- if (lines.length === 0) {
2364
- console.log(pc9.dim(" No commits found."));
2365
- console.log();
2366
- return;
2367
- }
2368
- console.log();
2369
- for (const line of lines) {
2370
- console.log(` ${colorizeGraphLine(line, protectedBranches, currentBranch)}`);
2371
- }
2372
- } else {
2373
- const entries = await getLogEntries({ count, all: showAll, branch: targetBranch });
2374
- if (entries.length === 0) {
2375
- console.log(pc9.dim(" No commits found."));
2376
- console.log();
2377
- return;
2378
- }
2379
- console.log();
2380
- for (const entry of entries) {
2381
- const hashStr = pc9.yellow(entry.hash);
2382
- const refsStr = entry.refs ? ` ${colorizeRefs(entry.refs, protectedBranches, currentBranch)}` : "";
2383
- const subjectStr = colorizeSubject(entry.subject);
2384
- console.log(` ${hashStr}${refsStr} ${subjectStr}`);
2385
- }
2386
- }
2387
- console.log();
2388
- console.log(pc9.dim(` Showing ${count} most recent commits${showAll ? " (all branches)" : targetBranch ? ` (${targetBranch})` : ""}`));
2389
- console.log(pc9.dim(` Use ${pc9.bold("contrib log -n 50")} for more, or ${pc9.bold("contrib log --all")} for all branches`));
2390
- console.log();
2391
- }
2392
- });
2393
- function colorizeGraphLine(line, protectedBranches, currentBranch) {
2394
- const match = line.match(/^([|/\\*\s_.-]*)([a-f0-9]{7,12})(\s+\(([^)]+)\))?\s*(.*)/);
2395
- if (!match) {
2396
- return pc9.cyan(line);
2397
- }
2398
- const [, graphPart = "", hash, , refs, subject = ""] = match;
2399
- const parts = [];
2400
- if (graphPart) {
2401
- parts.push(colorizeGraphChars(graphPart));
2402
- }
2403
- parts.push(pc9.yellow(hash));
2404
- if (refs) {
2405
- parts.push(` (${colorizeRefs(refs, protectedBranches, currentBranch)})`);
2406
- }
2407
- parts.push(` ${colorizeSubject(subject)}`);
2408
- return parts.join("");
2409
- }
2410
- function colorizeGraphChars(graphPart) {
2411
- return graphPart.split("").map((ch) => {
2412
- switch (ch) {
2413
- case "*":
2414
- return pc9.green(ch);
2415
- case "|":
2416
- return pc9.cyan(ch);
2417
- case "/":
2418
- case "\\":
2419
- return pc9.cyan(ch);
2420
- case "-":
2421
- case "_":
2422
- return pc9.cyan(ch);
2423
- default:
2424
- return ch;
2425
- }
2426
- }).join("");
2427
- }
2428
- function colorizeRefs(refs, protectedBranches, currentBranch) {
2429
- return refs.split(",").map((ref) => {
2430
- const trimmed = ref.trim();
2431
- if (trimmed.startsWith("HEAD ->") || trimmed === "HEAD") {
2432
- const branchName = trimmed.replace("HEAD -> ", "");
2433
- if (trimmed === "HEAD") {
2434
- return pc9.bold(pc9.cyan("HEAD"));
2435
- }
2436
- return `${pc9.bold(pc9.cyan("HEAD"))} ${pc9.dim("->")} ${colorizeRefName(branchName, protectedBranches, currentBranch)}`;
2437
- }
2438
- if (trimmed.startsWith("tag:")) {
2439
- return pc9.bold(pc9.magenta(trimmed));
2440
- }
2441
- return colorizeRefName(trimmed, protectedBranches, currentBranch);
2442
- }).join(pc9.dim(", "));
2443
- }
2444
- function colorizeRefName(name, protectedBranches, currentBranch) {
2445
- const isRemote = name.includes("/");
2446
- const localName = isRemote ? name.split("/").slice(1).join("/") : name;
2447
- if (protectedBranches.includes(localName)) {
2448
- return isRemote ? pc9.bold(pc9.red(name)) : pc9.bold(pc9.red(name));
2449
- }
2450
- if (localName === currentBranch) {
2451
- return pc9.bold(pc9.green(name));
2452
- }
2453
- if (isRemote) {
2454
- return pc9.blue(name);
2455
- }
2456
- return pc9.green(name);
2457
- }
2458
- function colorizeSubject(subject) {
2459
- const emojiMatch = subject.match(/^([\p{Emoji_Presentation}\p{Emoji}\uFE0F]+\s*)/u);
2460
- if (emojiMatch) {
2461
- const emoji = emojiMatch[1];
2462
- const rest = subject.slice(emoji.length);
2463
- return `${emoji}${pc9.white(rest)}`;
2464
- }
2465
- if (subject.startsWith("Merge ")) {
2466
- return pc9.dim(subject);
2467
- }
2468
- return pc9.white(subject);
2469
- }
2470
-
2471
2101
  // src/commands/setup.ts
2472
- import { defineCommand as defineCommand7 } from "citty";
2473
- import pc10 from "picocolors";
2474
- var setup_default = defineCommand7({
2102
+ import { defineCommand as defineCommand5 } from "citty";
2103
+ import pc8 from "picocolors";
2104
+ var setup_default = defineCommand5({
2475
2105
  meta: {
2476
2106
  name: "setup",
2477
2107
  description: "Initialize contribute-now config for this repo (.contributerc.json)"
@@ -2492,7 +2122,7 @@ var setup_default = defineCommand7({
2492
2122
  workflow = "github-flow";
2493
2123
  else if (workflowChoice.startsWith("Git Flow"))
2494
2124
  workflow = "git-flow";
2495
- info(`Workflow: ${pc10.bold(WORKFLOW_DESCRIPTIONS[workflow])}`);
2125
+ info(`Workflow: ${pc8.bold(WORKFLOW_DESCRIPTIONS[workflow])}`);
2496
2126
  const conventionChoice = await selectPrompt("Which commit convention should this project use?", [
2497
2127
  `${CONVENTION_DESCRIPTIONS["clean-commit"]} (recommended)`,
2498
2128
  CONVENTION_DESCRIPTIONS.conventional,
@@ -2545,8 +2175,8 @@ var setup_default = defineCommand7({
2545
2175
  detectedRole = roleChoice;
2546
2176
  detectionSource = "user selection";
2547
2177
  } else {
2548
- info(`Detected role: ${pc10.bold(detectedRole)} (via ${detectionSource})`);
2549
- const confirmed = await confirmPrompt(`Role detected as ${pc10.bold(detectedRole)}. Is this correct?`);
2178
+ info(`Detected role: ${pc8.bold(detectedRole)} (via ${detectionSource})`);
2179
+ const confirmed = await confirmPrompt(`Role detected as ${pc8.bold(detectedRole)}. Is this correct?`);
2550
2180
  if (!confirmed) {
2551
2181
  const roleChoice = await selectPrompt("Select your role:", ["maintainer", "contributor"]);
2552
2182
  detectedRole = roleChoice;
@@ -2591,22 +2221,22 @@ var setup_default = defineCommand7({
2591
2221
  warn(' echo ".contributerc.json" >> .gitignore');
2592
2222
  }
2593
2223
  console.log();
2594
- info(`Workflow: ${pc10.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
2595
- info(`Convention: ${pc10.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
2596
- info(`Role: ${pc10.bold(config.role)}`);
2224
+ info(`Workflow: ${pc8.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
2225
+ info(`Convention: ${pc8.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
2226
+ info(`Role: ${pc8.bold(config.role)}`);
2597
2227
  if (config.devBranch) {
2598
- info(`Main: ${pc10.bold(config.mainBranch)} | Dev: ${pc10.bold(config.devBranch)}`);
2228
+ info(`Main: ${pc8.bold(config.mainBranch)} | Dev: ${pc8.bold(config.devBranch)}`);
2599
2229
  } else {
2600
- info(`Main: ${pc10.bold(config.mainBranch)}`);
2230
+ info(`Main: ${pc8.bold(config.mainBranch)}`);
2601
2231
  }
2602
- info(`Origin: ${pc10.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${pc10.bold(config.upstream)}` : ""}`);
2232
+ info(`Origin: ${pc8.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${pc8.bold(config.upstream)}` : ""}`);
2603
2233
  }
2604
2234
  });
2605
2235
 
2606
2236
  // src/commands/start.ts
2607
- import { defineCommand as defineCommand8 } from "citty";
2608
- import pc11 from "picocolors";
2609
- var start_default = defineCommand8({
2237
+ import { defineCommand as defineCommand6 } from "citty";
2238
+ import pc9 from "picocolors";
2239
+ var start_default = defineCommand6({
2610
2240
  meta: {
2611
2241
  name: "start",
2612
2242
  description: "Create a new feature branch from the latest base branch"
@@ -2653,8 +2283,8 @@ var start_default = defineCommand8({
2653
2283
  if (suggested) {
2654
2284
  spinner.success("Branch name suggestion ready.");
2655
2285
  console.log(`
2656
- ${pc11.dim("AI suggestion:")} ${pc11.bold(pc11.cyan(suggested))}`);
2657
- const accepted = await confirmPrompt(`Use ${pc11.bold(suggested)} as your branch name?`);
2286
+ ${pc9.dim("AI suggestion:")} ${pc9.bold(pc9.cyan(suggested))}`);
2287
+ const accepted = await confirmPrompt(`Use ${pc9.bold(suggested)} as your branch name?`);
2658
2288
  if (accepted) {
2659
2289
  branchName = suggested;
2660
2290
  } else {
@@ -2665,14 +2295,14 @@ var start_default = defineCommand8({
2665
2295
  }
2666
2296
  }
2667
2297
  if (!hasPrefix(branchName, branchPrefixes)) {
2668
- const prefix = await selectPrompt(`Choose a branch type for ${pc11.bold(branchName)}:`, branchPrefixes);
2298
+ const prefix = await selectPrompt(`Choose a branch type for ${pc9.bold(branchName)}:`, branchPrefixes);
2669
2299
  branchName = formatBranchName(prefix, branchName);
2670
2300
  }
2671
2301
  if (!isValidBranchName(branchName)) {
2672
2302
  error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
2673
2303
  process.exit(1);
2674
2304
  }
2675
- info(`Creating branch: ${pc11.bold(branchName)}`);
2305
+ info(`Creating branch: ${pc9.bold(branchName)}`);
2676
2306
  await fetchRemote(syncSource.remote);
2677
2307
  const updateResult = await updateLocalBranch(baseBranch, syncSource.ref);
2678
2308
  if (updateResult.exitCode !== 0) {}
@@ -2681,14 +2311,14 @@ var start_default = defineCommand8({
2681
2311
  error(`Failed to create branch: ${result.stderr}`);
2682
2312
  process.exit(1);
2683
2313
  }
2684
- success(`✅ Created ${pc11.bold(branchName)} from latest ${pc11.bold(baseBranch)}`);
2314
+ success(`✅ Created ${pc9.bold(branchName)} from latest ${pc9.bold(baseBranch)}`);
2685
2315
  }
2686
2316
  });
2687
2317
 
2688
2318
  // src/commands/status.ts
2689
- import { defineCommand as defineCommand9 } from "citty";
2690
- import pc12 from "picocolors";
2691
- var status_default = defineCommand9({
2319
+ import { defineCommand as defineCommand7 } from "citty";
2320
+ import pc10 from "picocolors";
2321
+ var status_default = defineCommand7({
2692
2322
  meta: {
2693
2323
  name: "status",
2694
2324
  description: "Show sync status of branches"
@@ -2704,8 +2334,8 @@ var status_default = defineCommand9({
2704
2334
  process.exit(1);
2705
2335
  }
2706
2336
  heading("\uD83D\uDCCA contribute-now status");
2707
- console.log(` ${pc12.dim("Workflow:")} ${pc12.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
2708
- console.log(` ${pc12.dim("Role:")} ${pc12.bold(config.role)}`);
2337
+ console.log(` ${pc10.dim("Workflow:")} ${pc10.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
2338
+ console.log(` ${pc10.dim("Role:")} ${pc10.bold(config.role)}`);
2709
2339
  console.log();
2710
2340
  await fetchAll();
2711
2341
  const currentBranch = await getCurrentBranch();
@@ -2714,7 +2344,7 @@ var status_default = defineCommand9({
2714
2344
  const isContributor = config.role === "contributor";
2715
2345
  const [dirty, fileStatus] = await Promise.all([hasUncommittedChanges(), getFileStatus()]);
2716
2346
  if (dirty) {
2717
- console.log(` ${pc12.yellow("⚠")} ${pc12.yellow("Uncommitted changes in working tree")}`);
2347
+ console.log(` ${pc10.yellow("⚠")} ${pc10.yellow("Uncommitted changes in working tree")}`);
2718
2348
  console.log();
2719
2349
  }
2720
2350
  const mainRemote = `${origin}/${mainBranch}`;
@@ -2730,82 +2360,82 @@ var status_default = defineCommand9({
2730
2360
  if (currentBranch && currentBranch !== mainBranch && currentBranch !== config.devBranch) {
2731
2361
  const branchDiv = await getDivergence(currentBranch, baseBranch);
2732
2362
  const branchLine = formatStatus(currentBranch, baseBranch, branchDiv.ahead, branchDiv.behind);
2733
- console.log(branchLine + pc12.dim(` (current ${pc12.green("*")})`));
2363
+ console.log(branchLine + pc10.dim(` (current ${pc10.green("*")})`));
2734
2364
  } else if (currentBranch) {
2735
- console.log(pc12.dim(` (on ${pc12.bold(currentBranch)} branch)`));
2365
+ console.log(pc10.dim(` (on ${pc10.bold(currentBranch)} branch)`));
2736
2366
  }
2737
2367
  const hasFiles = fileStatus.staged.length > 0 || fileStatus.modified.length > 0 || fileStatus.untracked.length > 0;
2738
2368
  if (hasFiles) {
2739
2369
  console.log();
2740
2370
  if (fileStatus.staged.length > 0) {
2741
- console.log(` ${pc12.green("Staged for commit:")}`);
2371
+ console.log(` ${pc10.green("Staged for commit:")}`);
2742
2372
  for (const { file, status } of fileStatus.staged) {
2743
- console.log(` ${pc12.green("+")} ${pc12.dim(`${status}:`)} ${file}`);
2373
+ console.log(` ${pc10.green("+")} ${pc10.dim(`${status}:`)} ${file}`);
2744
2374
  }
2745
2375
  }
2746
2376
  if (fileStatus.modified.length > 0) {
2747
- console.log(` ${pc12.yellow("Unstaged changes:")}`);
2377
+ console.log(` ${pc10.yellow("Unstaged changes:")}`);
2748
2378
  for (const { file, status } of fileStatus.modified) {
2749
- console.log(` ${pc12.yellow("~")} ${pc12.dim(`${status}:`)} ${file}`);
2379
+ console.log(` ${pc10.yellow("~")} ${pc10.dim(`${status}:`)} ${file}`);
2750
2380
  }
2751
2381
  }
2752
2382
  if (fileStatus.untracked.length > 0) {
2753
- console.log(` ${pc12.red("Untracked files:")}`);
2383
+ console.log(` ${pc10.red("Untracked files:")}`);
2754
2384
  for (const file of fileStatus.untracked) {
2755
- console.log(` ${pc12.red("?")} ${file}`);
2385
+ console.log(` ${pc10.red("?")} ${file}`);
2756
2386
  }
2757
2387
  }
2758
2388
  } else if (!dirty) {
2759
- console.log(` ${pc12.green("✓")} ${pc12.dim("Working tree clean")}`);
2389
+ console.log(` ${pc10.green("✓")} ${pc10.dim("Working tree clean")}`);
2760
2390
  }
2761
2391
  const tips = [];
2762
2392
  if (fileStatus.staged.length > 0) {
2763
- tips.push(`Run ${pc12.bold("contrib commit")} to commit staged changes`);
2393
+ tips.push(`Run ${pc10.bold("contrib commit")} to commit staged changes`);
2764
2394
  }
2765
2395
  if (fileStatus.modified.length > 0 || fileStatus.untracked.length > 0) {
2766
- tips.push(`Run ${pc12.bold("contrib commit")} to stage and commit changes`);
2396
+ tips.push(`Run ${pc10.bold("contrib commit")} to stage and commit changes`);
2767
2397
  }
2768
2398
  if (fileStatus.staged.length === 0 && fileStatus.modified.length === 0 && fileStatus.untracked.length === 0 && currentBranch && currentBranch !== mainBranch && currentBranch !== config.devBranch) {
2769
2399
  const branchDiv = await getDivergence(currentBranch, `${origin}/${currentBranch}`);
2770
2400
  if (branchDiv.ahead > 0) {
2771
- tips.push(`Run ${pc12.bold("contrib submit")} to push and create/update your PR`);
2401
+ tips.push(`Run ${pc10.bold("contrib submit")} to push and create/update your PR`);
2772
2402
  }
2773
2403
  }
2774
2404
  if (tips.length > 0) {
2775
2405
  console.log();
2776
- console.log(` ${pc12.dim("\uD83D\uDCA1 Tip:")}`);
2406
+ console.log(` ${pc10.dim("\uD83D\uDCA1 Tip:")}`);
2777
2407
  for (const tip of tips) {
2778
- console.log(` ${pc12.dim(tip)}`);
2408
+ console.log(` ${pc10.dim(tip)}`);
2779
2409
  }
2780
2410
  }
2781
2411
  console.log();
2782
2412
  }
2783
2413
  });
2784
2414
  function formatStatus(branch, base, ahead, behind) {
2785
- const label = pc12.bold(branch.padEnd(20));
2415
+ const label = pc10.bold(branch.padEnd(20));
2786
2416
  if (ahead === 0 && behind === 0) {
2787
- return ` ${pc12.green("✓")} ${label} ${pc12.dim(`in sync with ${base}`)}`;
2417
+ return ` ${pc10.green("✓")} ${label} ${pc10.dim(`in sync with ${base}`)}`;
2788
2418
  }
2789
2419
  if (ahead > 0 && behind === 0) {
2790
- return ` ${pc12.yellow("↑")} ${label} ${pc12.yellow(`${ahead} commit${ahead !== 1 ? "s" : ""} ahead of ${base}`)}`;
2420
+ return ` ${pc10.yellow("↑")} ${label} ${pc10.yellow(`${ahead} commit${ahead !== 1 ? "s" : ""} ahead of ${base}`)}`;
2791
2421
  }
2792
2422
  if (behind > 0 && ahead === 0) {
2793
- return ` ${pc12.red("↓")} ${label} ${pc12.red(`${behind} commit${behind !== 1 ? "s" : ""} behind ${base}`)}`;
2423
+ return ` ${pc10.red("↓")} ${label} ${pc10.red(`${behind} commit${behind !== 1 ? "s" : ""} behind ${base}`)}`;
2794
2424
  }
2795
- return ` ${pc12.red("⚡")} ${label} ${pc12.yellow(`${ahead} ahead`)}${pc12.dim(", ")}${pc12.red(`${behind} behind`)} ${pc12.dim(base)}`;
2425
+ return ` ${pc10.red("⚡")} ${label} ${pc10.yellow(`${ahead} ahead`)}${pc10.dim(", ")}${pc10.red(`${behind} behind`)} ${pc10.dim(base)}`;
2796
2426
  }
2797
2427
 
2798
2428
  // src/commands/submit.ts
2799
- import { defineCommand as defineCommand10 } from "citty";
2800
- import pc13 from "picocolors";
2429
+ import { defineCommand as defineCommand8 } from "citty";
2430
+ import pc11 from "picocolors";
2801
2431
  async function performSquashMerge(origin, baseBranch, featureBranch, options) {
2802
- info(`Checking out ${pc13.bold(baseBranch)}...`);
2432
+ info(`Checking out ${pc11.bold(baseBranch)}...`);
2803
2433
  const coResult = await checkoutBranch2(baseBranch);
2804
2434
  if (coResult.exitCode !== 0) {
2805
2435
  error(`Failed to checkout ${baseBranch}: ${coResult.stderr}`);
2806
2436
  process.exit(1);
2807
2437
  }
2808
- info(`Squash merging ${pc13.bold(featureBranch)} into ${pc13.bold(baseBranch)}...`);
2438
+ info(`Squash merging ${pc11.bold(featureBranch)} into ${pc11.bold(baseBranch)}...`);
2809
2439
  const mergeResult = await mergeSquash(featureBranch);
2810
2440
  if (mergeResult.exitCode !== 0) {
2811
2441
  error(`Squash merge failed: ${mergeResult.stderr}`);
@@ -2835,26 +2465,26 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
2835
2465
  error(`Commit failed: ${commitResult.stderr}`);
2836
2466
  process.exit(1);
2837
2467
  }
2838
- info(`Pushing ${pc13.bold(baseBranch)} to ${origin}...`);
2468
+ info(`Pushing ${pc11.bold(baseBranch)} to ${origin}...`);
2839
2469
  const pushResult = await pushBranch(origin, baseBranch);
2840
2470
  if (pushResult.exitCode !== 0) {
2841
2471
  error(`Failed to push ${baseBranch}: ${pushResult.stderr}`);
2842
2472
  process.exit(1);
2843
2473
  }
2844
- info(`Deleting local branch ${pc13.bold(featureBranch)}...`);
2474
+ info(`Deleting local branch ${pc11.bold(featureBranch)}...`);
2845
2475
  const delLocal = await forceDeleteBranch(featureBranch);
2846
2476
  if (delLocal.exitCode !== 0) {
2847
2477
  warn(`Could not delete local branch: ${delLocal.stderr.trim()}`);
2848
2478
  }
2849
- info(`Deleting remote branch ${pc13.bold(featureBranch)}...`);
2479
+ info(`Deleting remote branch ${pc11.bold(featureBranch)}...`);
2850
2480
  const delRemote = await deleteRemoteBranch(origin, featureBranch);
2851
2481
  if (delRemote.exitCode !== 0) {
2852
2482
  warn(`Could not delete remote branch: ${delRemote.stderr.trim()}`);
2853
2483
  }
2854
- success(`✅ Squash merged ${pc13.bold(featureBranch)} into ${pc13.bold(baseBranch)} and pushed.`);
2855
- info(`Run ${pc13.bold("contrib start")} to begin a new feature.`);
2484
+ success(`✅ Squash merged ${pc11.bold(featureBranch)} into ${pc11.bold(baseBranch)} and pushed.`);
2485
+ info(`Run ${pc11.bold("contrib start")} to begin a new feature.`);
2856
2486
  }
2857
- var submit_default = defineCommand10({
2487
+ var submit_default = defineCommand8({
2858
2488
  meta: {
2859
2489
  name: "submit",
2860
2490
  description: "Push current branch and create a pull request"
@@ -2894,73 +2524,8 @@ var submit_default = defineCommand10({
2894
2524
  process.exit(1);
2895
2525
  }
2896
2526
  if (protectedBranches.includes(currentBranch)) {
2897
- heading("\uD83D\uDE80 contrib submit");
2898
- warn(`You're on ${pc13.bold(currentBranch)}, which is a protected branch. PRs should come from feature branches.`);
2899
- await fetchAll();
2900
- const remoteRef = `${origin}/${currentBranch}`;
2901
- const localWork = await hasLocalWork(origin, currentBranch);
2902
- const dirty = await hasUncommittedChanges();
2903
- const hasCommits = localWork.unpushedCommits > 0;
2904
- const hasAnything = hasCommits || dirty;
2905
- if (!hasAnything) {
2906
- error("No local changes or commits to move. Switch to a feature branch first.");
2907
- info(` Run ${pc13.bold("contrib start")} to create a new feature branch.`);
2908
- process.exit(1);
2909
- }
2910
- if (hasCommits) {
2911
- info(`Found ${pc13.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${pc13.bold(currentBranch)}.`);
2912
- }
2913
- if (dirty) {
2914
- info("You also have uncommitted changes in the working tree.");
2915
- }
2916
- console.log();
2917
- const MOVE_BRANCH = "Move my changes to a new feature branch";
2918
- const CANCEL2 = "Cancel (stay on this branch)";
2919
- const action = await selectPrompt("Let's get you back on track. What would you like to do?", [MOVE_BRANCH, CANCEL2]);
2920
- if (action === CANCEL2) {
2921
- info("No changes made. You are still on your current branch.");
2922
- return;
2923
- }
2924
- info(pc13.dim("Tip: Describe what you're working on in plain English and we'll generate a branch name."));
2925
- const description = await inputPrompt("What are you working on?");
2926
- let newBranchName = description;
2927
- if (looksLikeNaturalLanguage(description)) {
2928
- const copilotError = await checkCopilotAvailable();
2929
- if (!copilotError) {
2930
- const spinner = createSpinner("Generating branch name suggestion...");
2931
- const suggested = await suggestBranchName(description, args.model);
2932
- if (suggested) {
2933
- spinner.success("Branch name suggestion ready.");
2934
- console.log(`
2935
- ${pc13.dim("AI suggestion:")} ${pc13.bold(pc13.cyan(suggested))}`);
2936
- const accepted = await confirmPrompt(`Use ${pc13.bold(suggested)} as your branch name?`);
2937
- newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
2938
- } else {
2939
- spinner.fail("AI did not return a suggestion.");
2940
- newBranchName = await inputPrompt("Enter branch name", description);
2941
- }
2942
- }
2943
- }
2944
- if (!hasPrefix(newBranchName, config.branchPrefixes)) {
2945
- const prefix = await selectPrompt(`Choose a branch type for ${pc13.bold(newBranchName)}:`, config.branchPrefixes);
2946
- newBranchName = formatBranchName(prefix, newBranchName);
2947
- }
2948
- if (!isValidBranchName(newBranchName)) {
2949
- error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
2950
- process.exit(1);
2951
- }
2952
- const branchResult = await createBranch(newBranchName);
2953
- if (branchResult.exitCode !== 0) {
2954
- error(`Failed to create branch: ${branchResult.stderr}`);
2955
- process.exit(1);
2956
- }
2957
- success(`Created ${pc13.bold(newBranchName)} with your changes.`);
2958
- await updateLocalBranch(currentBranch, remoteRef);
2959
- info(`Reset ${pc13.bold(currentBranch)} back to ${pc13.bold(remoteRef)} — no damage done.`);
2960
- console.log();
2961
- success(`You're now on ${pc13.bold(newBranchName)} with all your work intact.`);
2962
- info(`Run ${pc13.bold("contrib submit")} again to push and create your PR.`);
2963
- return;
2527
+ error(`Cannot submit ${protectedBranches.map((b) => pc11.bold(b)).join(" or ")} as a PR. Switch to your feature branch.`);
2528
+ process.exit(1);
2964
2529
  }
2965
2530
  heading("\uD83D\uDE80 contrib submit");
2966
2531
  const ghInstalled = await checkGhInstalled();
@@ -2968,7 +2533,7 @@ var submit_default = defineCommand10({
2968
2533
  if (ghInstalled && ghAuthed) {
2969
2534
  const mergedPR = await getMergedPRForBranch(currentBranch);
2970
2535
  if (mergedPR) {
2971
- warn(`PR #${mergedPR.number} (${pc13.bold(mergedPR.title)}) was already merged.`);
2536
+ warn(`PR #${mergedPR.number} (${pc11.bold(mergedPR.title)}) was already merged.`);
2972
2537
  const localWork = await hasLocalWork(origin, currentBranch);
2973
2538
  const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
2974
2539
  if (hasWork) {
@@ -2976,7 +2541,7 @@ var submit_default = defineCommand10({
2976
2541
  warn("You have uncommitted changes in your working tree.");
2977
2542
  }
2978
2543
  if (localWork.unpushedCommits > 0) {
2979
- warn(`You have ${pc13.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not in the merged PR.`);
2544
+ warn(`You have ${pc11.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not in the merged PR.`);
2980
2545
  }
2981
2546
  const SAVE_NEW_BRANCH = "Save changes to a new branch";
2982
2547
  const DISCARD = "Discard all changes and clean up";
@@ -2987,7 +2552,7 @@ var submit_default = defineCommand10({
2987
2552
  return;
2988
2553
  }
2989
2554
  if (action === SAVE_NEW_BRANCH) {
2990
- info(pc13.dim("Tip: Describe what you're working on in plain English and we'll generate a branch name."));
2555
+ info(pc11.dim("Tip: Describe what you're working on in plain English and we'll generate a branch name."));
2991
2556
  const description = await inputPrompt("What are you working on?");
2992
2557
  let newBranchName = description;
2993
2558
  if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
@@ -2996,8 +2561,8 @@ var submit_default = defineCommand10({
2996
2561
  if (suggested) {
2997
2562
  spinner.success("Branch name suggestion ready.");
2998
2563
  console.log(`
2999
- ${pc13.dim("AI suggestion:")} ${pc13.bold(pc13.cyan(suggested))}`);
3000
- const accepted = await confirmPrompt(`Use ${pc13.bold(suggested)} as your branch name?`);
2564
+ ${pc11.dim("AI suggestion:")} ${pc11.bold(pc11.cyan(suggested))}`);
2565
+ const accepted = await confirmPrompt(`Use ${pc11.bold(suggested)} as your branch name?`);
3001
2566
  newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
3002
2567
  } else {
3003
2568
  spinner.fail("AI did not return a suggestion.");
@@ -3005,7 +2570,7 @@ var submit_default = defineCommand10({
3005
2570
  }
3006
2571
  }
3007
2572
  if (!hasPrefix(newBranchName, config.branchPrefixes)) {
3008
- const prefix = await selectPrompt(`Choose a branch type for ${pc13.bold(newBranchName)}:`, config.branchPrefixes);
2573
+ const prefix = await selectPrompt(`Choose a branch type for ${pc11.bold(newBranchName)}:`, config.branchPrefixes);
3009
2574
  newBranchName = formatBranchName(prefix, newBranchName);
3010
2575
  }
3011
2576
  if (!isValidBranchName(newBranchName)) {
@@ -3019,10 +2584,10 @@ var submit_default = defineCommand10({
3019
2584
  error(`Failed to rename branch: ${renameResult.stderr}`);
3020
2585
  process.exit(1);
3021
2586
  }
3022
- success(`Renamed ${pc13.bold(currentBranch)} → ${pc13.bold(newBranchName)}`);
2587
+ success(`Renamed ${pc11.bold(currentBranch)} → ${pc11.bold(newBranchName)}`);
3023
2588
  await unsetUpstream();
3024
2589
  const syncSource2 = getSyncSource(config);
3025
- info(`Syncing ${pc13.bold(newBranchName)} with latest ${pc13.bold(baseBranch)}...`);
2590
+ info(`Syncing ${pc11.bold(newBranchName)} with latest ${pc11.bold(baseBranch)}...`);
3026
2591
  await fetchRemote(syncSource2.remote);
3027
2592
  let rebaseResult;
3028
2593
  if (staleUpstreamHash) {
@@ -3033,17 +2598,17 @@ var submit_default = defineCommand10({
3033
2598
  }
3034
2599
  if (rebaseResult.exitCode !== 0) {
3035
2600
  warn("Rebase encountered conflicts. Resolve them manually, then run:");
3036
- info(` ${pc13.bold("git rebase --continue")}`);
2601
+ info(` ${pc11.bold("git rebase --continue")}`);
3037
2602
  } else {
3038
- success(`Rebased ${pc13.bold(newBranchName)} onto ${pc13.bold(syncSource2.ref)}.`);
2603
+ success(`Rebased ${pc11.bold(newBranchName)} onto ${pc11.bold(syncSource2.ref)}.`);
3039
2604
  }
3040
- info(`All your changes are preserved. Run ${pc13.bold("contrib submit")} when ready to create a new PR.`);
2605
+ info(`All your changes are preserved. Run ${pc11.bold("contrib submit")} when ready to create a new PR.`);
3041
2606
  return;
3042
2607
  }
3043
2608
  warn("Discarding local changes...");
3044
2609
  }
3045
2610
  const syncSource = getSyncSource(config);
3046
- info(`Switching to ${pc13.bold(baseBranch)} and syncing...`);
2611
+ info(`Switching to ${pc11.bold(baseBranch)} and syncing...`);
3047
2612
  await fetchRemote(syncSource.remote);
3048
2613
  const coResult = await checkoutBranch2(baseBranch);
3049
2614
  if (coResult.exitCode !== 0) {
@@ -3051,16 +2616,16 @@ var submit_default = defineCommand10({
3051
2616
  process.exit(1);
3052
2617
  }
3053
2618
  await updateLocalBranch(baseBranch, syncSource.ref);
3054
- success(`Synced ${pc13.bold(baseBranch)} with ${pc13.bold(syncSource.ref)}.`);
3055
- info(`Deleting stale branch ${pc13.bold(currentBranch)}...`);
2619
+ success(`Synced ${pc11.bold(baseBranch)} with ${pc11.bold(syncSource.ref)}.`);
2620
+ info(`Deleting stale branch ${pc11.bold(currentBranch)}...`);
3056
2621
  const delResult = await forceDeleteBranch(currentBranch);
3057
2622
  if (delResult.exitCode === 0) {
3058
- success(`Deleted ${pc13.bold(currentBranch)}.`);
2623
+ success(`Deleted ${pc11.bold(currentBranch)}.`);
3059
2624
  } else {
3060
2625
  warn(`Could not delete branch: ${delResult.stderr.trim()}`);
3061
2626
  }
3062
2627
  console.log();
3063
- info(`You're now on ${pc13.bold(baseBranch)}. Run ${pc13.bold("contrib start")} to begin a new feature.`);
2628
+ info(`You're now on ${pc11.bold(baseBranch)}. Run ${pc11.bold("contrib start")} to begin a new feature.`);
3064
2629
  return;
3065
2630
  }
3066
2631
  }
@@ -3080,10 +2645,10 @@ var submit_default = defineCommand10({
3080
2645
  prBody = result.body;
3081
2646
  spinner.success("PR description generated.");
3082
2647
  console.log(`
3083
- ${pc13.dim("AI title:")} ${pc13.bold(pc13.cyan(prTitle))}`);
2648
+ ${pc11.dim("AI title:")} ${pc11.bold(pc11.cyan(prTitle))}`);
3084
2649
  console.log(`
3085
- ${pc13.dim("AI body preview:")}`);
3086
- console.log(pc13.dim(prBody.slice(0, 300) + (prBody.length > 300 ? "..." : "")));
2650
+ ${pc11.dim("AI body preview:")}`);
2651
+ console.log(pc11.dim(prBody.slice(0, 300) + (prBody.length > 300 ? "..." : "")));
3087
2652
  } else {
3088
2653
  spinner.fail("AI did not return a PR description.");
3089
2654
  }
@@ -3172,7 +2737,7 @@ ${pc13.dim("AI body preview:")}`);
3172
2737
  });
3173
2738
  return;
3174
2739
  }
3175
- info(`Pushing ${pc13.bold(currentBranch)} to ${origin}...`);
2740
+ info(`Pushing ${pc11.bold(currentBranch)} to ${origin}...`);
3176
2741
  const pushResult = await pushSetUpstream(origin, currentBranch);
3177
2742
  if (pushResult.exitCode !== 0) {
3178
2743
  error(`Failed to push: ${pushResult.stderr}`);
@@ -3184,7 +2749,7 @@ ${pc13.dim("AI body preview:")}`);
3184
2749
  const prUrl = `https://github.com/${repoInfo.owner}/${repoInfo.repo}/compare/${baseBranch}...${currentBranch}?expand=1`;
3185
2750
  console.log();
3186
2751
  info("Create your PR manually:");
3187
- console.log(` ${pc13.cyan(prUrl)}`);
2752
+ console.log(` ${pc11.cyan(prUrl)}`);
3188
2753
  } else {
3189
2754
  info("gh CLI not available. Create your PR manually on GitHub.");
3190
2755
  }
@@ -3192,8 +2757,8 @@ ${pc13.dim("AI body preview:")}`);
3192
2757
  }
3193
2758
  const existingPR = await getPRForBranch(currentBranch);
3194
2759
  if (existingPR) {
3195
- success(`Pushed changes to existing PR #${existingPR.number}: ${pc13.bold(existingPR.title)}`);
3196
- console.log(` ${pc13.cyan(existingPR.url)}`);
2760
+ success(`Pushed changes to existing PR #${existingPR.number}: ${pc11.bold(existingPR.title)}`);
2761
+ console.log(` ${pc11.cyan(existingPR.url)}`);
3197
2762
  return;
3198
2763
  }
3199
2764
  if (submitAction === "fill") {
@@ -3224,9 +2789,9 @@ ${pc13.dim("AI body preview:")}`);
3224
2789
  });
3225
2790
 
3226
2791
  // src/commands/sync.ts
3227
- import { defineCommand as defineCommand11 } from "citty";
3228
- import pc14 from "picocolors";
3229
- var sync_default = defineCommand11({
2792
+ import { defineCommand as defineCommand9 } from "citty";
2793
+ import pc12 from "picocolors";
2794
+ var sync_default = defineCommand9({
3230
2795
  meta: {
3231
2796
  name: "sync",
3232
2797
  description: "Sync your local branches with the remote"
@@ -3268,12 +2833,12 @@ var sync_default = defineCommand11({
3268
2833
  }
3269
2834
  const div = await getDivergence(baseBranch, syncSource.ref);
3270
2835
  if (div.ahead > 0 || div.behind > 0) {
3271
- info(`${pc14.bold(baseBranch)} is ${pc14.yellow(`${div.ahead} ahead`)} and ${pc14.red(`${div.behind} behind`)} ${syncSource.ref}`);
2836
+ info(`${pc12.bold(baseBranch)} is ${pc12.yellow(`${div.ahead} ahead`)} and ${pc12.red(`${div.behind} behind`)} ${syncSource.ref}`);
3272
2837
  } else {
3273
- info(`${pc14.bold(baseBranch)} is already in sync with ${syncSource.ref}`);
2838
+ info(`${pc12.bold(baseBranch)} is already in sync with ${syncSource.ref}`);
3274
2839
  }
3275
2840
  if (!args.yes) {
3276
- const ok = await confirmPrompt(`This will pull ${pc14.bold(syncSource.ref)} into local ${pc14.bold(baseBranch)}.`);
2841
+ const ok = await confirmPrompt(`This will pull ${pc12.bold(syncSource.ref)} into local ${pc12.bold(baseBranch)}.`);
3277
2842
  if (!ok)
3278
2843
  process.exit(0);
3279
2844
  }
@@ -3291,7 +2856,7 @@ var sync_default = defineCommand11({
3291
2856
  if (hasDevBranch(workflow) && role === "maintainer") {
3292
2857
  const mainDiv = await getDivergence(config.mainBranch, `${origin}/${config.mainBranch}`);
3293
2858
  if (mainDiv.behind > 0) {
3294
- info(`Also syncing ${pc14.bold(config.mainBranch)}...`);
2859
+ info(`Also syncing ${pc12.bold(config.mainBranch)}...`);
3295
2860
  const mainCoResult = await checkoutBranch2(config.mainBranch);
3296
2861
  if (mainCoResult.exitCode === 0) {
3297
2862
  const mainPullResult = await pullBranch(origin, config.mainBranch);
@@ -3307,9 +2872,9 @@ var sync_default = defineCommand11({
3307
2872
 
3308
2873
  // src/commands/update.ts
3309
2874
  import { readFileSync as readFileSync4 } from "node:fs";
3310
- import { defineCommand as defineCommand12 } from "citty";
3311
- import pc15 from "picocolors";
3312
- var update_default = defineCommand12({
2875
+ import { defineCommand as defineCommand10 } from "citty";
2876
+ import pc13 from "picocolors";
2877
+ var update_default = defineCommand10({
3313
2878
  meta: {
3314
2879
  name: "update",
3315
2880
  description: "Rebase current branch onto the latest base branch"
@@ -3344,7 +2909,7 @@ var update_default = defineCommand12({
3344
2909
  process.exit(1);
3345
2910
  }
3346
2911
  if (protectedBranches.includes(currentBranch)) {
3347
- error(`Use \`contrib sync\` to update ${protectedBranches.map((b) => pc15.bold(b)).join(" or ")} branches.`);
2912
+ error(`Use \`contrib sync\` to update ${protectedBranches.map((b) => pc13.bold(b)).join(" or ")} branches.`);
3348
2913
  process.exit(1);
3349
2914
  }
3350
2915
  if (await hasUncommittedChanges()) {
@@ -3354,8 +2919,8 @@ var update_default = defineCommand12({
3354
2919
  heading("\uD83D\uDD03 contrib update");
3355
2920
  const mergedPR = await getMergedPRForBranch(currentBranch);
3356
2921
  if (mergedPR) {
3357
- warn(`PR #${mergedPR.number} (${pc15.bold(mergedPR.title)}) has already been merged.`);
3358
- info(`Link: ${pc15.underline(mergedPR.url)}`);
2922
+ warn(`PR #${mergedPR.number} (${pc13.bold(mergedPR.title)}) has already been merged.`);
2923
+ info(`Link: ${pc13.underline(mergedPR.url)}`);
3359
2924
  const localWork = await hasLocalWork(syncSource.remote, currentBranch);
3360
2925
  const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
3361
2926
  if (hasWork) {
@@ -3368,13 +2933,13 @@ var update_default = defineCommand12({
3368
2933
  const SAVE_NEW_BRANCH = "Save changes to a new branch";
3369
2934
  const DISCARD = "Discard all changes and clean up";
3370
2935
  const CANCEL = "Cancel";
3371
- const action = await selectPrompt(`${pc15.bold(currentBranch)} is stale but has local work. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
2936
+ const action = await selectPrompt(`${pc13.bold(currentBranch)} is stale but has local work. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
3372
2937
  if (action === CANCEL) {
3373
2938
  info("No changes made. You are still on your current branch.");
3374
2939
  return;
3375
2940
  }
3376
2941
  if (action === SAVE_NEW_BRANCH) {
3377
- info(pc15.dim("Tip: Describe what you're working on in plain English and we'll generate a branch name."));
2942
+ info(pc13.dim("Tip: Describe what you're working on in plain English and we'll generate a branch name."));
3378
2943
  const description = await inputPrompt("What are you working on?");
3379
2944
  let newBranchName = description;
3380
2945
  if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
@@ -3383,8 +2948,8 @@ var update_default = defineCommand12({
3383
2948
  if (suggested) {
3384
2949
  spinner.success("Branch name suggestion ready.");
3385
2950
  console.log(`
3386
- ${pc15.dim("AI suggestion:")} ${pc15.bold(pc15.cyan(suggested))}`);
3387
- const accepted = await confirmPrompt(`Use ${pc15.bold(suggested)} as your branch name?`);
2951
+ ${pc13.dim("AI suggestion:")} ${pc13.bold(pc13.cyan(suggested))}`);
2952
+ const accepted = await confirmPrompt(`Use ${pc13.bold(suggested)} as your branch name?`);
3388
2953
  newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
3389
2954
  } else {
3390
2955
  spinner.fail("AI did not return a suggestion.");
@@ -3392,7 +2957,7 @@ var update_default = defineCommand12({
3392
2957
  }
3393
2958
  }
3394
2959
  if (!hasPrefix(newBranchName, config.branchPrefixes)) {
3395
- const prefix = await selectPrompt(`Choose a branch type for ${pc15.bold(newBranchName)}:`, config.branchPrefixes);
2960
+ const prefix = await selectPrompt(`Choose a branch type for ${pc13.bold(newBranchName)}:`, config.branchPrefixes);
3396
2961
  newBranchName = formatBranchName(prefix, newBranchName);
3397
2962
  }
3398
2963
  if (!isValidBranchName(newBranchName)) {
@@ -3406,7 +2971,7 @@ var update_default = defineCommand12({
3406
2971
  error(`Failed to rename branch: ${renameResult.stderr}`);
3407
2972
  process.exit(1);
3408
2973
  }
3409
- success(`Renamed ${pc15.bold(currentBranch)} → ${pc15.bold(newBranchName)}`);
2974
+ success(`Renamed ${pc13.bold(currentBranch)} → ${pc13.bold(newBranchName)}`);
3410
2975
  await unsetUpstream();
3411
2976
  await fetchRemote(syncSource.remote);
3412
2977
  let rebaseResult2;
@@ -3418,11 +2983,11 @@ var update_default = defineCommand12({
3418
2983
  }
3419
2984
  if (rebaseResult2.exitCode !== 0) {
3420
2985
  warn("Rebase encountered conflicts. Resolve them manually, then run:");
3421
- info(` ${pc15.bold("git rebase --continue")}`);
2986
+ info(` ${pc13.bold("git rebase --continue")}`);
3422
2987
  } else {
3423
- success(`Rebased ${pc15.bold(newBranchName)} onto ${pc15.bold(syncSource.ref)}.`);
2988
+ success(`Rebased ${pc13.bold(newBranchName)} onto ${pc13.bold(syncSource.ref)}.`);
3424
2989
  }
3425
- info(`All your changes are preserved. Run ${pc15.bold("contrib submit")} when ready to create a new PR.`);
2990
+ info(`All your changes are preserved. Run ${pc13.bold("contrib submit")} when ready to create a new PR.`);
3426
2991
  return;
3427
2992
  }
3428
2993
  warn("Discarding local changes...");
@@ -3434,19 +2999,19 @@ var update_default = defineCommand12({
3434
2999
  process.exit(1);
3435
3000
  }
3436
3001
  await updateLocalBranch(baseBranch, syncSource.ref);
3437
- success(`Synced ${pc15.bold(baseBranch)} with ${pc15.bold(syncSource.ref)}.`);
3438
- info(`Deleting stale branch ${pc15.bold(currentBranch)}...`);
3002
+ success(`Synced ${pc13.bold(baseBranch)} with ${pc13.bold(syncSource.ref)}.`);
3003
+ info(`Deleting stale branch ${pc13.bold(currentBranch)}...`);
3439
3004
  await forceDeleteBranch(currentBranch);
3440
- success(`Deleted ${pc15.bold(currentBranch)}.`);
3441
- info(`Run ${pc15.bold("contrib start")} to begin a new feature branch.`);
3005
+ success(`Deleted ${pc13.bold(currentBranch)}.`);
3006
+ info(`Run ${pc13.bold("contrib start")} to begin a new feature branch.`);
3442
3007
  return;
3443
3008
  }
3444
- info(`Updating ${pc15.bold(currentBranch)} with latest ${pc15.bold(baseBranch)}...`);
3009
+ info(`Updating ${pc13.bold(currentBranch)} with latest ${pc13.bold(baseBranch)}...`);
3445
3010
  await fetchRemote(syncSource.remote);
3446
3011
  await updateLocalBranch(baseBranch, syncSource.ref);
3447
3012
  const rebaseStrategy = await determineRebaseStrategy(currentBranch, syncSource.ref);
3448
3013
  if (rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase) {
3449
- info(pc15.dim(`Using --onto rebase (branch was based on a different ref)`));
3014
+ info(pc13.dim(`Using --onto rebase (branch was based on a different ref)`));
3450
3015
  }
3451
3016
  const rebaseResult = rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase ? await rebaseOnto(syncSource.ref, rebaseStrategy.ontoOldBase) : await rebase(syncSource.ref);
3452
3017
  if (rebaseResult.exitCode !== 0) {
@@ -3475,10 +3040,10 @@ ${content.slice(0, 2000)}
3475
3040
  if (suggestion) {
3476
3041
  spinner.success("AI conflict guidance ready.");
3477
3042
  console.log(`
3478
- ${pc15.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
3479
- console.log(pc15.dim("─".repeat(60)));
3043
+ ${pc13.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
3044
+ console.log(pc13.dim("─".repeat(60)));
3480
3045
  console.log(suggestion);
3481
- console.log(pc15.dim("─".repeat(60)));
3046
+ console.log(pc13.dim("─".repeat(60)));
3482
3047
  console.log();
3483
3048
  } else {
3484
3049
  spinner.fail("AI could not analyze the conflicts.");
@@ -3486,22 +3051,22 @@ ${pc15.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
3486
3051
  }
3487
3052
  }
3488
3053
  }
3489
- console.log(pc15.bold("To resolve:"));
3054
+ console.log(pc13.bold("To resolve:"));
3490
3055
  console.log(` 1. Fix conflicts in the affected files`);
3491
- console.log(` 2. ${pc15.cyan("git add <resolved-files>")}`);
3492
- console.log(` 3. ${pc15.cyan("git rebase --continue")}`);
3056
+ console.log(` 2. ${pc13.cyan("git add <resolved-files>")}`);
3057
+ console.log(` 3. ${pc13.cyan("git rebase --continue")}`);
3493
3058
  console.log();
3494
- console.log(` Or abort: ${pc15.cyan("git rebase --abort")}`);
3059
+ console.log(` Or abort: ${pc13.cyan("git rebase --abort")}`);
3495
3060
  process.exit(1);
3496
3061
  }
3497
- success(`✅ ${pc15.bold(currentBranch)} has been rebased onto latest ${pc15.bold(baseBranch)}`);
3062
+ success(`✅ ${pc13.bold(currentBranch)} has been rebased onto latest ${pc13.bold(baseBranch)}`);
3498
3063
  }
3499
3064
  });
3500
3065
 
3501
3066
  // src/commands/validate.ts
3502
- import { defineCommand as defineCommand13 } from "citty";
3503
- import pc16 from "picocolors";
3504
- var validate_default = defineCommand13({
3067
+ import { defineCommand as defineCommand11 } from "citty";
3068
+ import pc14 from "picocolors";
3069
+ var validate_default = defineCommand11({
3505
3070
  meta: {
3506
3071
  name: "validate",
3507
3072
  description: "Validate a commit message against the configured convention"
@@ -3531,7 +3096,7 @@ var validate_default = defineCommand13({
3531
3096
  }
3532
3097
  const errors = getValidationError(convention);
3533
3098
  for (const line of errors) {
3534
- console.error(pc16.red(` ✗ ${line}`));
3099
+ console.error(pc14.red(` ✗ ${line}`));
3535
3100
  }
3536
3101
  process.exit(1);
3537
3102
  }
@@ -3539,7 +3104,7 @@ var validate_default = defineCommand13({
3539
3104
 
3540
3105
  // src/ui/banner.ts
3541
3106
  import figlet from "figlet";
3542
- import pc17 from "picocolors";
3107
+ import pc15 from "picocolors";
3543
3108
  var LOGO_BIG;
3544
3109
  try {
3545
3110
  LOGO_BIG = figlet.textSync(`Contribute
@@ -3561,14 +3126,14 @@ function getAuthor() {
3561
3126
  }
3562
3127
  function showBanner(variant = "small") {
3563
3128
  const logo = variant === "big" ? LOGO_BIG : LOGO_SMALL;
3564
- console.log(pc17.cyan(`
3129
+ console.log(pc15.cyan(`
3565
3130
  ${logo}`));
3566
- console.log(` ${pc17.dim(`v${getVersion()}`)} ${pc17.dim("—")} ${pc17.dim(`Built by ${getAuthor()}`)}`);
3131
+ console.log(` ${pc15.dim(`v${getVersion()}`)} ${pc15.dim("—")} ${pc15.dim(`Built by ${getAuthor()}`)}`);
3567
3132
  if (variant === "big") {
3568
3133
  console.log();
3569
- console.log(` ${pc17.yellow("Star")} ${pc17.cyan("https://github.com/warengonzaga/contribute-now")}`);
3570
- console.log(` ${pc17.green("Contribute")} ${pc17.cyan("https://github.com/warengonzaga/contribute-now/blob/main/CONTRIBUTING.md")}`);
3571
- console.log(` ${pc17.magenta("Sponsor")} ${pc17.cyan("https://warengonzaga.com/sponsor")}`);
3134
+ console.log(` ${pc15.yellow("Star")} ${pc15.cyan("https://github.com/warengonzaga/contribute-now")}`);
3135
+ console.log(` ${pc15.green("Contribute")} ${pc15.cyan("https://github.com/warengonzaga/contribute-now/blob/main/CONTRIBUTING.md")}`);
3136
+ console.log(` ${pc15.magenta("Sponsor")} ${pc15.cyan("https://warengonzaga.com/sponsor")}`);
3572
3137
  }
3573
3138
  console.log();
3574
3139
  }
@@ -3576,13 +3141,13 @@ ${logo}`));
3576
3141
  // src/index.ts
3577
3142
  var isVersion = process.argv.includes("--version") || process.argv.includes("-v");
3578
3143
  if (!isVersion) {
3579
- const subCommands = ["setup", "sync", "start", "commit", "update", "submit", "clean", "status", "log", "branch", "hook", "validate", "doctor"];
3144
+ const subCommands = ["setup", "sync", "start", "commit", "update", "submit", "clean", "status", "hook", "validate", "doctor"];
3580
3145
  const isHelp = process.argv.includes("--help") || process.argv.includes("-h");
3581
3146
  const hasSubCommand = subCommands.some((cmd) => process.argv.includes(cmd));
3582
3147
  const useBigBanner = isHelp || !hasSubCommand;
3583
3148
  showBanner(useBigBanner ? "big" : "small");
3584
3149
  }
3585
- var main = defineCommand14({
3150
+ var main = defineCommand12({
3586
3151
  meta: {
3587
3152
  name: "contrib",
3588
3153
  version: getVersion(),
@@ -3602,10 +3167,8 @@ var main = defineCommand14({
3602
3167
  commit: commit_default,
3603
3168
  update: update_default,
3604
3169
  submit: submit_default,
3605
- branch: branch_default,
3606
3170
  clean: clean_default,
3607
3171
  status: status_default,
3608
- log: log_default,
3609
3172
  hook: hook_default,
3610
3173
  validate: validate_default,
3611
3174
  doctor: doctor_default