contribute-now 0.6.2-dev.967437a → 0.6.2-dev.9785651

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 (3) hide show
  1. package/README.md +3 -8
  2. package/dist/index.js +579 -547
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7059,9 +7059,6 @@ function configExists(cwd = process.cwd()) {
7059
7059
  var VALID_WORKFLOWS = ["clean-flow", "github-flow", "git-flow"];
7060
7060
  var VALID_ROLES = ["maintainer", "contributor"];
7061
7061
  var VALID_CONVENTIONS = ["conventional", "clean-commit", "none"];
7062
- function isAIEnabled(config, cliNoAI = false) {
7063
- return config.aiEnabled !== false && !cliNoAI;
7064
- }
7065
7062
  function readConfig(cwd = process.cwd()) {
7066
7063
  const path = getConfigPath(cwd);
7067
7064
  if (!existsSync(path))
@@ -7104,10 +7101,7 @@ function readConfig(cwd = process.cwd()) {
7104
7101
  console.error("Invalid .contributerc.json: all branchPrefixes must be non-empty strings.");
7105
7102
  return null;
7106
7103
  }
7107
- return {
7108
- ...parsed,
7109
- aiEnabled: parsed.aiEnabled !== false
7110
- };
7104
+ return parsed;
7111
7105
  } catch {
7112
7106
  return null;
7113
7107
  }
@@ -7155,8 +7149,7 @@ function getDefaultConfig() {
7155
7149
  upstream: "upstream",
7156
7150
  origin: "origin",
7157
7151
  branchPrefixes: ["feature", "fix", "docs", "chore", "test", "refactor"],
7158
- commitConvention: "clean-commit",
7159
- aiEnabled: true
7152
+ commitConvention: "clean-commit"
7160
7153
  };
7161
7154
  }
7162
7155
 
@@ -8763,7 +8756,6 @@ var LogEngine = {
8763
8756
 
8764
8757
  // src/utils/logger.ts
8765
8758
  var import_picocolors = __toESM(require_picocolors(), 1);
8766
- var PROJECT_DISPLAY_NAME = "Contribute Now";
8767
8759
  LogEngine.configure({
8768
8760
  mode: LogMode.INFO,
8769
8761
  format: {
@@ -8784,10 +8776,9 @@ function warn(msg, emoji = "⚠️") {
8784
8776
  function info(msg, emoji = "ℹ️") {
8785
8777
  LogEngine.info(msg, undefined, { emoji });
8786
8778
  }
8787
- function projectHeading(command, emoji) {
8788
- const prefix = emoji ? `${import_picocolors.default.bold(emoji)} ` : "";
8779
+ function heading(msg) {
8789
8780
  console.log(`
8790
- ${prefix}${import_picocolors.default.bold(import_picocolors.default.cyan(PROJECT_DISPLAY_NAME))} ${import_picocolors.default.dim("—")} ${import_picocolors.default.bold(command)}`);
8781
+ ${import_picocolors.default.bold(msg)}`);
8791
8782
  }
8792
8783
 
8793
8784
  // src/utils/workflow.ts
@@ -8880,7 +8871,7 @@ var branch_default = defineCommand({
8880
8871
  const currentBranch = await getCurrentBranch();
8881
8872
  const showRemoteOnly = args.remote;
8882
8873
  const showAll = args.all;
8883
- projectHeading("branch", "\uD83C\uDF3F");
8874
+ heading("\uD83C\uDF3F branches");
8884
8875
  console.log();
8885
8876
  if (!showRemoteOnly) {
8886
8877
  const localBranches = await getLocalBranches();
@@ -8990,9 +8981,6 @@ function groupByRemote(branches) {
8990
8981
  }
8991
8982
 
8992
8983
  // src/commands/clean.ts
8993
- var import_picocolors8 = __toESM(require_picocolors(), 1);
8994
-
8995
- // src/utils/branchPrompt.ts
8996
8984
  var import_picocolors7 = __toESM(require_picocolors(), 1);
8997
8985
 
8998
8986
  // src/utils/branch.ts
@@ -10266,7 +10254,7 @@ ${truncated}
10266
10254
  return result.length > maxTotalChars ? `${result.slice(0, maxTotalChars - 15)}
10267
10255
  ...(truncated)` : result;
10268
10256
  }
10269
- async function checkCopilotAvailable2() {
10257
+ async function checkCopilotAvailable() {
10270
10258
  try {
10271
10259
  const client = await getManagedClient();
10272
10260
  try {
@@ -10613,154 +10601,6 @@ ${diffContent}`;
10613
10601
  }
10614
10602
  }
10615
10603
 
10616
- // src/utils/spinner.ts
10617
- var import_picocolors6 = __toESM(require_picocolors(), 1);
10618
- var FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
10619
- function createSpinner(text) {
10620
- let frameIdx = 0;
10621
- let currentText = text;
10622
- let stopped = false;
10623
- const clearLine = () => {
10624
- process.stderr.write("\r\x1B[K");
10625
- };
10626
- const render = () => {
10627
- if (stopped)
10628
- return;
10629
- const frame = import_picocolors6.default.cyan(FRAMES[frameIdx % FRAMES.length]);
10630
- clearLine();
10631
- process.stderr.write(`${frame} ${currentText}`);
10632
- frameIdx++;
10633
- };
10634
- const timer = setInterval(render, 80);
10635
- render();
10636
- const stop = () => {
10637
- if (stopped)
10638
- return;
10639
- stopped = true;
10640
- clearInterval(timer);
10641
- clearLine();
10642
- };
10643
- return {
10644
- update(newText) {
10645
- currentText = newText;
10646
- },
10647
- success(msg) {
10648
- stop();
10649
- process.stderr.write(`${import_picocolors6.default.green("✔")} ${msg}
10650
- `);
10651
- },
10652
- fail(msg) {
10653
- stop();
10654
- process.stderr.write(`${import_picocolors6.default.red("✖")} ${msg}
10655
- `);
10656
- },
10657
- stop() {
10658
- stop();
10659
- }
10660
- };
10661
- }
10662
-
10663
- // src/utils/branchPrompt.ts
10664
- async function promptForBranchName(options) {
10665
- const promptMessage = options.promptMessage ?? "What are you going to work on?";
10666
- let branchInput = options.initialValue?.trim() ?? "";
10667
- while (!branchInput) {
10668
- branchInput = (await inputPrompt(promptMessage)).trim();
10669
- if (branchInput)
10670
- break;
10671
- warn("A branch name or description is required.");
10672
- const action = await selectPrompt("What would you like to do?", ["Try again", "Cancel"]);
10673
- if (action === "Cancel")
10674
- return null;
10675
- }
10676
- let branchName = branchInput;
10677
- const useAI = options.useAI !== false && looksLikeNaturalLanguage(branchInput);
10678
- if (useAI) {
10679
- const copilotError = await checkCopilotAvailable2();
10680
- if (copilotError) {
10681
- warn(`AI unavailable: ${copilotError}`);
10682
- } else {
10683
- while (true) {
10684
- const spinner = createSpinner("Generating branch name suggestion...");
10685
- const suggested = await suggestBranchName(branchInput, options.model);
10686
- if (suggested) {
10687
- spinner.success("Branch name suggestion ready.");
10688
- console.log(`
10689
- ${import_picocolors7.default.dim("AI suggestion:")} ${import_picocolors7.default.bold(import_picocolors7.default.cyan(suggested))}`);
10690
- const action2 = await selectPrompt("What would you like to do with this branch name?", [
10691
- "Use this suggestion",
10692
- "Try again with AI",
10693
- "Enter branch name manually",
10694
- "Use my original description",
10695
- "Cancel"
10696
- ]);
10697
- if (action2 === "Use this suggestion") {
10698
- branchName = suggested;
10699
- break;
10700
- }
10701
- if (action2 === "Try again with AI") {
10702
- continue;
10703
- }
10704
- if (action2 === "Enter branch name manually") {
10705
- branchName = (await inputPrompt("Enter branch name", branchInput)).trim();
10706
- break;
10707
- }
10708
- if (action2 === "Use my original description") {
10709
- branchName = branchInput;
10710
- break;
10711
- }
10712
- return null;
10713
- }
10714
- spinner.fail("AI did not return a branch name suggestion.");
10715
- const action = await selectPrompt("AI could not generate a branch name. What would you like to do?", [
10716
- "Try again with AI",
10717
- "Enter branch name manually",
10718
- "Use my original description",
10719
- "Cancel"
10720
- ]);
10721
- if (action === "Try again with AI") {
10722
- continue;
10723
- }
10724
- if (action === "Enter branch name manually") {
10725
- branchName = (await inputPrompt("Enter branch name", branchInput)).trim();
10726
- break;
10727
- }
10728
- if (action === "Use my original description") {
10729
- branchName = branchInput;
10730
- break;
10731
- }
10732
- return null;
10733
- }
10734
- }
10735
- }
10736
- while (true) {
10737
- if (!branchName) {
10738
- branchName = (await inputPrompt("Enter branch name", branchInput)).trim();
10739
- if (!branchName) {
10740
- const action = await selectPrompt("What would you like to do?", ["Try again", "Cancel"]);
10741
- if (action === "Cancel")
10742
- return null;
10743
- continue;
10744
- }
10745
- }
10746
- if (!hasPrefix(branchName, options.branchPrefixes)) {
10747
- const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors7.default.bold(branchName)}:`, options.branchPrefixes);
10748
- branchName = formatBranchName(prefix, branchName);
10749
- }
10750
- if (!isValidBranchName(branchName)) {
10751
- warn("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
10752
- branchName = (await inputPrompt("Enter branch name", branchName)).trim();
10753
- continue;
10754
- }
10755
- if (await branchExists(branchName)) {
10756
- warn(`Branch ${import_picocolors7.default.bold(branchName)} already exists. Choose a different name.`);
10757
- branchName = (await inputPrompt("Enter branch name", branchName)).trim();
10758
- continue;
10759
- }
10760
- return branchName;
10761
- }
10762
- }
10763
-
10764
10604
  // src/utils/gh.ts
10765
10605
  import { execFile as execFileCb2 } from "node:child_process";
10766
10606
  function run2(args) {
@@ -10899,6 +10739,53 @@ async function getMergedPRForBranch(headBranch) {
10899
10739
  }
10900
10740
  }
10901
10741
 
10742
+ // src/utils/spinner.ts
10743
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
10744
+ var FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
10745
+ function createSpinner(text) {
10746
+ let frameIdx = 0;
10747
+ let currentText = text;
10748
+ let stopped = false;
10749
+ const clearLine = () => {
10750
+ process.stderr.write("\r\x1B[K");
10751
+ };
10752
+ const render = () => {
10753
+ if (stopped)
10754
+ return;
10755
+ const frame = import_picocolors6.default.cyan(FRAMES[frameIdx % FRAMES.length]);
10756
+ clearLine();
10757
+ process.stderr.write(`${frame} ${currentText}`);
10758
+ frameIdx++;
10759
+ };
10760
+ const timer = setInterval(render, 80);
10761
+ render();
10762
+ const stop = () => {
10763
+ if (stopped)
10764
+ return;
10765
+ stopped = true;
10766
+ clearInterval(timer);
10767
+ clearLine();
10768
+ };
10769
+ return {
10770
+ update(newText) {
10771
+ currentText = newText;
10772
+ },
10773
+ success(msg) {
10774
+ stop();
10775
+ process.stderr.write(`${import_picocolors6.default.green("✔")} ${msg}
10776
+ `);
10777
+ },
10778
+ fail(msg) {
10779
+ stop();
10780
+ process.stderr.write(`${import_picocolors6.default.red("✖")} ${msg}
10781
+ `);
10782
+ },
10783
+ stop() {
10784
+ stop();
10785
+ }
10786
+ };
10787
+ }
10788
+
10902
10789
  // src/commands/clean.ts
10903
10790
  async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
10904
10791
  if (!config)
@@ -10911,22 +10798,44 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
10911
10798
  warn("You have uncommitted changes in your working tree.");
10912
10799
  }
10913
10800
  if (localWork.unpushedCommits > 0) {
10914
- warn(`You have ${import_picocolors8.default.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not pushed.`);
10801
+ warn(`You have ${import_picocolors7.default.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not pushed.`);
10915
10802
  }
10916
10803
  const SAVE_NEW_BRANCH = "Save changes to a new branch";
10917
10804
  const DISCARD = "Discard all changes and clean up";
10918
10805
  const CANCEL = "Skip this branch";
10919
- const action = await selectPrompt(`${import_picocolors8.default.bold(currentBranch)} has local changes. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
10806
+ const action = await selectPrompt(`${import_picocolors7.default.bold(currentBranch)} has local changes. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
10920
10807
  if (action === CANCEL)
10921
10808
  return "skipped";
10922
10809
  if (action === SAVE_NEW_BRANCH) {
10923
10810
  if (!config)
10924
10811
  return "skipped";
10925
- const newBranchName = await promptForBranchName({
10926
- branchPrefixes: config.branchPrefixes,
10927
- useAI: isAIEnabled(config)
10928
- });
10929
- if (!newBranchName) {
10812
+ info(import_picocolors7.default.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
10813
+ const description = await inputPrompt("What are you going to work on?");
10814
+ let newBranchName = description;
10815
+ if (looksLikeNaturalLanguage(description)) {
10816
+ const spinner = createSpinner("Generating branch name suggestion...");
10817
+ const suggested = await suggestBranchName(description);
10818
+ if (suggested) {
10819
+ spinner.success("Branch name suggestion ready.");
10820
+ console.log(`
10821
+ ${import_picocolors7.default.dim("AI suggestion:")} ${import_picocolors7.default.bold(import_picocolors7.default.cyan(suggested))}`);
10822
+ const accepted = await confirmPrompt(`Use ${import_picocolors7.default.bold(suggested)} as your branch name?`);
10823
+ newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
10824
+ } else {
10825
+ spinner.fail("AI did not return a suggestion.");
10826
+ newBranchName = await inputPrompt("Enter branch name", description);
10827
+ }
10828
+ }
10829
+ if (!hasPrefix(newBranchName, config.branchPrefixes)) {
10830
+ const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors7.default.bold(newBranchName)}:`, config.branchPrefixes);
10831
+ newBranchName = formatBranchName(prefix, newBranchName);
10832
+ }
10833
+ if (!isValidBranchName(newBranchName)) {
10834
+ error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
10835
+ return "skipped";
10836
+ }
10837
+ if (await branchExists(newBranchName)) {
10838
+ error(`Branch ${import_picocolors7.default.bold(newBranchName)} already exists. Choose a different name.`);
10930
10839
  return "skipped";
10931
10840
  }
10932
10841
  const renameResult = await renameBranch(currentBranch, newBranchName);
@@ -10934,7 +10843,7 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
10934
10843
  error(`Failed to rename branch: ${renameResult.stderr}`);
10935
10844
  return "skipped";
10936
10845
  }
10937
- success(`Renamed ${import_picocolors8.default.bold(currentBranch)} → ${import_picocolors8.default.bold(newBranchName)}`);
10846
+ success(`Renamed ${import_picocolors7.default.bold(currentBranch)} → ${import_picocolors7.default.bold(newBranchName)}`);
10938
10847
  const syncSource2 = getSyncSource(config);
10939
10848
  await fetchRemote(syncSource2.remote);
10940
10849
  const savedUpstreamRef = await getUpstreamRef();
@@ -10942,10 +10851,10 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
10942
10851
  if (rebaseResult.exitCode !== 0) {
10943
10852
  await rebaseAbort();
10944
10853
  warn("Rebase had conflicts — aborted to keep the repo in a clean state.");
10945
- info(`Your work is saved on ${import_picocolors8.default.bold(newBranchName)}. After cleanup, rebase manually:`, "");
10946
- info(` ${import_picocolors8.default.bold(`git checkout ${newBranchName} && git rebase ${syncSource2.ref}`)}`, "");
10854
+ info(`Your work is saved on ${import_picocolors7.default.bold(newBranchName)}. After cleanup, rebase manually:`, "");
10855
+ info(` ${import_picocolors7.default.bold(`git checkout ${newBranchName} && git rebase ${syncSource2.ref}`)}`, "");
10947
10856
  } else {
10948
- success(`Rebased ${import_picocolors8.default.bold(newBranchName)} onto ${import_picocolors8.default.bold(syncSource2.ref)}.`);
10857
+ success(`Rebased ${import_picocolors7.default.bold(newBranchName)} onto ${import_picocolors7.default.bold(syncSource2.ref)}.`);
10949
10858
  }
10950
10859
  const coResult2 = await checkoutBranch(baseBranch);
10951
10860
  if (coResult2.exitCode !== 0) {
@@ -10953,12 +10862,12 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
10953
10862
  return "saved";
10954
10863
  }
10955
10864
  await updateLocalBranch(baseBranch, syncSource2.ref);
10956
- success(`Synced ${import_picocolors8.default.bold(baseBranch)} with ${import_picocolors8.default.bold(syncSource2.ref)}.`);
10865
+ success(`Synced ${import_picocolors7.default.bold(baseBranch)} with ${import_picocolors7.default.bold(syncSource2.ref)}.`);
10957
10866
  return "saved";
10958
10867
  }
10959
10868
  }
10960
10869
  const syncSource = getSyncSource(config);
10961
- info(`Switching to ${import_picocolors8.default.bold(baseBranch)} and syncing...`);
10870
+ info(`Switching to ${import_picocolors7.default.bold(baseBranch)} and syncing...`);
10962
10871
  await fetchRemote(syncSource.remote);
10963
10872
  await resetHard("HEAD");
10964
10873
  const coResult = await checkoutBranch(baseBranch);
@@ -10967,7 +10876,7 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
10967
10876
  return "skipped";
10968
10877
  }
10969
10878
  await updateLocalBranch(baseBranch, syncSource.ref);
10970
- success(`Synced ${import_picocolors8.default.bold(baseBranch)} with ${import_picocolors8.default.bold(syncSource.ref)}.`);
10879
+ success(`Synced ${import_picocolors7.default.bold(baseBranch)} with ${import_picocolors7.default.bold(syncSource.ref)}.`);
10971
10880
  return "switched";
10972
10881
  }
10973
10882
  var clean_default = defineCommand({
@@ -10997,7 +10906,7 @@ var clean_default = defineCommand({
10997
10906
  const { origin } = config;
10998
10907
  const baseBranch = getBaseBranch(config);
10999
10908
  let currentBranch = await getCurrentBranch();
11000
- projectHeading("clean", "\uD83E\uDDF9");
10909
+ heading("\uD83E\uDDF9 contrib clean");
11001
10910
  info(`Pruning ${origin} remote refs...`);
11002
10911
  const pruneResult = await pruneRemote(origin);
11003
10912
  if (pruneResult.exitCode === 0) {
@@ -11017,21 +10926,21 @@ var clean_default = defineCommand({
11017
10926
  if (ghInstalled && ghAuthed) {
11018
10927
  const mergedPR = await getMergedPRForBranch(currentBranch);
11019
10928
  if (mergedPR) {
11020
- warn(`PR #${mergedPR.number} (${import_picocolors8.default.bold(mergedPR.title)}) has already been merged.`);
11021
- info(`Link: ${import_picocolors8.default.underline(mergedPR.url)}`, "");
10929
+ warn(`PR #${mergedPR.number} (${import_picocolors7.default.bold(mergedPR.title)}) has already been merged.`);
10930
+ info(`Link: ${import_picocolors7.default.underline(mergedPR.url)}`, "");
11022
10931
  goneCandidates.push(currentBranch);
11023
10932
  }
11024
10933
  }
11025
10934
  }
11026
10935
  if (mergedCandidates.length > 0) {
11027
10936
  console.log(`
11028
- ${import_picocolors8.default.bold("Merged branches to delete:")}`);
10937
+ ${import_picocolors7.default.bold("Merged branches to delete:")}`);
11029
10938
  for (const b2 of mergedCandidates) {
11030
- const marker = b2 === currentBranch ? import_picocolors8.default.yellow(" (current)") : "";
11031
- console.log(` ${import_picocolors8.default.dim("•")} ${b2}${marker}`);
10939
+ const marker = b2 === currentBranch ? import_picocolors7.default.yellow(" (current)") : "";
10940
+ console.log(` ${import_picocolors7.default.dim("•")} ${b2}${marker}`);
11032
10941
  }
11033
10942
  console.log();
11034
- const ok = args.yes || await confirmPrompt(`Delete ${import_picocolors8.default.bold(String(mergedCandidates.length))} merged branch${mergedCandidates.length !== 1 ? "es" : ""}?`);
10943
+ const ok = args.yes || await confirmPrompt(`Delete ${import_picocolors7.default.bold(String(mergedCandidates.length))} merged branch${mergedCandidates.length !== 1 ? "es" : ""}?`);
11035
10944
  if (ok) {
11036
10945
  for (const branch of mergedCandidates) {
11037
10946
  if (branch === currentBranch) {
@@ -11048,7 +10957,7 @@ ${import_picocolors8.default.bold("Merged branches to delete:")}`);
11048
10957
  }
11049
10958
  const result = await deleteBranch(branch);
11050
10959
  if (result.exitCode === 0) {
11051
- success(` Deleted ${import_picocolors8.default.bold(branch)}`);
10960
+ success(` Deleted ${import_picocolors7.default.bold(branch)}`);
11052
10961
  } else {
11053
10962
  warn(` Failed to delete ${branch}: ${result.stderr.trim()}`);
11054
10963
  }
@@ -11059,13 +10968,13 @@ ${import_picocolors8.default.bold("Merged branches to delete:")}`);
11059
10968
  }
11060
10969
  if (goneCandidates.length > 0) {
11061
10970
  console.log(`
11062
- ${import_picocolors8.default.bold("Stale branches (remote deleted, likely squash-merged):")}`);
10971
+ ${import_picocolors7.default.bold("Stale branches (remote deleted, likely squash-merged):")}`);
11063
10972
  for (const b2 of goneCandidates) {
11064
- const marker = b2 === currentBranch ? import_picocolors8.default.yellow(" (current)") : "";
11065
- console.log(` ${import_picocolors8.default.dim("•")} ${b2}${marker}`);
10973
+ const marker = b2 === currentBranch ? import_picocolors7.default.yellow(" (current)") : "";
10974
+ console.log(` ${import_picocolors7.default.dim("•")} ${b2}${marker}`);
11066
10975
  }
11067
10976
  console.log();
11068
- const ok = args.yes || await confirmPrompt(`Delete ${import_picocolors8.default.bold(String(goneCandidates.length))} stale branch${goneCandidates.length !== 1 ? "es" : ""}?`);
10977
+ const ok = args.yes || await confirmPrompt(`Delete ${import_picocolors7.default.bold(String(goneCandidates.length))} stale branch${goneCandidates.length !== 1 ? "es" : ""}?`);
11069
10978
  if (ok) {
11070
10979
  for (const branch of goneCandidates) {
11071
10980
  if (branch === currentBranch) {
@@ -11082,7 +10991,7 @@ ${import_picocolors8.default.bold("Stale branches (remote deleted, likely squash
11082
10991
  }
11083
10992
  const result = await forceDeleteBranch(branch);
11084
10993
  if (result.exitCode === 0) {
11085
- success(` Deleted ${import_picocolors8.default.bold(branch)}`);
10994
+ success(` Deleted ${import_picocolors7.default.bold(branch)}`);
11086
10995
  } else {
11087
10996
  warn(` Failed to delete ${branch}: ${result.stderr.trim()}`);
11088
10997
  }
@@ -11097,13 +11006,13 @@ ${import_picocolors8.default.bold("Stale branches (remote deleted, likely squash
11097
11006
  const finalBranch = await getCurrentBranch();
11098
11007
  if (finalBranch && protectedBranches.has(finalBranch)) {
11099
11008
  console.log();
11100
- info(`You're on ${import_picocolors8.default.bold(finalBranch)}. Run ${import_picocolors8.default.bold("contrib start")} to begin a new feature.`);
11009
+ info(`You're on ${import_picocolors7.default.bold(finalBranch)}. Run ${import_picocolors7.default.bold("contrib start")} to begin a new feature.`);
11101
11010
  }
11102
11011
  }
11103
11012
  });
11104
11013
 
11105
11014
  // src/commands/commit.ts
11106
- var import_picocolors9 = __toESM(require_picocolors(), 1);
11015
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
11107
11016
 
11108
11017
  // src/utils/convention.ts
11109
11018
  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;
@@ -11189,13 +11098,8 @@ var commit_default = defineCommand({
11189
11098
  error("No .contributerc.json found. Run `contrib setup` first.");
11190
11099
  process.exit(1);
11191
11100
  }
11192
- projectHeading("commit", "\uD83D\uDCBE");
11193
- const aiEnabled = isAIEnabled(config, args["no-ai"]);
11101
+ heading("\uD83D\uDCBE contrib commit");
11194
11102
  if (args.group) {
11195
- if (!aiEnabled) {
11196
- error("AI group commit is unavailable because AI is disabled. Re-run without --group or enable AI in .contributerc.json.");
11197
- process.exit(1);
11198
- }
11199
11103
  await runGroupCommit(args.model, config);
11200
11104
  return;
11201
11105
  }
@@ -11207,9 +11111,9 @@ var commit_default = defineCommand({
11207
11111
  process.exit(1);
11208
11112
  }
11209
11113
  console.log(`
11210
- ${import_picocolors9.default.bold("Changed files:")}`);
11114
+ ${import_picocolors8.default.bold("Changed files:")}`);
11211
11115
  for (const f3 of changedFiles) {
11212
- console.log(` ${import_picocolors9.default.dim("•")} ${f3}`);
11116
+ console.log(` ${import_picocolors8.default.dim("•")} ${f3}`);
11213
11117
  }
11214
11118
  const stageAction = await selectPrompt("No staged changes. How would you like to stage?", [
11215
11119
  "Stage all changes",
@@ -11251,8 +11155,8 @@ ${import_picocolors9.default.bold("Changed files:")}`);
11251
11155
  const dirs = new Set(stagedFiles.map((f3) => f3.split("/")[0]));
11252
11156
  if (dirs.size > 1) {
11253
11157
  console.log();
11254
- warn(`You're staging ${import_picocolors9.default.bold(String(stagedFiles.length))} files across ${import_picocolors9.default.bold(String(dirs.size))} directories in a single commit.`);
11255
- info(import_picocolors9.default.dim("Large commits mixing different topics make history harder to read and bisect. " + "For cleaner history, consider splitting into atomic commits."));
11158
+ warn(`You're staging ${import_picocolors8.default.bold(String(stagedFiles.length))} files across ${import_picocolors8.default.bold(String(dirs.size))} directories in a single commit.`);
11159
+ info(import_picocolors8.default.dim("Large commits mixing different topics make history harder to read and bisect. " + "For cleaner history, consider splitting into atomic commits."));
11256
11160
  const choice = await selectPrompt("How would you like to proceed?", [
11257
11161
  "Continue as single commit",
11258
11162
  "Switch to group mode (AI splits into atomic commits)",
@@ -11268,9 +11172,9 @@ ${import_picocolors9.default.bold("Changed files:")}`);
11268
11172
  }
11269
11173
  }
11270
11174
  let commitMessage = null;
11271
- const useAI = aiEnabled;
11175
+ const useAI = !args["no-ai"];
11272
11176
  if (useAI) {
11273
- const [copilotError, diff] = await Promise.all([checkCopilotAvailable2(), getStagedDiff()]);
11177
+ const [copilotError, diff] = await Promise.all([checkCopilotAvailable(), getStagedDiff()]);
11274
11178
  if (copilotError) {
11275
11179
  warn(`AI unavailable: ${copilotError}`);
11276
11180
  warn("Falling back to manual commit message entry.");
@@ -11281,7 +11185,7 @@ ${import_picocolors9.default.bold("Changed files:")}`);
11281
11185
  if (commitMessage) {
11282
11186
  spinner.success("AI commit message generated.");
11283
11187
  console.log(`
11284
- ${import_picocolors9.default.dim("AI suggestion:")} ${import_picocolors9.default.bold(import_picocolors9.default.cyan(commitMessage))}`);
11188
+ ${import_picocolors8.default.dim("AI suggestion:")} ${import_picocolors8.default.bold(import_picocolors8.default.cyan(commitMessage))}`);
11285
11189
  } else {
11286
11190
  spinner.fail("AI did not return a commit message.");
11287
11191
  warn("Falling back to manual entry.");
@@ -11307,7 +11211,7 @@ ${import_picocolors9.default.bold("Changed files:")}`);
11307
11211
  if (regen) {
11308
11212
  spinner.success("Commit message regenerated.");
11309
11213
  console.log(`
11310
- ${import_picocolors9.default.dim("AI suggestion:")} ${import_picocolors9.default.bold(import_picocolors9.default.cyan(regen))}`);
11214
+ ${import_picocolors8.default.dim("AI suggestion:")} ${import_picocolors8.default.bold(import_picocolors8.default.cyan(regen))}`);
11311
11215
  const ok = await confirmPrompt("Use this message?");
11312
11216
  finalMessage = ok ? regen : await inputPrompt("Enter commit message manually");
11313
11217
  } else {
@@ -11322,7 +11226,7 @@ ${import_picocolors9.default.bold("Changed files:")}`);
11322
11226
  if (convention2 !== "none") {
11323
11227
  console.log();
11324
11228
  for (const hint of CONVENTION_FORMAT_HINTS[convention2]) {
11325
- console.log(import_picocolors9.default.dim(hint));
11229
+ console.log(import_picocolors8.default.dim(hint));
11326
11230
  }
11327
11231
  console.log();
11328
11232
  }
@@ -11346,7 +11250,7 @@ ${import_picocolors9.default.bold("Changed files:")}`);
11346
11250
  error(`Failed to commit: ${result.stderr}`);
11347
11251
  process.exit(1);
11348
11252
  }
11349
- success(`Committed: ${import_picocolors9.default.bold(finalMessage)}`);
11253
+ success(`Committed: ${import_picocolors8.default.bold(finalMessage)}`);
11350
11254
  }
11351
11255
  });
11352
11256
  function getFallbackGroupMessage(convention) {
@@ -11360,7 +11264,7 @@ function getFallbackGroupMessage(convention) {
11360
11264
  }
11361
11265
  async function runGroupCommit(model, config) {
11362
11266
  const [copilotError, changedFiles] = await Promise.all([
11363
- checkCopilotAvailable2(),
11267
+ checkCopilotAvailable(),
11364
11268
  getChangedFiles()
11365
11269
  ]);
11366
11270
  if (copilotError) {
@@ -11372,9 +11276,9 @@ async function runGroupCommit(model, config) {
11372
11276
  process.exit(1);
11373
11277
  }
11374
11278
  console.log(`
11375
- ${import_picocolors9.default.bold("Changed files:")}`);
11279
+ ${import_picocolors8.default.bold("Changed files:")}`);
11376
11280
  for (const f3 of changedFiles) {
11377
- console.log(` ${import_picocolors9.default.dim("•")} ${f3}`);
11281
+ console.log(` ${import_picocolors8.default.dim("•")} ${f3}`);
11378
11282
  }
11379
11283
  const spinner = createSpinner(changedFiles.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD ? `Asking AI to group ${changedFiles.length} file(s) into logical commits (using optimized batching)...` : `Asking AI to group ${changedFiles.length} file(s) into logical commits...`);
11380
11284
  const diffs = await getFullDiffForFiles(changedFiles);
@@ -11422,13 +11326,13 @@ ${import_picocolors9.default.bold("Changed files:")}`);
11422
11326
  let commitAll = false;
11423
11327
  while (!proceedToCommit) {
11424
11328
  console.log(`
11425
- ${import_picocolors9.default.bold(`AI suggested ${validGroups.length} commit group(s):`)}
11329
+ ${import_picocolors8.default.bold(`AI suggested ${validGroups.length} commit group(s):`)}
11426
11330
  `);
11427
11331
  for (let i2 = 0;i2 < validGroups.length; i2++) {
11428
11332
  const g3 = validGroups[i2];
11429
- console.log(` ${import_picocolors9.default.cyan(`Group ${i2 + 1}:`)} ${import_picocolors9.default.bold(g3.message)}`);
11333
+ console.log(` ${import_picocolors8.default.cyan(`Group ${i2 + 1}:`)} ${import_picocolors8.default.bold(g3.message)}`);
11430
11334
  for (const f3 of g3.files) {
11431
- console.log(` ${import_picocolors9.default.dim("•")} ${f3}`);
11335
+ console.log(` ${import_picocolors8.default.dim("•")} ${f3}`);
11432
11336
  }
11433
11337
  console.log();
11434
11338
  }
@@ -11482,16 +11386,16 @@ ${import_picocolors9.default.bold(`AI suggested ${validGroups.length} commit gro
11482
11386
  continue;
11483
11387
  }
11484
11388
  committed++;
11485
- success(`Committed group ${i2 + 1}: ${import_picocolors9.default.bold(group.message)}`);
11389
+ success(`Committed group ${i2 + 1}: ${import_picocolors8.default.bold(group.message)}`);
11486
11390
  }
11487
11391
  } else {
11488
11392
  for (let i2 = 0;i2 < validGroups.length; i2++) {
11489
11393
  const group = validGroups[i2];
11490
- console.log(import_picocolors9.default.bold(`
11394
+ console.log(import_picocolors8.default.bold(`
11491
11395
  ── Group ${i2 + 1}/${validGroups.length} ──`));
11492
- console.log(` ${import_picocolors9.default.cyan(group.message)}`);
11396
+ console.log(` ${import_picocolors8.default.cyan(group.message)}`);
11493
11397
  for (const f3 of group.files) {
11494
- console.log(` ${import_picocolors9.default.dim("•")} ${f3}`);
11398
+ console.log(` ${import_picocolors8.default.dim("•")} ${f3}`);
11495
11399
  }
11496
11400
  let message = group.message;
11497
11401
  let actionDone = false;
@@ -11513,7 +11417,7 @@ ${import_picocolors9.default.bold(`AI suggested ${validGroups.length} commit gro
11513
11417
  if (newMsg) {
11514
11418
  message = newMsg;
11515
11419
  group.message = newMsg;
11516
- regenSpinner.success(`New message: ${import_picocolors9.default.bold(message)}`);
11420
+ regenSpinner.success(`New message: ${import_picocolors8.default.bold(message)}`);
11517
11421
  } else {
11518
11422
  regenSpinner.fail("AI could not generate a new message. Keeping current one.");
11519
11423
  }
@@ -11564,7 +11468,7 @@ ${import_picocolors9.default.bold(`AI suggested ${validGroups.length} commit gro
11564
11468
  continue;
11565
11469
  }
11566
11470
  committed++;
11567
- success(`Committed group ${i2 + 1}: ${import_picocolors9.default.bold(message)}`);
11471
+ success(`Committed group ${i2 + 1}: ${import_picocolors8.default.bold(message)}`);
11568
11472
  actionDone = true;
11569
11473
  }
11570
11474
  }
@@ -11580,11 +11484,11 @@ ${import_picocolors9.default.bold(`AI suggested ${validGroups.length} commit gro
11580
11484
 
11581
11485
  // src/commands/doctor.ts
11582
11486
  import { execFile as execFileCb3 } from "node:child_process";
11583
- var import_picocolors10 = __toESM(require_picocolors(), 1);
11487
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
11584
11488
  // package.json
11585
11489
  var package_default = {
11586
11490
  name: "contribute-now",
11587
- version: "0.6.2-dev.967437a",
11491
+ version: "0.6.2-dev.9785651",
11588
11492
  description: "Developer CLI that automates git workflows — branching, syncing, committing, and PRs — with multi-workflow and commit convention support.",
11589
11493
  type: "module",
11590
11494
  bin: {
@@ -11675,16 +11579,16 @@ async function getRepoInfoFromRemote(remote = "origin") {
11675
11579
  }
11676
11580
 
11677
11581
  // src/commands/doctor.ts
11678
- var PASS = ` ${import_picocolors10.default.green("✔")} `;
11679
- var FAIL = ` ${import_picocolors10.default.red("✗")} `;
11680
- var WARN = ` ${import_picocolors10.default.yellow("⚠")} `;
11582
+ var PASS = ` ${import_picocolors9.default.green("✔")} `;
11583
+ var FAIL = ` ${import_picocolors9.default.red("✗")} `;
11584
+ var WARN = ` ${import_picocolors9.default.yellow("⚠")} `;
11681
11585
  function printReport(report) {
11682
11586
  for (const section of report.sections) {
11683
11587
  console.log(`
11684
- ${import_picocolors10.default.bold(import_picocolors10.default.underline(section.title))}`);
11588
+ ${import_picocolors9.default.bold(import_picocolors9.default.underline(section.title))}`);
11685
11589
  for (const check of section.checks) {
11686
11590
  const prefix = check.ok ? check.warning ? WARN : PASS : FAIL;
11687
- const text = check.detail ? `${check.label} ${import_picocolors10.default.dim(`— ${check.detail}`)}` : check.label;
11591
+ const text = check.detail ? `${check.label} ${import_picocolors9.default.dim(`— ${check.detail}`)}` : check.label;
11688
11592
  console.log(`${prefix}${text}`);
11689
11593
  }
11690
11594
  }
@@ -11947,20 +11851,20 @@ var doctor_default = defineCommand({
11947
11851
  console.log(toJson(report));
11948
11852
  return;
11949
11853
  }
11950
- projectHeading("doctor", "\uD83E\uDE7A");
11854
+ heading("\uD83E\uDE7A contribute-now doctor");
11951
11855
  printReport(report);
11952
11856
  const total = report.sections.flatMap((s2) => s2.checks);
11953
11857
  const failures = total.filter((c3) => !c3.ok);
11954
11858
  const warnings = total.filter((c3) => c3.ok && c3.warning);
11955
11859
  if (failures.length === 0 && warnings.length === 0) {
11956
- console.log(` ${import_picocolors10.default.green("All checks passed!")} No issues detected.
11860
+ console.log(` ${import_picocolors9.default.green("All checks passed!")} No issues detected.
11957
11861
  `);
11958
11862
  } else {
11959
11863
  if (failures.length > 0) {
11960
- console.log(` ${import_picocolors10.default.red(`${failures.length} issue${failures.length !== 1 ? "s" : ""} found.`)}`);
11864
+ console.log(` ${import_picocolors9.default.red(`${failures.length} issue${failures.length !== 1 ? "s" : ""} found.`)}`);
11961
11865
  }
11962
11866
  if (warnings.length > 0) {
11963
- console.log(` ${import_picocolors10.default.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}.`)}`);
11867
+ console.log(` ${import_picocolors9.default.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}.`)}`);
11964
11868
  }
11965
11869
  console.log();
11966
11870
  }
@@ -11970,7 +11874,7 @@ var doctor_default = defineCommand({
11970
11874
  // src/commands/hook.ts
11971
11875
  import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
11972
11876
  import { join as join4 } from "node:path";
11973
- var import_picocolors11 = __toESM(require_picocolors(), 1);
11877
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
11974
11878
  var HOOK_MARKER = "# managed by contribute-now";
11975
11879
  function getHooksDir(cwd = process.cwd()) {
11976
11880
  return join4(cwd, ".git", "hooks");
@@ -12038,7 +11942,7 @@ var hook_default = defineCommand({
12038
11942
  }
12039
11943
  });
12040
11944
  async function installHook() {
12041
- projectHeading("hook install", "\uD83E\uDE9D");
11945
+ heading("\uD83E\uDE9D hook install");
12042
11946
  const config = readConfig();
12043
11947
  if (!config) {
12044
11948
  error("No .contributerc.json found. Run `contrib setup` first.");
@@ -12066,12 +11970,12 @@ async function installHook() {
12066
11970
  }
12067
11971
  writeFileSync3(hookPath, generateHookScript(), { mode: 493 });
12068
11972
  success(`commit-msg hook installed.`);
12069
- info(`Convention: ${import_picocolors11.default.bold(CONVENTION_LABELS[config.commitConvention])}`, "");
12070
- info(`Path: ${import_picocolors11.default.dim(hookPath)}`, "");
11973
+ info(`Convention: ${import_picocolors10.default.bold(CONVENTION_LABELS[config.commitConvention])}`, "");
11974
+ info(`Path: ${import_picocolors10.default.dim(hookPath)}`, "");
12071
11975
  warn("Note: hooks can be bypassed with `git commit --no-verify`.");
12072
11976
  }
12073
11977
  async function uninstallHook() {
12074
- projectHeading("hook uninstall", "\uD83E\uDE9D");
11978
+ heading("\uD83E\uDE9D hook uninstall");
12075
11979
  const hookPath = getHookPath();
12076
11980
  if (!existsSync4(hookPath)) {
12077
11981
  info("No commit-msg hook found. Nothing to uninstall.");
@@ -12087,7 +11991,7 @@ async function uninstallHook() {
12087
11991
  }
12088
11992
 
12089
11993
  // src/commands/log.ts
12090
- var import_picocolors12 = __toESM(require_picocolors(), 1);
11994
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
12091
11995
  var log_default = defineCommand({
12092
11996
  meta: {
12093
11997
  name: "log",
@@ -12157,14 +12061,14 @@ var log_default = defineCommand({
12157
12061
  usingFallback = true;
12158
12062
  }
12159
12063
  }
12160
- projectHeading("log", "\uD83D\uDCDC");
12064
+ heading("\uD83D\uDCDC commit log");
12161
12065
  printModeHeader(mode, currentBranch, compareRef, usingFallback);
12162
12066
  if (mode === "local" || mode === "remote") {
12163
12067
  if (!compareRef) {
12164
12068
  console.log();
12165
- console.log(import_picocolors12.default.yellow(" ⚠ Could not determine a comparison branch."));
12166
- console.log(import_picocolors12.default.dim(" No upstream tracking set and no remote base branch found."));
12167
- console.log(import_picocolors12.default.dim(` Use ${import_picocolors12.default.bold("contrib log --full")} to see the full commit history instead.`));
12069
+ console.log(import_picocolors11.default.yellow(" ⚠ Could not determine a comparison branch."));
12070
+ console.log(import_picocolors11.default.dim(" No upstream tracking set and no remote base branch found."));
12071
+ console.log(import_picocolors11.default.dim(` Use ${import_picocolors11.default.bold("contrib log --full")} to see the full commit history instead.`));
12168
12072
  console.log();
12169
12073
  printGuidance();
12170
12074
  return;
@@ -12206,26 +12110,26 @@ async function resolveBaseBranchRef(config) {
12206
12110
  }
12207
12111
  function printModeHeader(mode, currentBranch, compareRef, usingFallback = false) {
12208
12112
  const branch = currentBranch ?? "HEAD";
12209
- const fallbackNote = usingFallback ? import_picocolors12.default.yellow(" (no upstream — comparing against base branch)") : "";
12113
+ const fallbackNote = usingFallback ? import_picocolors11.default.yellow(" (no upstream — comparing against base branch)") : "";
12210
12114
  console.log();
12211
12115
  switch (mode) {
12212
12116
  case "local":
12213
- console.log(import_picocolors12.default.dim(` mode: ${import_picocolors12.default.bold("local")} — unpushed commits on ${import_picocolors12.default.bold(branch)}`) + fallbackNote);
12117
+ console.log(import_picocolors11.default.dim(` mode: ${import_picocolors11.default.bold("local")} — unpushed commits on ${import_picocolors11.default.bold(branch)}`) + fallbackNote);
12214
12118
  if (compareRef) {
12215
- console.log(import_picocolors12.default.dim(` comparing: ${import_picocolors12.default.bold(compareRef)} ➜ ${import_picocolors12.default.bold("HEAD")}`));
12119
+ console.log(import_picocolors11.default.dim(` comparing: ${import_picocolors11.default.bold(compareRef)} ➜ ${import_picocolors11.default.bold("HEAD")}`));
12216
12120
  }
12217
12121
  break;
12218
12122
  case "remote":
12219
- console.log(import_picocolors12.default.dim(` mode: ${import_picocolors12.default.bold("remote")} — commits on remote not yet pulled into ${import_picocolors12.default.bold(branch)}`) + fallbackNote);
12123
+ console.log(import_picocolors11.default.dim(` mode: ${import_picocolors11.default.bold("remote")} — commits on remote not yet pulled into ${import_picocolors11.default.bold(branch)}`) + fallbackNote);
12220
12124
  if (compareRef) {
12221
- console.log(import_picocolors12.default.dim(` comparing: ${import_picocolors12.default.bold("HEAD")} ➜ ${import_picocolors12.default.bold(compareRef)}`));
12125
+ console.log(import_picocolors11.default.dim(` comparing: ${import_picocolors11.default.bold("HEAD")} ➜ ${import_picocolors11.default.bold(compareRef)}`));
12222
12126
  }
12223
12127
  break;
12224
12128
  case "full":
12225
- console.log(import_picocolors12.default.dim(` mode: ${import_picocolors12.default.bold("full")} — complete commit history for ${import_picocolors12.default.bold(branch)}`));
12129
+ console.log(import_picocolors11.default.dim(` mode: ${import_picocolors11.default.bold("full")} — complete commit history for ${import_picocolors11.default.bold(branch)}`));
12226
12130
  break;
12227
12131
  case "all":
12228
- console.log(import_picocolors12.default.dim(` mode: ${import_picocolors12.default.bold("all")} — commits across all branches`));
12132
+ console.log(import_picocolors11.default.dim(` mode: ${import_picocolors11.default.bold("all")} — commits across all branches`));
12229
12133
  break;
12230
12134
  }
12231
12135
  }
@@ -12251,7 +12155,7 @@ async function renderScopedLog(options) {
12251
12155
  }
12252
12156
  console.log();
12253
12157
  for (const entry of entries) {
12254
- const hashStr = import_picocolors12.default.yellow(entry.hash);
12158
+ const hashStr = import_picocolors11.default.yellow(entry.hash);
12255
12159
  const refsStr = entry.refs ? ` ${colorizeRefs(entry.refs, protectedBranches, currentBranch)}` : "";
12256
12160
  const subjectStr = colorizeSubject(entry.subject);
12257
12161
  console.log(` ${hashStr}${refsStr} ${subjectStr}`);
@@ -12262,9 +12166,9 @@ async function renderScopedLog(options) {
12262
12166
  function printEmptyState(mode) {
12263
12167
  console.log();
12264
12168
  if (mode === "local") {
12265
- console.log(import_picocolors12.default.dim(" No local unpushed commits — you're up to date with remote!"));
12169
+ console.log(import_picocolors11.default.dim(" No local unpushed commits — you're up to date with remote!"));
12266
12170
  } else {
12267
- console.log(import_picocolors12.default.dim(" No remote-only commits — your local branch is up to date!"));
12171
+ console.log(import_picocolors11.default.dim(" No remote-only commits — your local branch is up to date!"));
12268
12172
  }
12269
12173
  console.log();
12270
12174
  }
@@ -12273,7 +12177,7 @@ async function renderFullLog(options) {
12273
12177
  if (showGraph) {
12274
12178
  const lines = await getLogGraph({ count, all, branch: targetBranch });
12275
12179
  if (lines.length === 0) {
12276
- console.log(import_picocolors12.default.dim(" No commits found."));
12180
+ console.log(import_picocolors11.default.dim(" No commits found."));
12277
12181
  console.log();
12278
12182
  return false;
12279
12183
  }
@@ -12284,13 +12188,13 @@ async function renderFullLog(options) {
12284
12188
  } else {
12285
12189
  const entries = await getLogEntries({ count, all, branch: targetBranch });
12286
12190
  if (entries.length === 0) {
12287
- console.log(import_picocolors12.default.dim(" No commits found."));
12191
+ console.log(import_picocolors11.default.dim(" No commits found."));
12288
12192
  console.log();
12289
12193
  return false;
12290
12194
  }
12291
12195
  console.log();
12292
12196
  for (const entry of entries) {
12293
- const hashStr = import_picocolors12.default.yellow(entry.hash);
12197
+ const hashStr = import_picocolors11.default.yellow(entry.hash);
12294
12198
  const refsStr = entry.refs ? ` ${colorizeRefs(entry.refs, protectedBranches, currentBranch)}` : "";
12295
12199
  const subjectStr = colorizeSubject(entry.subject);
12296
12200
  console.log(` ${hashStr}${refsStr} ${subjectStr}`);
@@ -12302,42 +12206,42 @@ function printFooter(mode, count, targetBranch) {
12302
12206
  console.log();
12303
12207
  switch (mode) {
12304
12208
  case "local":
12305
- console.log(import_picocolors12.default.dim(` Showing up to ${count} unpushed commits`));
12209
+ console.log(import_picocolors11.default.dim(` Showing up to ${count} unpushed commits`));
12306
12210
  break;
12307
12211
  case "remote":
12308
- console.log(import_picocolors12.default.dim(` Showing up to ${count} remote-only commits`));
12212
+ console.log(import_picocolors11.default.dim(` Showing up to ${count} remote-only commits`));
12309
12213
  break;
12310
12214
  case "full":
12311
- console.log(import_picocolors12.default.dim(` Showing ${count} most recent commits${targetBranch ? ` (${targetBranch})` : ""}`));
12215
+ console.log(import_picocolors11.default.dim(` Showing ${count} most recent commits${targetBranch ? ` (${targetBranch})` : ""}`));
12312
12216
  break;
12313
12217
  case "all":
12314
- console.log(import_picocolors12.default.dim(` Showing ${count} most recent commits (all branches)`));
12218
+ console.log(import_picocolors11.default.dim(` Showing ${count} most recent commits (all branches)`));
12315
12219
  break;
12316
12220
  }
12317
12221
  }
12318
12222
  function printGuidance() {
12319
12223
  console.log();
12320
- console.log(import_picocolors12.default.dim(" ─── quick guide ───"));
12321
- console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log")} local unpushed commits (default)`));
12322
- console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log --remote")} commits on remote not yet pulled`));
12323
- console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log --full")} full history for the current branch`));
12324
- console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log --all")} commits across all branches`));
12325
- console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log -n 50")} change the commit limit (default: 20)`));
12326
- console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log -b dev")} view log for a specific branch`));
12327
- console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log --no-graph")} flat list without graph lines`));
12224
+ console.log(import_picocolors11.default.dim(" ─── quick guide ───"));
12225
+ console.log(import_picocolors11.default.dim(` ${import_picocolors11.default.bold("contrib log")} local unpushed commits (default)`));
12226
+ console.log(import_picocolors11.default.dim(` ${import_picocolors11.default.bold("contrib log --remote")} commits on remote not yet pulled`));
12227
+ console.log(import_picocolors11.default.dim(` ${import_picocolors11.default.bold("contrib log --full")} full history for the current branch`));
12228
+ console.log(import_picocolors11.default.dim(` ${import_picocolors11.default.bold("contrib log --all")} commits across all branches`));
12229
+ console.log(import_picocolors11.default.dim(` ${import_picocolors11.default.bold("contrib log -n 50")} change the commit limit (default: 20)`));
12230
+ console.log(import_picocolors11.default.dim(` ${import_picocolors11.default.bold("contrib log -b dev")} view log for a specific branch`));
12231
+ console.log(import_picocolors11.default.dim(` ${import_picocolors11.default.bold("contrib log --no-graph")} flat list without graph lines`));
12328
12232
  console.log();
12329
12233
  }
12330
12234
  function colorizeGraphLine(line, protectedBranches, currentBranch) {
12331
12235
  const match = line.match(/^([|/\\*\s_.-]*)([a-f0-9]{7,12})(\s+\(([^)]+)\))?\s*(.*)/);
12332
12236
  if (!match) {
12333
- return import_picocolors12.default.cyan(line);
12237
+ return import_picocolors11.default.cyan(line);
12334
12238
  }
12335
12239
  const [, graphPart = "", hash, , refs, subject = ""] = match;
12336
12240
  const parts = [];
12337
12241
  if (graphPart) {
12338
12242
  parts.push(colorizeGraphChars(graphPart));
12339
12243
  }
12340
- parts.push(import_picocolors12.default.yellow(hash));
12244
+ parts.push(import_picocolors11.default.yellow(hash));
12341
12245
  if (refs) {
12342
12246
  parts.push(` (${colorizeRefs(refs, protectedBranches, currentBranch)})`);
12343
12247
  }
@@ -12348,15 +12252,15 @@ function colorizeGraphChars(graphPart) {
12348
12252
  return graphPart.split("").map((ch) => {
12349
12253
  switch (ch) {
12350
12254
  case "*":
12351
- return import_picocolors12.default.green(ch);
12255
+ return import_picocolors11.default.green(ch);
12352
12256
  case "|":
12353
- return import_picocolors12.default.cyan(ch);
12257
+ return import_picocolors11.default.cyan(ch);
12354
12258
  case "/":
12355
12259
  case "\\":
12356
- return import_picocolors12.default.cyan(ch);
12260
+ return import_picocolors11.default.cyan(ch);
12357
12261
  case "-":
12358
12262
  case "_":
12359
- return import_picocolors12.default.cyan(ch);
12263
+ return import_picocolors11.default.cyan(ch);
12360
12264
  default:
12361
12265
  return ch;
12362
12266
  }
@@ -12368,45 +12272,45 @@ function colorizeRefs(refs, protectedBranches, currentBranch) {
12368
12272
  if (trimmed.startsWith("HEAD ->") || trimmed === "HEAD") {
12369
12273
  const branchName = trimmed.replace("HEAD -> ", "");
12370
12274
  if (trimmed === "HEAD") {
12371
- return import_picocolors12.default.bold(import_picocolors12.default.cyan("HEAD"));
12275
+ return import_picocolors11.default.bold(import_picocolors11.default.cyan("HEAD"));
12372
12276
  }
12373
- return `${import_picocolors12.default.bold(import_picocolors12.default.cyan("HEAD"))} ${import_picocolors12.default.dim("->")} ${colorizeRefName(branchName, protectedBranches, currentBranch)}`;
12277
+ return `${import_picocolors11.default.bold(import_picocolors11.default.cyan("HEAD"))} ${import_picocolors11.default.dim("->")} ${colorizeRefName(branchName, protectedBranches, currentBranch)}`;
12374
12278
  }
12375
12279
  if (trimmed.startsWith("tag:")) {
12376
- return import_picocolors12.default.bold(import_picocolors12.default.magenta(trimmed));
12280
+ return import_picocolors11.default.bold(import_picocolors11.default.magenta(trimmed));
12377
12281
  }
12378
12282
  return colorizeRefName(trimmed, protectedBranches, currentBranch);
12379
- }).join(import_picocolors12.default.dim(", "));
12283
+ }).join(import_picocolors11.default.dim(", "));
12380
12284
  }
12381
12285
  function colorizeRefName(name, protectedBranches, currentBranch) {
12382
12286
  const isRemote = name.includes("/");
12383
12287
  const localName = isRemote ? name.split("/").slice(1).join("/") : name;
12384
12288
  if (protectedBranches.includes(localName)) {
12385
- return isRemote ? import_picocolors12.default.bold(import_picocolors12.default.red(name)) : import_picocolors12.default.bold(import_picocolors12.default.red(name));
12289
+ return isRemote ? import_picocolors11.default.bold(import_picocolors11.default.red(name)) : import_picocolors11.default.bold(import_picocolors11.default.red(name));
12386
12290
  }
12387
12291
  if (localName === currentBranch) {
12388
- return import_picocolors12.default.bold(import_picocolors12.default.green(name));
12292
+ return import_picocolors11.default.bold(import_picocolors11.default.green(name));
12389
12293
  }
12390
12294
  if (isRemote) {
12391
- return import_picocolors12.default.blue(name);
12295
+ return import_picocolors11.default.blue(name);
12392
12296
  }
12393
- return import_picocolors12.default.green(name);
12297
+ return import_picocolors11.default.green(name);
12394
12298
  }
12395
12299
  function colorizeSubject(subject) {
12396
12300
  const emojiMatch = subject.match(/^((?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)+\s*)/u);
12397
12301
  if (emojiMatch) {
12398
12302
  const emoji = emojiMatch[1];
12399
12303
  const rest = subject.slice(emoji.length);
12400
- return `${emoji}${import_picocolors12.default.white(rest)}`;
12304
+ return `${emoji}${import_picocolors11.default.white(rest)}`;
12401
12305
  }
12402
12306
  if (subject.startsWith("Merge ")) {
12403
- return import_picocolors12.default.dim(subject);
12307
+ return import_picocolors11.default.dim(subject);
12404
12308
  }
12405
- return import_picocolors12.default.white(subject);
12309
+ return import_picocolors11.default.white(subject);
12406
12310
  }
12407
12311
 
12408
12312
  // src/commands/save.ts
12409
- var import_picocolors13 = __toESM(require_picocolors(), 1);
12313
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
12410
12314
  import { execFile as execFileCb4 } from "node:child_process";
12411
12315
  function gitRun(args) {
12412
12316
  return new Promise((resolve2) => {
@@ -12468,7 +12372,7 @@ var save_default = defineCommand({
12468
12372
  }
12469
12373
  });
12470
12374
  async function handleSave(message) {
12471
- projectHeading("save", "\uD83D\uDCBE");
12375
+ heading("\uD83D\uDCBE contrib save");
12472
12376
  const currentBranch = await getCurrentBranch();
12473
12377
  const label = message ?? `work-in-progress on ${currentBranch ?? "unknown"}`;
12474
12378
  const stashMsg = `contrib-save: ${label}`;
@@ -12481,11 +12385,11 @@ async function handleSave(message) {
12481
12385
  info("No uncommitted changes to save.");
12482
12386
  return;
12483
12387
  }
12484
- success(`Saved: ${import_picocolors13.default.dim(label)}`);
12485
- info(`Use ${import_picocolors13.default.bold("contrib save --restore")} to bring them back.`, "");
12388
+ success(`Saved: ${import_picocolors12.default.dim(label)}`);
12389
+ info(`Use ${import_picocolors12.default.bold("contrib save --restore")} to bring them back.`, "");
12486
12390
  }
12487
12391
  async function handleRestore() {
12488
- projectHeading("save --restore", "\uD83D\uDCBE");
12392
+ heading("\uD83D\uDCBE contrib save --restore");
12489
12393
  const stashes = await getStashList();
12490
12394
  if (stashes.length === 0) {
12491
12395
  info("No saved changes found.");
@@ -12498,7 +12402,7 @@ async function handleRestore() {
12498
12402
  warn("You may have conflicts. Resolve them and run `git stash drop` when done.");
12499
12403
  process.exit(1);
12500
12404
  }
12501
- success(`Restored: ${import_picocolors13.default.dim(stashes[0].message)}`);
12405
+ success(`Restored: ${import_picocolors12.default.dim(stashes[0].message)}`);
12502
12406
  return;
12503
12407
  }
12504
12408
  const choices = stashes.map((s2) => `${s2.index} ${s2.message}`);
@@ -12511,10 +12415,10 @@ async function handleRestore() {
12511
12415
  process.exit(1);
12512
12416
  }
12513
12417
  const match = stashes.find((s2) => String(s2.index) === idx);
12514
- success(`Restored: ${import_picocolors13.default.dim(match?.message ?? "saved changes")}`);
12418
+ success(`Restored: ${import_picocolors12.default.dim(match?.message ?? "saved changes")}`);
12515
12419
  }
12516
12420
  async function handleList() {
12517
- projectHeading("save --list", "\uD83D\uDCBE");
12421
+ heading("\uD83D\uDCBE contrib save --list");
12518
12422
  const stashes = await getStashList();
12519
12423
  if (stashes.length === 0) {
12520
12424
  info("No saved changes.");
@@ -12522,16 +12426,16 @@ async function handleList() {
12522
12426
  }
12523
12427
  console.log();
12524
12428
  for (const s2 of stashes) {
12525
- const idx = import_picocolors13.default.dim(`[${s2.index}]`);
12429
+ const idx = import_picocolors12.default.dim(`[${s2.index}]`);
12526
12430
  const msg = s2.message;
12527
12431
  console.log(` ${idx} ${msg}`);
12528
12432
  }
12529
12433
  console.log();
12530
- info(`Use ${import_picocolors13.default.bold("contrib save --restore")} to bring changes back.`, "");
12531
- info(`Use ${import_picocolors13.default.bold("contrib save --drop")} to discard saved changes.`, "");
12434
+ info(`Use ${import_picocolors12.default.bold("contrib save --restore")} to bring changes back.`, "");
12435
+ info(`Use ${import_picocolors12.default.bold("contrib save --drop")} to discard saved changes.`, "");
12532
12436
  }
12533
12437
  async function handleDrop() {
12534
- projectHeading("save --drop", "\uD83D\uDCBE");
12438
+ heading("\uD83D\uDCBE contrib save --drop");
12535
12439
  const stashes = await getStashList();
12536
12440
  if (stashes.length === 0) {
12537
12441
  info("No saved changes to drop.");
@@ -12546,7 +12450,7 @@ async function handleDrop() {
12546
12450
  process.exit(1);
12547
12451
  }
12548
12452
  const match = stashes.find((s2) => String(s2.index) === idx);
12549
- success(`Dropped: ${import_picocolors13.default.dim(match?.message ?? "saved changes")}`);
12453
+ success(`Dropped: ${import_picocolors12.default.dim(match?.message ?? "saved changes")}`);
12550
12454
  }
12551
12455
  async function getStashList() {
12552
12456
  const result = await gitRun(["stash", "list"]);
@@ -12563,7 +12467,7 @@ async function getStashList() {
12563
12467
  }
12564
12468
 
12565
12469
  // src/commands/setup.ts
12566
- var import_picocolors14 = __toESM(require_picocolors(), 1);
12470
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
12567
12471
  async function shouldContinueSetupWithExistingConfig(options) {
12568
12472
  const {
12569
12473
  existingConfig,
@@ -12611,7 +12515,7 @@ var setup_default = defineCommand({
12611
12515
  error("Not inside a git repository. Run this command from within a git repo.");
12612
12516
  process.exit(1);
12613
12517
  }
12614
- projectHeading("setup", "\uD83D\uDD27");
12518
+ heading("\uD83D\uDD27 contribute-now setup");
12615
12519
  const existingConfig = readConfig();
12616
12520
  const shouldContinue = await shouldContinueSetupWithExistingConfig({
12617
12521
  existingConfig,
@@ -12636,7 +12540,7 @@ var setup_default = defineCommand({
12636
12540
  workflow = "github-flow";
12637
12541
  else if (workflowChoice.startsWith("Git Flow"))
12638
12542
  workflow = "git-flow";
12639
- info(`Workflow: ${import_picocolors14.default.bold(WORKFLOW_DESCRIPTIONS[workflow])}`);
12543
+ info(`Workflow: ${import_picocolors13.default.bold(WORKFLOW_DESCRIPTIONS[workflow])}`);
12640
12544
  const conventionChoice = await selectPrompt("Which commit convention should this project use?", [
12641
12545
  `${CONVENTION_DESCRIPTIONS["clean-commit"]} (recommended)`,
12642
12546
  CONVENTION_DESCRIPTIONS.conventional,
@@ -12647,7 +12551,6 @@ var setup_default = defineCommand({
12647
12551
  commitConvention = "conventional";
12648
12552
  else if (conventionChoice.includes("No commit"))
12649
12553
  commitConvention = "none";
12650
- const enableAI = await confirmPrompt("Enable AI-assisted features like commit messages, branch naming, PR text, and conflict guidance?");
12651
12554
  const remotes = await getRemotes();
12652
12555
  if (remotes.length === 0) {
12653
12556
  error("No git remotes found. Add a remote first (e.g., git remote add origin <url>).");
@@ -12701,15 +12604,15 @@ var setup_default = defineCommand({
12701
12604
  detectedRole = roleChoice;
12702
12605
  detectionSource = "user selection";
12703
12606
  } else {
12704
- info(`Detected role: ${import_picocolors14.default.bold(detectedRole)} (via ${detectionSource})`);
12705
- const confirmed = await confirmPrompt(`Role detected as ${import_picocolors14.default.bold(detectedRole)}. Is this correct?`);
12607
+ info(`Detected role: ${import_picocolors13.default.bold(detectedRole)} (via ${detectionSource})`);
12608
+ const confirmed = await confirmPrompt(`Role detected as ${import_picocolors13.default.bold(detectedRole)}. Is this correct?`);
12706
12609
  if (!confirmed) {
12707
12610
  const roleChoice = await selectPrompt("Select your role:", ["maintainer", "contributor"]);
12708
12611
  detectedRole = roleChoice;
12709
12612
  }
12710
12613
  }
12711
12614
  const defaultConfig = getDefaultConfig();
12712
- info(import_picocolors14.default.dim("Tip: press Enter to keep the default branch name shown in each prompt."));
12615
+ info(import_picocolors13.default.dim("Tip: press Enter to keep the default branch name shown in each prompt."));
12713
12616
  const mainBranchDefault = defaultConfig.mainBranch;
12714
12617
  const mainBranch = await inputPrompt(`Main branch name (default: ${mainBranchDefault} — press Enter to keep)`, mainBranchDefault);
12715
12618
  let devBranch;
@@ -12735,7 +12638,7 @@ var setup_default = defineCommand({
12735
12638
  error("Setup cannot continue without the upstream remote for contributors.");
12736
12639
  process.exit(1);
12737
12640
  }
12738
- success(`Added remote ${import_picocolors14.default.bold(upstreamRemote)} → ${upstreamUrl}`);
12641
+ success(`Added remote ${import_picocolors13.default.bold(upstreamRemote)} → ${upstreamUrl}`);
12739
12642
  } else {
12740
12643
  error("An upstream remote URL is required for contributors.");
12741
12644
  info("Add it manually: git remote add upstream <url>", "");
@@ -12751,23 +12654,22 @@ var setup_default = defineCommand({
12751
12654
  upstream: upstreamRemote,
12752
12655
  origin: originRemote,
12753
12656
  branchPrefixes: defaultConfig.branchPrefixes,
12754
- commitConvention,
12755
- aiEnabled: enableAI
12657
+ commitConvention
12756
12658
  };
12757
12659
  writeConfig(config);
12758
12660
  success(`Config written to .contributerc.json`);
12759
12661
  const syncRemote = config.role === "contributor" ? config.upstream : config.origin;
12760
- info(`Fetching ${import_picocolors14.default.bold(syncRemote)} to verify branch configuration...`, "");
12662
+ info(`Fetching ${import_picocolors13.default.bold(syncRemote)} to verify branch configuration...`, "");
12761
12663
  await fetchRemote(syncRemote);
12762
12664
  const mainRef = `${syncRemote}/${config.mainBranch}`;
12763
12665
  if (!await refExists(mainRef)) {
12764
- warn(`Main branch ref ${import_picocolors14.default.bold(mainRef)} not found on remote.`);
12666
+ warn(`Main branch ref ${import_picocolors13.default.bold(mainRef)} not found on remote.`);
12765
12667
  warn("Config was saved — verify the branch name and re-run setup if needed.");
12766
12668
  }
12767
12669
  if (config.devBranch) {
12768
12670
  const devRef = `${syncRemote}/${config.devBranch}`;
12769
12671
  if (!await refExists(devRef)) {
12770
- warn(`Dev branch ref ${import_picocolors14.default.bold(devRef)} not found on remote.`);
12672
+ warn(`Dev branch ref ${import_picocolors13.default.bold(devRef)} not found on remote.`);
12771
12673
  warn("Config was saved — verify the branch name and re-run setup if needed.");
12772
12674
  }
12773
12675
  }
@@ -12775,33 +12677,31 @@ var setup_default = defineCommand({
12775
12677
  info("Added .contributerc.json to .gitignore to avoid committing personal config.");
12776
12678
  }
12777
12679
  console.log();
12778
- info(`Workflow: ${import_picocolors14.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
12779
- info(`Convention: ${import_picocolors14.default.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
12780
- info(`AI: ${import_picocolors14.default.bold(isAIEnabled(config) ? "enabled" : "disabled")}`);
12781
- info(`Role: ${import_picocolors14.default.bold(config.role)}`);
12680
+ info(`Workflow: ${import_picocolors13.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
12681
+ info(`Convention: ${import_picocolors13.default.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
12682
+ info(`Role: ${import_picocolors13.default.bold(config.role)}`);
12782
12683
  if (config.devBranch) {
12783
- info(`Main: ${import_picocolors14.default.bold(config.mainBranch)} | Dev: ${import_picocolors14.default.bold(config.devBranch)}`);
12684
+ info(`Main: ${import_picocolors13.default.bold(config.mainBranch)} | Dev: ${import_picocolors13.default.bold(config.devBranch)}`);
12784
12685
  } else {
12785
- info(`Main: ${import_picocolors14.default.bold(config.mainBranch)}`);
12686
+ info(`Main: ${import_picocolors13.default.bold(config.mainBranch)}`);
12786
12687
  }
12787
- info(`Origin: ${import_picocolors14.default.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${import_picocolors14.default.bold(config.upstream)}` : ""}`);
12688
+ info(`Origin: ${import_picocolors13.default.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${import_picocolors13.default.bold(config.upstream)}` : ""}`);
12788
12689
  }
12789
12690
  });
12790
12691
  function logConfigSummary(config) {
12791
- info(`Workflow: ${import_picocolors14.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
12792
- info(`Convention: ${import_picocolors14.default.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
12793
- info(`AI: ${import_picocolors14.default.bold(isAIEnabled(config) ? "enabled" : "disabled")}`);
12794
- info(`Role: ${import_picocolors14.default.bold(config.role)}`);
12692
+ info(`Workflow: ${import_picocolors13.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
12693
+ info(`Convention: ${import_picocolors13.default.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
12694
+ info(`Role: ${import_picocolors13.default.bold(config.role)}`);
12795
12695
  if (config.devBranch) {
12796
- info(`Main: ${import_picocolors14.default.bold(config.mainBranch)} | Dev: ${import_picocolors14.default.bold(config.devBranch)}`);
12696
+ info(`Main: ${import_picocolors13.default.bold(config.mainBranch)} | Dev: ${import_picocolors13.default.bold(config.devBranch)}`);
12797
12697
  } else {
12798
- info(`Main: ${import_picocolors14.default.bold(config.mainBranch)}`);
12698
+ info(`Main: ${import_picocolors13.default.bold(config.mainBranch)}`);
12799
12699
  }
12800
- info(`Origin: ${import_picocolors14.default.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${import_picocolors14.default.bold(config.upstream)}` : ""}`);
12700
+ info(`Origin: ${import_picocolors13.default.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${import_picocolors13.default.bold(config.upstream)}` : ""}`);
12801
12701
  }
12802
12702
 
12803
12703
  // src/commands/start.ts
12804
- var import_picocolors15 = __toESM(require_picocolors(), 1);
12704
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
12805
12705
  var start_default = defineCommand({
12806
12706
  meta: {
12807
12707
  name: "start",
@@ -12841,28 +12741,57 @@ var start_default = defineCommand({
12841
12741
  const { branchPrefixes } = config;
12842
12742
  const baseBranch = getBaseBranch(config);
12843
12743
  const syncSource = getSyncSource(config);
12844
- let branchName = args.name?.trim();
12845
- projectHeading("start", "\uD83C\uDF3F");
12846
- branchName = await promptForBranchName({
12847
- initialValue: branchName,
12848
- branchPrefixes,
12849
- useAI: isAIEnabled(config, args["no-ai"]),
12850
- model: args.model
12851
- });
12744
+ let branchName = args.name;
12745
+ heading("\uD83C\uDF3F contrib start");
12852
12746
  if (!branchName) {
12853
- warn("Start cancelled.");
12854
- process.exit(0);
12747
+ branchName = await inputPrompt("What are you going to work on?");
12748
+ if (!branchName || branchName.trim().length === 0) {
12749
+ error("A branch name or description is required.");
12750
+ process.exit(1);
12751
+ }
12752
+ branchName = branchName.trim();
12753
+ }
12754
+ const useAI = !args["no-ai"] && looksLikeNaturalLanguage(branchName);
12755
+ if (useAI) {
12756
+ const spinner = createSpinner("Generating branch name suggestion...");
12757
+ const suggested = await suggestBranchName(branchName, args.model);
12758
+ if (suggested) {
12759
+ spinner.success("Branch name suggestion ready.");
12760
+ console.log(`
12761
+ ${import_picocolors14.default.dim("AI suggestion:")} ${import_picocolors14.default.bold(import_picocolors14.default.cyan(suggested))}`);
12762
+ const accepted = await confirmPrompt(`Use ${import_picocolors14.default.bold(suggested)} as your branch name?`);
12763
+ if (accepted) {
12764
+ branchName = suggested;
12765
+ } else {
12766
+ branchName = await inputPrompt("Enter branch name", branchName);
12767
+ }
12768
+ } else {
12769
+ spinner.fail("AI did not return a branch name suggestion.");
12770
+ }
12771
+ }
12772
+ if (!hasPrefix(branchName, branchPrefixes)) {
12773
+ const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors14.default.bold(branchName)}:`, branchPrefixes);
12774
+ branchName = formatBranchName(prefix, branchName);
12775
+ }
12776
+ if (!isValidBranchName(branchName)) {
12777
+ error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
12778
+ process.exit(1);
12779
+ }
12780
+ info(`Creating branch: ${import_picocolors14.default.bold(branchName)}`);
12781
+ if (await branchExists(branchName)) {
12782
+ error(`Branch ${import_picocolors14.default.bold(branchName)} already exists.`);
12783
+ info(` Use ${import_picocolors14.default.bold(`git checkout ${branchName}`)} to switch to it, or choose a different name.`, "");
12784
+ process.exit(1);
12855
12785
  }
12856
- info(`Creating branch: ${import_picocolors15.default.bold(branchName)}`);
12857
12786
  await fetchRemote(syncSource.remote);
12858
12787
  if (!await refExists(syncSource.ref)) {
12859
- warn(`Remote ref ${import_picocolors15.default.bold(syncSource.ref)} not found. Creating branch from local ${import_picocolors15.default.bold(baseBranch)}.`);
12788
+ warn(`Remote ref ${import_picocolors14.default.bold(syncSource.ref)} not found. Creating branch from local ${import_picocolors14.default.bold(baseBranch)}.`);
12860
12789
  }
12861
12790
  const currentBranch = await getCurrentBranch();
12862
12791
  if (currentBranch === baseBranch && await refExists(syncSource.ref)) {
12863
12792
  const ahead = await countCommitsAhead(baseBranch, syncSource.ref);
12864
12793
  if (ahead > 0) {
12865
- warn(`You are on ${import_picocolors15.default.bold(baseBranch)} with ${import_picocolors15.default.bold(String(ahead))} local commit${ahead > 1 ? "s" : ""} not in ${import_picocolors15.default.bold(syncSource.ref)}.`);
12794
+ warn(`You are on ${import_picocolors14.default.bold(baseBranch)} with ${import_picocolors14.default.bold(String(ahead))} local commit${ahead > 1 ? "s" : ""} not in ${import_picocolors14.default.bold(syncSource.ref)}.`);
12866
12795
  info(" Syncing will discard those commits. Consider backing them up first (e.g. create a branch).");
12867
12796
  const proceed = await confirmPrompt("Discard local commits and sync to remote?");
12868
12797
  if (!proceed) {
@@ -12879,10 +12808,10 @@ var start_default = defineCommand({
12879
12808
  error(`Failed to create branch: ${result2.stderr}`);
12880
12809
  process.exit(1);
12881
12810
  }
12882
- success(`Created ${import_picocolors15.default.bold(branchName)} from ${import_picocolors15.default.bold(syncSource.ref)}`);
12811
+ success(`Created ${import_picocolors14.default.bold(branchName)} from ${import_picocolors14.default.bold(syncSource.ref)}`);
12883
12812
  return;
12884
12813
  }
12885
- error(`Failed to update ${import_picocolors15.default.bold(baseBranch)}: ${updateResult.stderr}`);
12814
+ error(`Failed to update ${import_picocolors14.default.bold(baseBranch)}: ${updateResult.stderr}`);
12886
12815
  info("Make sure your base branch exists locally or the remote ref is available.", "");
12887
12816
  process.exit(1);
12888
12817
  }
@@ -12891,12 +12820,12 @@ var start_default = defineCommand({
12891
12820
  error(`Failed to create branch: ${result.stderr}`);
12892
12821
  process.exit(1);
12893
12822
  }
12894
- success(`Created ${import_picocolors15.default.bold(branchName)} from latest ${import_picocolors15.default.bold(baseBranch)}`);
12823
+ success(`Created ${import_picocolors14.default.bold(branchName)} from latest ${import_picocolors14.default.bold(baseBranch)}`);
12895
12824
  }
12896
12825
  });
12897
12826
 
12898
12827
  // src/commands/status.ts
12899
- var import_picocolors16 = __toESM(require_picocolors(), 1);
12828
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
12900
12829
  var status_default = defineCommand({
12901
12830
  meta: {
12902
12831
  name: "status",
@@ -12912,9 +12841,9 @@ var status_default = defineCommand({
12912
12841
  error("No .contributerc.json found. Run `contrib setup` first.");
12913
12842
  process.exit(1);
12914
12843
  }
12915
- projectHeading("status", "\uD83D\uDCCA");
12916
- console.log(` ${import_picocolors16.default.dim("Workflow:")} ${import_picocolors16.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
12917
- console.log(` ${import_picocolors16.default.dim("Role:")} ${import_picocolors16.default.bold(config.role)}`);
12844
+ heading("\uD83D\uDCCA contribute-now status");
12845
+ console.log(` ${import_picocolors15.default.dim("Workflow:")} ${import_picocolors15.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
12846
+ console.log(` ${import_picocolors15.default.dim("Role:")} ${import_picocolors15.default.bold(config.role)}`);
12918
12847
  console.log();
12919
12848
  await fetchAll();
12920
12849
  const currentBranch = await getCurrentBranch();
@@ -12923,7 +12852,7 @@ var status_default = defineCommand({
12923
12852
  const isContributor = config.role === "contributor";
12924
12853
  const [dirty, fileStatus] = await Promise.all([hasUncommittedChanges(), getFileStatus()]);
12925
12854
  if (dirty) {
12926
- console.log(` ${import_picocolors16.default.yellow("⚠")} ${import_picocolors16.default.yellow("Uncommitted changes in working tree")}`);
12855
+ console.log(` ${import_picocolors15.default.yellow("⚠")} ${import_picocolors15.default.yellow("Uncommitted changes in working tree")}`);
12927
12856
  console.log();
12928
12857
  }
12929
12858
  const mainRemote = `${origin}/${mainBranch}`;
@@ -12942,16 +12871,16 @@ var status_default = defineCommand({
12942
12871
  if (isFeatureBranch) {
12943
12872
  const branchDiv = await getDivergence(currentBranch, baseBranch);
12944
12873
  const branchLine = formatStatus(currentBranch, baseBranch, branchDiv.ahead, branchDiv.behind);
12945
- console.log(branchLine + import_picocolors16.default.dim(` (current ${import_picocolors16.default.green("*")})`));
12874
+ console.log(branchLine + import_picocolors15.default.dim(` (current ${import_picocolors15.default.green("*")})`));
12946
12875
  branchStatus = await detectBranchStatus(currentBranch, baseBranch);
12947
12876
  if (branchStatus.merged) {
12948
- console.log(` ${import_picocolors16.default.green("✓")} ${import_picocolors16.default.green("Branch merged")} — ${import_picocolors16.default.dim(branchStatus.mergedReason ?? "all commits reachable from base")}`);
12877
+ console.log(` ${import_picocolors15.default.green("✓")} ${import_picocolors15.default.green("Branch merged")} — ${import_picocolors15.default.dim(branchStatus.mergedReason ?? "all commits reachable from base")}`);
12949
12878
  }
12950
12879
  if (branchStatus.stale) {
12951
- console.log(` ${import_picocolors16.default.yellow("⏳")} ${import_picocolors16.default.yellow("Branch is stale")} — ${import_picocolors16.default.dim(`last commit ${branchStatus.staleDaysAgo} days ago`)}`);
12880
+ console.log(` ${import_picocolors15.default.yellow("⏳")} ${import_picocolors15.default.yellow("Branch is stale")} — ${import_picocolors15.default.dim(`last commit ${branchStatus.staleDaysAgo} days ago`)}`);
12952
12881
  }
12953
12882
  } else if (currentBranch) {
12954
- console.log(import_picocolors16.default.dim(` (on ${import_picocolors16.default.bold(currentBranch)} branch)`));
12883
+ console.log(import_picocolors15.default.dim(` (on ${import_picocolors15.default.bold(currentBranch)} branch)`));
12955
12884
  }
12956
12885
  let branchesAligned = true;
12957
12886
  {
@@ -12981,20 +12910,20 @@ var status_default = defineCommand({
12981
12910
  }
12982
12911
  branchesAligned = groups.size === 1;
12983
12912
  console.log();
12984
- console.log(` ${import_picocolors16.default.bold("\uD83D\uDD17 Branch Alignment")}`);
12913
+ console.log(` ${import_picocolors15.default.bold("\uD83D\uDD17 Branch Alignment")}`);
12985
12914
  for (const [hash, names] of groups) {
12986
12915
  const short = hash.slice(0, 7);
12987
- const nameStr = names.map((n2) => import_picocolors16.default.bold(n2)).join(import_picocolors16.default.dim(" · "));
12988
- console.log(` ${import_picocolors16.default.yellow(short)} ${import_picocolors16.default.dim("──")} ${nameStr}`);
12916
+ const nameStr = names.map((n2) => import_picocolors15.default.bold(n2)).join(import_picocolors15.default.dim(" · "));
12917
+ console.log(` ${import_picocolors15.default.yellow(short)} ${import_picocolors15.default.dim("──")} ${nameStr}`);
12989
12918
  const subject = await getCommitSubject(hash);
12990
12919
  if (subject) {
12991
- console.log(` ${import_picocolors16.default.dim(subject)}`);
12920
+ console.log(` ${import_picocolors15.default.dim(subject)}`);
12992
12921
  }
12993
12922
  }
12994
12923
  if (branchesAligned) {
12995
- console.log(` ${import_picocolors16.default.green("✓")} ${import_picocolors16.default.green("All branches aligned")} ${import_picocolors16.default.dim("— ready to start")}`);
12924
+ console.log(` ${import_picocolors15.default.green("✓")} ${import_picocolors15.default.green("All branches aligned")} ${import_picocolors15.default.dim("— ready to start")}`);
12996
12925
  } else {
12997
- console.log(` ${import_picocolors16.default.yellow("⚠")} ${import_picocolors16.default.yellow("Branches are not fully aligned")}`);
12926
+ console.log(` ${import_picocolors15.default.yellow("⚠")} ${import_picocolors15.default.yellow("Branches are not fully aligned")}`);
12998
12927
  }
12999
12928
  }
13000
12929
  }
@@ -13002,70 +12931,70 @@ var status_default = defineCommand({
13002
12931
  if (hasFiles) {
13003
12932
  console.log();
13004
12933
  if (fileStatus.staged.length > 0) {
13005
- console.log(` ${import_picocolors16.default.green("Staged for commit:")}`);
12934
+ console.log(` ${import_picocolors15.default.green("Staged for commit:")}`);
13006
12935
  for (const { file, status } of fileStatus.staged) {
13007
- console.log(` ${import_picocolors16.default.green("+")} ${import_picocolors16.default.dim(`${status}:`)} ${file}`);
12936
+ console.log(` ${import_picocolors15.default.green("+")} ${import_picocolors15.default.dim(`${status}:`)} ${file}`);
13008
12937
  }
13009
12938
  }
13010
12939
  if (fileStatus.modified.length > 0) {
13011
- console.log(` ${import_picocolors16.default.yellow("Unstaged changes:")}`);
12940
+ console.log(` ${import_picocolors15.default.yellow("Unstaged changes:")}`);
13012
12941
  for (const { file, status } of fileStatus.modified) {
13013
- console.log(` ${import_picocolors16.default.yellow("~")} ${import_picocolors16.default.dim(`${status}:`)} ${file}`);
12942
+ console.log(` ${import_picocolors15.default.yellow("~")} ${import_picocolors15.default.dim(`${status}:`)} ${file}`);
13014
12943
  }
13015
12944
  }
13016
12945
  if (fileStatus.untracked.length > 0) {
13017
- console.log(` ${import_picocolors16.default.red("Untracked files:")}`);
12946
+ console.log(` ${import_picocolors15.default.red("Untracked files:")}`);
13018
12947
  for (const file of fileStatus.untracked) {
13019
- console.log(` ${import_picocolors16.default.red("?")} ${file}`);
12948
+ console.log(` ${import_picocolors15.default.red("?")} ${file}`);
13020
12949
  }
13021
12950
  }
13022
12951
  } else if (!dirty) {
13023
- console.log(` ${import_picocolors16.default.green("✓")} ${import_picocolors16.default.dim("Working tree clean")}`);
12952
+ console.log(` ${import_picocolors15.default.green("✓")} ${import_picocolors15.default.dim("Working tree clean")}`);
13024
12953
  }
13025
12954
  const tips = [];
13026
12955
  if (!branchesAligned) {
13027
- tips.push(`Run ${import_picocolors16.default.bold("contrib sync")} to align your local branches with the remote`);
12956
+ tips.push(`Run ${import_picocolors15.default.bold("contrib sync")} to align your local branches with the remote`);
13028
12957
  }
13029
12958
  if (fileStatus.staged.length > 0) {
13030
- tips.push(`Run ${import_picocolors16.default.bold("contrib commit")} to commit staged changes`);
12959
+ tips.push(`Run ${import_picocolors15.default.bold("contrib commit")} to commit staged changes`);
13031
12960
  }
13032
12961
  if (fileStatus.modified.length > 0 || fileStatus.untracked.length > 0) {
13033
- tips.push(`Run ${import_picocolors16.default.bold("contrib commit")} to stage and commit changes`);
12962
+ tips.push(`Run ${import_picocolors15.default.bold("contrib commit")} to stage and commit changes`);
13034
12963
  }
13035
12964
  if (isFeatureBranch && branchStatus) {
13036
12965
  if (branchStatus.merged) {
13037
- tips.push(`Run ${import_picocolors16.default.bold("contrib clean")} to delete this merged branch`);
12966
+ tips.push(`Run ${import_picocolors15.default.bold("contrib clean")} to delete this merged branch`);
13038
12967
  } else if (branchStatus.stale) {
13039
- tips.push(`Run ${import_picocolors16.default.bold("contrib sync")} to rebase on latest changes, or ${import_picocolors16.default.bold("contrib clean")} if no longer needed`);
12968
+ tips.push(`Run ${import_picocolors15.default.bold("contrib sync")} to rebase on latest changes, or ${import_picocolors15.default.bold("contrib clean")} if no longer needed`);
13040
12969
  } else if (fileStatus.staged.length === 0 && fileStatus.modified.length === 0 && fileStatus.untracked.length === 0) {
13041
12970
  const branchDiv = await getDivergence(currentBranch, `${origin}/${currentBranch}`);
13042
12971
  if (branchDiv.ahead > 0) {
13043
- tips.push(`Run ${import_picocolors16.default.bold("contrib submit")} to push and create/update your PR`);
12972
+ tips.push(`Run ${import_picocolors15.default.bold("contrib submit")} to push and create/update your PR`);
13044
12973
  }
13045
12974
  }
13046
12975
  }
13047
12976
  if (tips.length > 0) {
13048
12977
  console.log();
13049
- console.log(` ${import_picocolors16.default.dim("\uD83D\uDCA1 Tip:")}`);
12978
+ console.log(` ${import_picocolors15.default.dim("\uD83D\uDCA1 Tip:")}`);
13050
12979
  for (const tip of tips) {
13051
- console.log(` ${import_picocolors16.default.dim(tip)}`);
12980
+ console.log(` ${import_picocolors15.default.dim(tip)}`);
13052
12981
  }
13053
12982
  }
13054
12983
  console.log();
13055
12984
  }
13056
12985
  });
13057
12986
  function formatStatus(branch, base, ahead, behind) {
13058
- const label = import_picocolors16.default.bold(branch.padEnd(20));
12987
+ const label = import_picocolors15.default.bold(branch.padEnd(20));
13059
12988
  if (ahead === 0 && behind === 0) {
13060
- return ` ${import_picocolors16.default.green("✓")} ${label} ${import_picocolors16.default.dim(`in sync with ${base}`)}`;
12989
+ return ` ${import_picocolors15.default.green("✓")} ${label} ${import_picocolors15.default.dim(`in sync with ${base}`)}`;
13061
12990
  }
13062
12991
  if (ahead > 0 && behind === 0) {
13063
- return ` ${import_picocolors16.default.yellow("↑")} ${label} ${import_picocolors16.default.yellow(`${ahead} commit${ahead !== 1 ? "s" : ""} ahead of ${base}`)}`;
12992
+ return ` ${import_picocolors15.default.yellow("↑")} ${label} ${import_picocolors15.default.yellow(`${ahead} commit${ahead !== 1 ? "s" : ""} ahead of ${base}`)}`;
13064
12993
  }
13065
12994
  if (behind > 0 && ahead === 0) {
13066
- return ` ${import_picocolors16.default.red("↓")} ${label} ${import_picocolors16.default.red(`${behind} commit${behind !== 1 ? "s" : ""} behind ${base}`)}`;
12995
+ return ` ${import_picocolors15.default.red("↓")} ${label} ${import_picocolors15.default.red(`${behind} commit${behind !== 1 ? "s" : ""} behind ${base}`)}`;
13067
12996
  }
13068
- return ` ${import_picocolors16.default.red("⚡")} ${label} ${import_picocolors16.default.yellow(`${ahead} ahead`)}${import_picocolors16.default.dim(", ")}${import_picocolors16.default.red(`${behind} behind`)} ${import_picocolors16.default.dim(base)}`;
12997
+ return ` ${import_picocolors15.default.red("⚡")} ${label} ${import_picocolors15.default.yellow(`${ahead} ahead`)}${import_picocolors15.default.dim(", ")}${import_picocolors15.default.red(`${behind} behind`)} ${import_picocolors15.default.dim(base)}`;
13069
12998
  }
13070
12999
  var STALE_THRESHOLD_DAYS = 14;
13071
13000
  async function detectBranchStatus(branch, baseBranch) {
@@ -13111,23 +13040,23 @@ async function detectBranchStatus(branch, baseBranch) {
13111
13040
  }
13112
13041
 
13113
13042
  // src/commands/submit.ts
13114
- var import_picocolors17 = __toESM(require_picocolors(), 1);
13043
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
13115
13044
  async function performSquashMerge(origin, baseBranch, featureBranch, options) {
13116
- info(`Checking out ${import_picocolors17.default.bold(baseBranch)}...`);
13045
+ info(`Checking out ${import_picocolors16.default.bold(baseBranch)}...`);
13117
13046
  const coResult = await checkoutBranch(baseBranch);
13118
13047
  if (coResult.exitCode !== 0) {
13119
13048
  error(`Failed to checkout ${baseBranch}: ${coResult.stderr}`);
13120
13049
  process.exit(1);
13121
13050
  }
13122
- info(`Squash merging ${import_picocolors17.default.bold(featureBranch)} into ${import_picocolors17.default.bold(baseBranch)}...`);
13051
+ info(`Squash merging ${import_picocolors16.default.bold(featureBranch)} into ${import_picocolors16.default.bold(baseBranch)}...`);
13123
13052
  const mergeResult = await mergeSquash(featureBranch);
13124
13053
  if (mergeResult.exitCode !== 0) {
13125
13054
  error(`Squash merge failed: ${mergeResult.stderr}`);
13126
13055
  process.exit(1);
13127
13056
  }
13128
13057
  let message = options?.defaultMsg;
13129
- if (!message && options?.useAI !== false) {
13130
- const copilotError = await checkCopilotAvailable2();
13058
+ if (!message) {
13059
+ const copilotError = await checkCopilotAvailable();
13131
13060
  if (!copilotError) {
13132
13061
  while (!message) {
13133
13062
  const spinner = createSpinner("Generating AI commit message for squash merge...");
@@ -13137,7 +13066,7 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
13137
13066
  message = aiMsg;
13138
13067
  spinner.success("AI commit message generated.");
13139
13068
  console.log(`
13140
- ${import_picocolors17.default.dim("AI suggestion:")} ${import_picocolors17.default.bold(import_picocolors17.default.cyan(message))}`);
13069
+ ${import_picocolors16.default.dim("AI suggestion:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(message))}`);
13141
13070
  break;
13142
13071
  }
13143
13072
  spinner.fail("AI did not return a commit message.");
@@ -13158,11 +13087,12 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
13158
13087
  let finalMsg = null;
13159
13088
  if (message) {
13160
13089
  while (!finalMsg) {
13161
- const actions = ["Accept this message", "Edit this message", "Write manually"];
13162
- if (options?.useAI !== false) {
13163
- actions.splice(2, 0, "Regenerate");
13164
- }
13165
- const action = await selectPrompt("What would you like to do?", actions);
13090
+ const action = await selectPrompt("What would you like to do?", [
13091
+ "Accept this message",
13092
+ "Edit this message",
13093
+ "Regenerate",
13094
+ "Write manually"
13095
+ ]);
13166
13096
  if (action === "Accept this message") {
13167
13097
  finalMsg = message;
13168
13098
  } else if (action === "Edit this message") {
@@ -13175,7 +13105,7 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
13175
13105
  message = regen;
13176
13106
  spinner.success("Commit message regenerated.");
13177
13107
  console.log(`
13178
- ${import_picocolors17.default.dim("AI suggestion:")} ${import_picocolors17.default.bold(import_picocolors17.default.cyan(regen))}`);
13108
+ ${import_picocolors16.default.dim("AI suggestion:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(regen))}`);
13179
13109
  } else {
13180
13110
  spinner.fail("Regeneration failed.");
13181
13111
  continue;
@@ -13192,13 +13122,13 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
13192
13122
  error(`Commit failed: ${commitResult.stderr}`);
13193
13123
  process.exit(1);
13194
13124
  }
13195
- info(`Pushing ${import_picocolors17.default.bold(baseBranch)} to ${origin}...`);
13125
+ info(`Pushing ${import_picocolors16.default.bold(baseBranch)} to ${origin}...`);
13196
13126
  const pushResult = await pushBranch(origin, baseBranch);
13197
13127
  if (pushResult.exitCode !== 0) {
13198
13128
  error(`Failed to push ${baseBranch}: ${pushResult.stderr}`);
13199
13129
  process.exit(1);
13200
13130
  }
13201
- info(`Deleting local branch ${import_picocolors17.default.bold(featureBranch)}...`);
13131
+ info(`Deleting local branch ${import_picocolors16.default.bold(featureBranch)}...`);
13202
13132
  const delLocal = await forceDeleteBranch(featureBranch);
13203
13133
  if (delLocal.exitCode !== 0) {
13204
13134
  warn(`Could not delete local branch: ${delLocal.stderr.trim()}`);
@@ -13206,14 +13136,14 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
13206
13136
  const remoteBranchRef = `${origin}/${featureBranch}`;
13207
13137
  const remoteExists = await branchExists(remoteBranchRef);
13208
13138
  if (remoteExists) {
13209
- info(`Deleting remote branch ${import_picocolors17.default.bold(featureBranch)}...`);
13139
+ info(`Deleting remote branch ${import_picocolors16.default.bold(featureBranch)}...`);
13210
13140
  const delRemote = await deleteRemoteBranch(origin, featureBranch);
13211
13141
  if (delRemote.exitCode !== 0) {
13212
13142
  warn(`Could not delete remote branch: ${delRemote.stderr.trim()}`);
13213
13143
  }
13214
13144
  }
13215
- success(`Squash merged ${import_picocolors17.default.bold(featureBranch)} into ${import_picocolors17.default.bold(baseBranch)} and pushed.`);
13216
- info(`Run ${import_picocolors17.default.bold("contrib start")} to begin a new feature.`, "");
13145
+ success(`Squash merged ${import_picocolors16.default.bold(featureBranch)} into ${import_picocolors16.default.bold(baseBranch)} and pushed.`);
13146
+ info(`Run ${import_picocolors16.default.bold("contrib start")} to begin a new feature.`, "");
13217
13147
  }
13218
13148
  var submit_default = defineCommand({
13219
13149
  meta: {
@@ -13260,7 +13190,6 @@ var submit_default = defineCommand({
13260
13190
  process.exit(1);
13261
13191
  }
13262
13192
  const { origin } = config;
13263
- const aiEnabled = isAIEnabled(config, args["no-ai"]);
13264
13193
  const baseBranch = getBaseBranch(config);
13265
13194
  const protectedBranches = getProtectedBranches(config);
13266
13195
  const currentBranch = await getCurrentBranch();
@@ -13269,8 +13198,8 @@ var submit_default = defineCommand({
13269
13198
  process.exit(1);
13270
13199
  }
13271
13200
  if (protectedBranches.includes(currentBranch)) {
13272
- projectHeading("submit", "\uD83D\uDE80");
13273
- warn(`You're on ${import_picocolors17.default.bold(currentBranch)}, which is a protected branch. PRs should come from feature branches.`);
13201
+ heading("\uD83D\uDE80 contrib submit");
13202
+ warn(`You're on ${import_picocolors16.default.bold(currentBranch)}, which is a protected branch. PRs should come from feature branches.`);
13274
13203
  await fetchAll();
13275
13204
  const remoteRef = `${origin}/${currentBranch}`;
13276
13205
  const localWork = await hasLocalWork(origin, currentBranch);
@@ -13279,11 +13208,11 @@ var submit_default = defineCommand({
13279
13208
  const hasAnything = hasCommits || dirty;
13280
13209
  if (!hasAnything) {
13281
13210
  error("No local changes or commits to move. Switch to a feature branch first.");
13282
- info(` Run ${import_picocolors17.default.bold("contrib start")} to create a new feature branch.`, "");
13211
+ info(` Run ${import_picocolors16.default.bold("contrib start")} to create a new feature branch.`, "");
13283
13212
  process.exit(1);
13284
13213
  }
13285
13214
  if (hasCommits) {
13286
- info(`Found ${import_picocolors17.default.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${import_picocolors17.default.bold(currentBranch)}.`);
13215
+ info(`Found ${import_picocolors16.default.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${import_picocolors16.default.bold(currentBranch)}.`);
13287
13216
  }
13288
13217
  if (dirty) {
13289
13218
  info("You also have uncommitted changes in the working tree.");
@@ -13299,35 +13228,58 @@ var submit_default = defineCommand({
13299
13228
  info("No changes made. You are still on your current branch.");
13300
13229
  return;
13301
13230
  }
13302
- const newBranchName = await promptForBranchName({
13303
- branchPrefixes: config.branchPrefixes,
13304
- useAI: aiEnabled,
13305
- model: args.model
13306
- });
13307
- if (!newBranchName) {
13308
- info("No changes made. You are still on your current branch.");
13309
- return;
13231
+ info(import_picocolors16.default.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
13232
+ const description = await inputPrompt("What are you going to work on?");
13233
+ let newBranchName = description;
13234
+ if (looksLikeNaturalLanguage(description)) {
13235
+ const copilotError = await checkCopilotAvailable();
13236
+ if (!copilotError) {
13237
+ const spinner = createSpinner("Generating branch name suggestion...");
13238
+ const suggested = await suggestBranchName(description, args.model);
13239
+ if (suggested) {
13240
+ spinner.success("Branch name suggestion ready.");
13241
+ console.log(`
13242
+ ${import_picocolors16.default.dim("AI suggestion:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(suggested))}`);
13243
+ const accepted = await confirmPrompt(`Use ${import_picocolors16.default.bold(suggested)} as your branch name?`);
13244
+ newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
13245
+ } else {
13246
+ spinner.fail("AI did not return a suggestion.");
13247
+ newBranchName = await inputPrompt("Enter branch name", description);
13248
+ }
13249
+ }
13250
+ }
13251
+ if (!hasPrefix(newBranchName, config.branchPrefixes)) {
13252
+ const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors16.default.bold(newBranchName)}:`, config.branchPrefixes);
13253
+ newBranchName = formatBranchName(prefix, newBranchName);
13254
+ }
13255
+ if (!isValidBranchName(newBranchName)) {
13256
+ error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
13257
+ process.exit(1);
13258
+ }
13259
+ if (await branchExists(newBranchName)) {
13260
+ error(`Branch ${import_picocolors16.default.bold(newBranchName)} already exists. Choose a different name.`);
13261
+ process.exit(1);
13310
13262
  }
13311
13263
  const branchResult = await createBranch(newBranchName);
13312
13264
  if (branchResult.exitCode !== 0) {
13313
13265
  error(`Failed to create branch: ${branchResult.stderr}`);
13314
13266
  process.exit(1);
13315
13267
  }
13316
- success(`Created ${import_picocolors17.default.bold(newBranchName)} with your changes.`);
13268
+ success(`Created ${import_picocolors16.default.bold(newBranchName)} with your changes.`);
13317
13269
  await updateLocalBranch(currentBranch, remoteRef);
13318
- info(`Reset ${import_picocolors17.default.bold(currentBranch)} back to ${import_picocolors17.default.bold(remoteRef)} — no damage done.`, "");
13270
+ info(`Reset ${import_picocolors16.default.bold(currentBranch)} back to ${import_picocolors16.default.bold(remoteRef)} — no damage done.`, "");
13319
13271
  console.log();
13320
- success(`You're now on ${import_picocolors17.default.bold(newBranchName)} with all your work intact.`);
13321
- info(`Run ${import_picocolors17.default.bold("contrib submit")} again to push and create your PR.`, "");
13272
+ success(`You're now on ${import_picocolors16.default.bold(newBranchName)} with all your work intact.`);
13273
+ info(`Run ${import_picocolors16.default.bold("contrib submit")} again to push and create your PR.`, "");
13322
13274
  return;
13323
13275
  }
13324
- projectHeading("submit", "\uD83D\uDE80");
13276
+ heading("\uD83D\uDE80 contrib submit");
13325
13277
  const ghInstalled = await checkGhInstalled();
13326
13278
  const ghAuthed = ghInstalled && await checkGhAuth();
13327
13279
  if (ghInstalled && ghAuthed) {
13328
13280
  const mergedPR = await getMergedPRForBranch(currentBranch);
13329
13281
  if (mergedPR) {
13330
- warn(`PR #${mergedPR.number} (${import_picocolors17.default.bold(mergedPR.title)}) was already merged.`);
13282
+ warn(`PR #${mergedPR.number} (${import_picocolors16.default.bold(mergedPR.title)}) was already merged.`);
13331
13283
  const localWork = await hasLocalWork(origin, currentBranch);
13332
13284
  const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
13333
13285
  if (hasWork) {
@@ -13335,7 +13287,7 @@ var submit_default = defineCommand({
13335
13287
  warn("You have uncommitted changes in your working tree.");
13336
13288
  }
13337
13289
  if (localWork.unpushedCommits > 0) {
13338
- warn(`You have ${import_picocolors17.default.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not in the merged PR.`);
13290
+ warn(`You have ${import_picocolors16.default.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not in the merged PR.`);
13339
13291
  }
13340
13292
  const SAVE_NEW_BRANCH = "Save changes to a new branch";
13341
13293
  const DISCARD = "Discard all changes and clean up";
@@ -13346,26 +13298,46 @@ var submit_default = defineCommand({
13346
13298
  return;
13347
13299
  }
13348
13300
  if (action === SAVE_NEW_BRANCH) {
13349
- const newBranchName = await promptForBranchName({
13350
- branchPrefixes: config.branchPrefixes,
13351
- useAI: aiEnabled,
13352
- model: args.model
13353
- });
13354
- if (!newBranchName) {
13355
- info("No changes made. You are still on your current branch.");
13356
- return;
13301
+ info(import_picocolors16.default.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
13302
+ const description = await inputPrompt("What are you going to work on?");
13303
+ let newBranchName = description;
13304
+ if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
13305
+ const spinner = createSpinner("Generating branch name suggestion...");
13306
+ const suggested = await suggestBranchName(description, args.model);
13307
+ if (suggested) {
13308
+ spinner.success("Branch name suggestion ready.");
13309
+ console.log(`
13310
+ ${import_picocolors16.default.dim("AI suggestion:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(suggested))}`);
13311
+ const accepted = await confirmPrompt(`Use ${import_picocolors16.default.bold(suggested)} as your branch name?`);
13312
+ newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
13313
+ } else {
13314
+ spinner.fail("AI did not return a suggestion.");
13315
+ newBranchName = await inputPrompt("Enter branch name", description);
13316
+ }
13317
+ }
13318
+ if (!hasPrefix(newBranchName, config.branchPrefixes)) {
13319
+ const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors16.default.bold(newBranchName)}:`, config.branchPrefixes);
13320
+ newBranchName = formatBranchName(prefix, newBranchName);
13321
+ }
13322
+ if (!isValidBranchName(newBranchName)) {
13323
+ error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
13324
+ process.exit(1);
13357
13325
  }
13358
13326
  const staleUpstream = await getUpstreamRef();
13359
13327
  const staleUpstreamHash = staleUpstream ? await getCommitHash(staleUpstream) : null;
13328
+ if (await branchExists(newBranchName)) {
13329
+ error(`Branch ${import_picocolors16.default.bold(newBranchName)} already exists. Choose a different name.`);
13330
+ process.exit(1);
13331
+ }
13360
13332
  const renameResult = await renameBranch(currentBranch, newBranchName);
13361
13333
  if (renameResult.exitCode !== 0) {
13362
13334
  error(`Failed to rename branch: ${renameResult.stderr}`);
13363
13335
  process.exit(1);
13364
13336
  }
13365
- success(`Renamed ${import_picocolors17.default.bold(currentBranch)} → ${import_picocolors17.default.bold(newBranchName)}`);
13337
+ success(`Renamed ${import_picocolors16.default.bold(currentBranch)} → ${import_picocolors16.default.bold(newBranchName)}`);
13366
13338
  await unsetUpstream();
13367
13339
  const syncSource2 = getSyncSource(config);
13368
- info(`Syncing ${import_picocolors17.default.bold(newBranchName)} with latest ${import_picocolors17.default.bold(baseBranch)}...`);
13340
+ info(`Syncing ${import_picocolors16.default.bold(newBranchName)} with latest ${import_picocolors16.default.bold(baseBranch)}...`);
13369
13341
  await fetchRemote(syncSource2.remote);
13370
13342
  let rebaseResult;
13371
13343
  if (staleUpstreamHash) {
@@ -13376,17 +13348,17 @@ var submit_default = defineCommand({
13376
13348
  }
13377
13349
  if (rebaseResult.exitCode !== 0) {
13378
13350
  warn("Rebase encountered conflicts. Resolve them manually, then run:");
13379
- info(` ${import_picocolors17.default.bold("git rebase --continue")}`, "");
13351
+ info(` ${import_picocolors16.default.bold("git rebase --continue")}`, "");
13380
13352
  } else {
13381
- success(`Rebased ${import_picocolors17.default.bold(newBranchName)} onto ${import_picocolors17.default.bold(syncSource2.ref)}.`);
13353
+ success(`Rebased ${import_picocolors16.default.bold(newBranchName)} onto ${import_picocolors16.default.bold(syncSource2.ref)}.`);
13382
13354
  }
13383
- info(`All your changes are preserved. Run ${import_picocolors17.default.bold("contrib submit")} when ready to create a new PR.`, "");
13355
+ info(`All your changes are preserved. Run ${import_picocolors16.default.bold("contrib submit")} when ready to create a new PR.`, "");
13384
13356
  return;
13385
13357
  }
13386
13358
  warn("Discarding local changes...");
13387
13359
  }
13388
13360
  const syncSource = getSyncSource(config);
13389
- info(`Switching to ${import_picocolors17.default.bold(baseBranch)} and syncing...`);
13361
+ info(`Switching to ${import_picocolors16.default.bold(baseBranch)} and syncing...`);
13390
13362
  await fetchRemote(syncSource.remote);
13391
13363
  await resetHard("HEAD");
13392
13364
  const coResult = await checkoutBranch(baseBranch);
@@ -13395,23 +13367,23 @@ var submit_default = defineCommand({
13395
13367
  process.exit(1);
13396
13368
  }
13397
13369
  await updateLocalBranch(baseBranch, syncSource.ref);
13398
- success(`Synced ${import_picocolors17.default.bold(baseBranch)} with ${import_picocolors17.default.bold(syncSource.ref)}.`);
13399
- info(`Deleting stale branch ${import_picocolors17.default.bold(currentBranch)}...`);
13370
+ success(`Synced ${import_picocolors16.default.bold(baseBranch)} with ${import_picocolors16.default.bold(syncSource.ref)}.`);
13371
+ info(`Deleting stale branch ${import_picocolors16.default.bold(currentBranch)}...`);
13400
13372
  const delResult = await forceDeleteBranch(currentBranch);
13401
13373
  if (delResult.exitCode === 0) {
13402
- success(`Deleted ${import_picocolors17.default.bold(currentBranch)}.`);
13374
+ success(`Deleted ${import_picocolors16.default.bold(currentBranch)}.`);
13403
13375
  } else {
13404
13376
  warn(`Could not delete branch: ${delResult.stderr.trim()}`);
13405
13377
  }
13406
13378
  console.log();
13407
- info(`You're now on ${import_picocolors17.default.bold(baseBranch)}. Run ${import_picocolors17.default.bold("contrib start")} to begin a new feature.`);
13379
+ info(`You're now on ${import_picocolors16.default.bold(baseBranch)}. Run ${import_picocolors16.default.bold("contrib start")} to begin a new feature.`);
13408
13380
  return;
13409
13381
  }
13410
13382
  }
13411
13383
  if (ghInstalled && ghAuthed) {
13412
13384
  const existingPR = await getPRForBranch(currentBranch);
13413
13385
  if (existingPR) {
13414
- info(`Pushing ${import_picocolors17.default.bold(currentBranch)} to ${origin}...`);
13386
+ info(`Pushing ${import_picocolors16.default.bold(currentBranch)} to ${origin}...`);
13415
13387
  const pushResult2 = await pushSetUpstream(origin, currentBranch);
13416
13388
  if (pushResult2.exitCode !== 0) {
13417
13389
  error(`Failed to push: ${pushResult2.stderr}`);
@@ -13422,8 +13394,8 @@ var submit_default = defineCommand({
13422
13394
  }
13423
13395
  process.exit(1);
13424
13396
  }
13425
- success(`Pushed changes to existing PR #${existingPR.number}: ${import_picocolors17.default.bold(existingPR.title)}`);
13426
- console.log(` ${import_picocolors17.default.cyan(existingPR.url)}`);
13397
+ success(`Pushed changes to existing PR #${existingPR.number}: ${import_picocolors16.default.bold(existingPR.title)}`);
13398
+ console.log(` ${import_picocolors16.default.cyan(existingPR.url)}`);
13427
13399
  return;
13428
13400
  }
13429
13401
  }
@@ -13431,7 +13403,7 @@ var submit_default = defineCommand({
13431
13403
  let prBody = null;
13432
13404
  async function tryGenerateAI() {
13433
13405
  const [copilotError, commits, diff] = await Promise.all([
13434
- checkCopilotAvailable2(),
13406
+ checkCopilotAvailable(),
13435
13407
  getLog(baseBranch, "HEAD"),
13436
13408
  getLogDiff(baseBranch, "HEAD")
13437
13409
  ]);
@@ -13443,10 +13415,10 @@ var submit_default = defineCommand({
13443
13415
  prBody = result.body;
13444
13416
  spinner.success("PR description generated.");
13445
13417
  console.log(`
13446
- ${import_picocolors17.default.dim("AI title:")} ${import_picocolors17.default.bold(import_picocolors17.default.cyan(prTitle))}`);
13418
+ ${import_picocolors16.default.dim("AI title:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(prTitle))}`);
13447
13419
  console.log(`
13448
- ${import_picocolors17.default.dim("AI body preview:")}`);
13449
- console.log(import_picocolors17.default.dim(prBody.slice(0, 300) + (prBody.length > 300 ? "..." : "")));
13420
+ ${import_picocolors16.default.dim("AI body preview:")}`);
13421
+ console.log(import_picocolors16.default.dim(prBody.slice(0, 300) + (prBody.length > 300 ? "..." : "")));
13450
13422
  } else {
13451
13423
  spinner.fail("AI did not return a PR description.");
13452
13424
  }
@@ -13470,8 +13442,7 @@ ${import_picocolors17.default.dim("AI body preview:")}`);
13470
13442
  if (args.local) {
13471
13443
  await performSquashMerge(origin, baseBranch, currentBranch, {
13472
13444
  model: args.model,
13473
- convention: config.commitConvention,
13474
- useAI: aiEnabled
13445
+ convention: config.commitConvention
13475
13446
  });
13476
13447
  return;
13477
13448
  }
@@ -13484,13 +13455,12 @@ ${import_picocolors17.default.dim("AI body preview:")}`);
13484
13455
  if (maintainerChoice === SQUASH_LOCAL) {
13485
13456
  await performSquashMerge(origin, baseBranch, currentBranch, {
13486
13457
  model: args.model,
13487
- convention: config.commitConvention,
13488
- useAI: aiEnabled
13458
+ convention: config.commitConvention
13489
13459
  });
13490
13460
  return;
13491
13461
  }
13492
13462
  }
13493
- if (aiEnabled) {
13463
+ if (!args["no-ai"]) {
13494
13464
  await tryGenerateAI();
13495
13465
  }
13496
13466
  let actionResolved = false;
@@ -13529,7 +13499,7 @@ ${import_picocolors17.default.dim("AI body preview:")}`);
13529
13499
  }
13530
13500
  } else {
13531
13501
  const choices = [];
13532
- if (aiEnabled)
13502
+ if (!args["no-ai"])
13533
13503
  choices.push(REGENERATE);
13534
13504
  choices.push("Write title & body manually", "Use gh --fill (auto-fill from commits)", CANCEL);
13535
13505
  const action = await selectPrompt("How would you like to create the PR?", choices);
@@ -13553,7 +13523,7 @@ ${import_picocolors17.default.dim("AI body preview:")}`);
13553
13523
  warn("Submit cancelled.");
13554
13524
  return;
13555
13525
  }
13556
- info(`Pushing ${import_picocolors17.default.bold(currentBranch)} to ${origin}...`);
13526
+ info(`Pushing ${import_picocolors16.default.bold(currentBranch)} to ${origin}...`);
13557
13527
  const pushResult = await pushSetUpstream(origin, currentBranch);
13558
13528
  if (pushResult.exitCode !== 0) {
13559
13529
  error(`Failed to push: ${pushResult.stderr}`);
@@ -13572,7 +13542,7 @@ ${import_picocolors17.default.dim("AI body preview:")}`);
13572
13542
  const prUrl = `https://github.com/${repoInfo.owner}/${repoInfo.repo}/compare/${baseBranch}...${currentBranch}?expand=1`;
13573
13543
  console.log();
13574
13544
  info("Create your PR manually:", "");
13575
- console.log(` ${import_picocolors17.default.cyan(prUrl)}`);
13545
+ console.log(` ${import_picocolors16.default.cyan(prUrl)}`);
13576
13546
  } else {
13577
13547
  info("gh CLI not available. Create your PR manually on GitHub.", "");
13578
13548
  }
@@ -13606,7 +13576,7 @@ ${import_picocolors17.default.dim("AI body preview:")}`);
13606
13576
  });
13607
13577
 
13608
13578
  // src/commands/switch.ts
13609
- var import_picocolors18 = __toESM(require_picocolors(), 1);
13579
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
13610
13580
  var switch_default = defineCommand({
13611
13581
  meta: {
13612
13582
  name: "switch",
@@ -13627,7 +13597,7 @@ var switch_default = defineCommand({
13627
13597
  const config = readConfig();
13628
13598
  const protectedBranches = config ? getProtectedBranches(config) : ["main", "master"];
13629
13599
  const currentBranch = await getCurrentBranch();
13630
- projectHeading("switch", "\uD83D\uDD00");
13600
+ heading("\uD83D\uDD00 contrib switch");
13631
13601
  let targetBranch = args.name;
13632
13602
  if (!targetBranch) {
13633
13603
  const localBranches = await getLocalBranches();
@@ -13638,11 +13608,11 @@ var switch_default = defineCommand({
13638
13608
  const choices = localBranches.filter((b2) => b2.name !== currentBranch).map((b2) => {
13639
13609
  const labels = [];
13640
13610
  if (protectedBranches.includes(b2.name))
13641
- labels.push(import_picocolors18.default.red("protected"));
13611
+ labels.push(import_picocolors17.default.red("protected"));
13642
13612
  if (b2.upstream)
13643
- labels.push(import_picocolors18.default.dim(`→ ${b2.upstream}`));
13613
+ labels.push(import_picocolors17.default.dim(`→ ${b2.upstream}`));
13644
13614
  if (b2.gone)
13645
- labels.push(import_picocolors18.default.red("remote gone"));
13615
+ labels.push(import_picocolors17.default.red("remote gone"));
13646
13616
  const suffix = labels.length > 0 ? ` ${labels.join(" · ")}` : "";
13647
13617
  return `${b2.name}${suffix}`;
13648
13618
  });
@@ -13654,7 +13624,7 @@ var switch_default = defineCommand({
13654
13624
  targetBranch = selected.split(/\s{2,}/)[0].trim();
13655
13625
  }
13656
13626
  if (targetBranch === currentBranch) {
13657
- info(`Already on ${import_picocolors18.default.bold(targetBranch)}.`);
13627
+ info(`Already on ${import_picocolors17.default.bold(targetBranch)}.`);
13658
13628
  return;
13659
13629
  }
13660
13630
  if (await hasUncommittedChanges()) {
@@ -13673,7 +13643,7 @@ var switch_default = defineCommand({
13673
13643
  const stashMsg = `contrib-save: auto-save from ${currentBranch}`;
13674
13644
  try {
13675
13645
  await exec("git", ["stash", "push", "-m", stashMsg]);
13676
- info(`Saved changes: ${import_picocolors18.default.dim(stashMsg)}`);
13646
+ info(`Saved changes: ${import_picocolors17.default.dim(stashMsg)}`);
13677
13647
  } catch {
13678
13648
  error("Failed to save changes. Please commit or save manually.");
13679
13649
  process.exit(1);
@@ -13689,9 +13659,9 @@ var switch_default = defineCommand({
13689
13659
  }
13690
13660
  process.exit(1);
13691
13661
  }
13692
- success(`Switched to ${import_picocolors18.default.bold(targetBranch)}`);
13693
- info(`Your changes from ${import_picocolors18.default.bold(currentBranch ?? "previous branch")} are saved.`, "");
13694
- info(`Use ${import_picocolors18.default.bold("contrib save --restore")} to bring them back.`, "");
13662
+ success(`Switched to ${import_picocolors17.default.bold(targetBranch)}`);
13663
+ info(`Your changes from ${import_picocolors17.default.bold(currentBranch ?? "previous branch")} are saved.`, "");
13664
+ info(`Use ${import_picocolors17.default.bold("contrib save --restore")} to bring them back.`, "");
13695
13665
  return;
13696
13666
  }
13697
13667
  const result = await checkoutBranch(targetBranch);
@@ -13699,12 +13669,12 @@ var switch_default = defineCommand({
13699
13669
  error(`Failed to switch to ${targetBranch}: ${result.stderr}`);
13700
13670
  process.exit(1);
13701
13671
  }
13702
- success(`Switched to ${import_picocolors18.default.bold(targetBranch)}`);
13672
+ success(`Switched to ${import_picocolors17.default.bold(targetBranch)}`);
13703
13673
  }
13704
13674
  });
13705
13675
 
13706
13676
  // src/commands/sync.ts
13707
- var import_picocolors19 = __toESM(require_picocolors(), 1);
13677
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
13708
13678
  var sync_default = defineCommand({
13709
13679
  meta: {
13710
13680
  name: "sync",
@@ -13743,7 +13713,7 @@ var sync_default = defineCommand({
13743
13713
  error("You have uncommitted changes. Please commit or stash them before syncing.");
13744
13714
  process.exit(1);
13745
13715
  }
13746
- projectHeading(`sync (${workflow}, ${role})`, "\uD83D\uDD04");
13716
+ heading(`\uD83D\uDD04 contrib sync (${workflow}, ${role})`);
13747
13717
  const baseBranch = getBaseBranch(config);
13748
13718
  const syncSource = getSyncSource(config);
13749
13719
  info(`Fetching ${syncSource.remote}...`);
@@ -13756,24 +13726,24 @@ var sync_default = defineCommand({
13756
13726
  await fetchRemote(origin);
13757
13727
  }
13758
13728
  if (!await refExists(syncSource.ref)) {
13759
- error(`Remote ref ${import_picocolors19.default.bold(syncSource.ref)} does not exist.`);
13729
+ error(`Remote ref ${import_picocolors18.default.bold(syncSource.ref)} does not exist.`);
13760
13730
  info("This can happen if the branch was renamed or deleted on the remote.", "");
13761
- info(`Check your config: the base branch may need updating via ${import_picocolors19.default.bold("contrib setup")}.`, "");
13731
+ info(`Check your config: the base branch may need updating via ${import_picocolors18.default.bold("contrib setup")}.`, "");
13762
13732
  process.exit(1);
13763
13733
  }
13764
13734
  let allowMergeCommit = false;
13765
13735
  const div = await getDivergence(baseBranch, syncSource.ref);
13766
13736
  if (div.ahead > 0 || div.behind > 0) {
13767
- info(`${import_picocolors19.default.bold(baseBranch)} is ${import_picocolors19.default.yellow(`${div.ahead} ahead`)} and ${import_picocolors19.default.red(`${div.behind} behind`)} ${syncSource.ref}`);
13737
+ info(`${import_picocolors18.default.bold(baseBranch)} is ${import_picocolors18.default.yellow(`${div.ahead} ahead`)} and ${import_picocolors18.default.red(`${div.behind} behind`)} ${syncSource.ref}`);
13768
13738
  } else {
13769
- info(`${import_picocolors19.default.bold(baseBranch)} is already in sync with ${syncSource.ref}`);
13739
+ info(`${import_picocolors18.default.bold(baseBranch)} is already in sync with ${syncSource.ref}`);
13770
13740
  }
13771
13741
  if (div.ahead > 0) {
13772
13742
  const currentBranch = await getCurrentBranch();
13773
13743
  const protectedBranches = getProtectedBranches(config);
13774
13744
  const isOnProtected = currentBranch && protectedBranches.includes(currentBranch);
13775
13745
  if (isOnProtected) {
13776
- warn(`You have ${import_picocolors19.default.bold(String(div.ahead))} local commit${div.ahead !== 1 ? "s" : ""} on ${import_picocolors19.default.bold(baseBranch)} that aren't on the remote.`);
13746
+ warn(`You have ${import_picocolors18.default.bold(String(div.ahead))} local commit${div.ahead !== 1 ? "s" : ""} on ${import_picocolors18.default.bold(baseBranch)} that aren't on the remote.`);
13777
13747
  info("Pulling now could create a merge commit, which breaks clean history.");
13778
13748
  console.log();
13779
13749
  const MOVE_BRANCH = "Move my commits to a new feature branch, then sync";
@@ -13789,21 +13759,44 @@ var sync_default = defineCommand({
13789
13759
  return;
13790
13760
  }
13791
13761
  if (action === MOVE_BRANCH) {
13792
- const newBranchName = await promptForBranchName({
13793
- branchPrefixes: config.branchPrefixes,
13794
- useAI: isAIEnabled(config, args["no-ai"]),
13795
- model: args.model
13796
- });
13797
- if (!newBranchName) {
13798
- info("No changes made.");
13799
- return;
13762
+ info(import_picocolors18.default.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
13763
+ const description = await inputPrompt("What are you going to work on?");
13764
+ let newBranchName = description;
13765
+ if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
13766
+ const copilotError = await checkCopilotAvailable();
13767
+ if (!copilotError) {
13768
+ const spinner = createSpinner("Generating branch name suggestion...");
13769
+ const suggested = await suggestBranchName(description, args.model);
13770
+ if (suggested) {
13771
+ spinner.success("Branch name suggestion ready.");
13772
+ console.log(`
13773
+ ${import_picocolors18.default.dim("AI suggestion:")} ${import_picocolors18.default.bold(import_picocolors18.default.cyan(suggested))}`);
13774
+ const accepted = await confirmPrompt(`Use ${import_picocolors18.default.bold(suggested)} as your branch name?`);
13775
+ newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
13776
+ } else {
13777
+ spinner.fail("AI did not return a suggestion.");
13778
+ newBranchName = await inputPrompt("Enter branch name", description);
13779
+ }
13780
+ }
13781
+ }
13782
+ if (!hasPrefix(newBranchName, config.branchPrefixes)) {
13783
+ const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors18.default.bold(newBranchName)}:`, config.branchPrefixes);
13784
+ newBranchName = formatBranchName(prefix, newBranchName);
13785
+ }
13786
+ if (!isValidBranchName(newBranchName)) {
13787
+ error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
13788
+ process.exit(1);
13789
+ }
13790
+ if (await branchExists(newBranchName)) {
13791
+ error(`Branch ${import_picocolors18.default.bold(newBranchName)} already exists. Choose a different name.`);
13792
+ process.exit(1);
13800
13793
  }
13801
13794
  const branchResult = await createBranch(newBranchName);
13802
13795
  if (branchResult.exitCode !== 0) {
13803
13796
  error(`Failed to create branch: ${branchResult.stderr}`);
13804
13797
  process.exit(1);
13805
13798
  }
13806
- success(`Created ${import_picocolors19.default.bold(newBranchName)} with your commits.`);
13799
+ success(`Created ${import_picocolors18.default.bold(newBranchName)} with your commits.`);
13807
13800
  const coResult2 = await checkoutBranch(baseBranch);
13808
13801
  if (coResult2.exitCode !== 0) {
13809
13802
  error(`Failed to checkout ${baseBranch}: ${coResult2.stderr}`);
@@ -13811,11 +13804,11 @@ var sync_default = defineCommand({
13811
13804
  }
13812
13805
  const remoteRef = syncSource.ref;
13813
13806
  await updateLocalBranch(baseBranch, remoteRef);
13814
- success(`Reset ${import_picocolors19.default.bold(baseBranch)} to ${import_picocolors19.default.bold(remoteRef)}.`);
13815
- success(`${import_picocolors19.default.bold(baseBranch)} is now in sync with ${syncSource.ref}`);
13807
+ success(`Reset ${import_picocolors18.default.bold(baseBranch)} to ${import_picocolors18.default.bold(remoteRef)}.`);
13808
+ success(`${import_picocolors18.default.bold(baseBranch)} is now in sync with ${syncSource.ref}`);
13816
13809
  console.log();
13817
- info(`Your commits are safe on ${import_picocolors19.default.bold(newBranchName)}.`, "");
13818
- info(`Run ${import_picocolors19.default.bold(`git checkout ${newBranchName}`)} then ${import_picocolors19.default.bold("contrib update")} to rebase onto the synced ${import_picocolors19.default.bold(baseBranch)}.`, "");
13810
+ info(`Your commits are safe on ${import_picocolors18.default.bold(newBranchName)}.`, "");
13811
+ info(`Run ${import_picocolors18.default.bold(`git checkout ${newBranchName}`)} then ${import_picocolors18.default.bold("contrib update")} to rebase onto the synced ${import_picocolors18.default.bold(baseBranch)}.`, "");
13819
13812
  return;
13820
13813
  }
13821
13814
  allowMergeCommit = true;
@@ -13823,7 +13816,7 @@ var sync_default = defineCommand({
13823
13816
  }
13824
13817
  }
13825
13818
  if (!args.yes) {
13826
- const ok = await confirmPrompt(`This will pull ${import_picocolors19.default.bold(syncSource.ref)} into local ${import_picocolors19.default.bold(baseBranch)}.`);
13819
+ const ok = await confirmPrompt(`This will pull ${import_picocolors18.default.bold(syncSource.ref)} into local ${import_picocolors18.default.bold(baseBranch)}.`);
13827
13820
  if (!ok)
13828
13821
  process.exit(0);
13829
13822
  }
@@ -13837,8 +13830,8 @@ var sync_default = defineCommand({
13837
13830
  if (allowMergeCommit) {
13838
13831
  error(`Pull failed: ${pullResult.stderr.trim()}`);
13839
13832
  } else {
13840
- error(`Fast-forward pull failed. Your local ${import_picocolors19.default.bold(baseBranch)} may have diverged.`);
13841
- info(`Use ${import_picocolors19.default.bold("contrib sync")} again and choose "Move my commits to a new feature branch" to fix this.`, "");
13833
+ error(`Fast-forward pull failed. Your local ${import_picocolors18.default.bold(baseBranch)} may have diverged.`);
13834
+ info(`Use ${import_picocolors18.default.bold("contrib sync")} again and choose "Move my commits to a new feature branch" to fix this.`, "");
13842
13835
  }
13843
13836
  process.exit(1);
13844
13837
  }
@@ -13846,7 +13839,7 @@ var sync_default = defineCommand({
13846
13839
  if (hasDevBranch(workflow) && role === "maintainer") {
13847
13840
  const mainDiv = await getDivergence(config.mainBranch, `${origin}/${config.mainBranch}`);
13848
13841
  if (mainDiv.behind > 0) {
13849
- info(`Also syncing ${import_picocolors19.default.bold(config.mainBranch)}...`);
13842
+ info(`Also syncing ${import_picocolors18.default.bold(config.mainBranch)}...`);
13850
13843
  const mainCoResult = await checkoutBranch(config.mainBranch);
13851
13844
  if (mainCoResult.exitCode === 0) {
13852
13845
  const mainPullResult = await pullFastForwardOnly(origin, config.mainBranch);
@@ -13884,20 +13877,20 @@ var sync_default = defineCommand({
13884
13877
  groups.get(hash).push(name);
13885
13878
  }
13886
13879
  console.log();
13887
- console.log(` ${import_picocolors19.default.bold("\uD83D\uDD17 Branch Alignment")}`);
13880
+ console.log(` ${import_picocolors18.default.bold("\uD83D\uDD17 Branch Alignment")}`);
13888
13881
  for (const [hash, names] of groups) {
13889
13882
  const short = hash.slice(0, 7);
13890
- const nameStr = names.map((n2) => import_picocolors19.default.bold(n2)).join(import_picocolors19.default.dim(" · "));
13891
- console.log(` ${import_picocolors19.default.yellow(short)} ${import_picocolors19.default.dim("──")} ${nameStr}`);
13883
+ const nameStr = names.map((n2) => import_picocolors18.default.bold(n2)).join(import_picocolors18.default.dim(" · "));
13884
+ console.log(` ${import_picocolors18.default.yellow(short)} ${import_picocolors18.default.dim("──")} ${nameStr}`);
13892
13885
  const subject = await getCommitSubject(hash);
13893
13886
  if (subject) {
13894
- console.log(` ${import_picocolors19.default.dim(subject)}`);
13887
+ console.log(` ${import_picocolors18.default.dim(subject)}`);
13895
13888
  }
13896
13889
  }
13897
13890
  if (groups.size === 1) {
13898
- console.log(` ${import_picocolors19.default.green("✓")} ${import_picocolors19.default.green("All branches aligned")} ${import_picocolors19.default.dim("— ready to start")}`);
13891
+ console.log(` ${import_picocolors18.default.green("✓")} ${import_picocolors18.default.green("All branches aligned")} ${import_picocolors18.default.dim("— ready to start")}`);
13899
13892
  } else {
13900
- console.log(` ${import_picocolors19.default.yellow("⚠")} ${import_picocolors19.default.yellow("Branches are not fully aligned")}`);
13893
+ console.log(` ${import_picocolors18.default.yellow("⚠")} ${import_picocolors18.default.yellow("Branches are not fully aligned")}`);
13901
13894
  }
13902
13895
  }
13903
13896
  }
@@ -13906,7 +13899,7 @@ var sync_default = defineCommand({
13906
13899
 
13907
13900
  // src/commands/update.ts
13908
13901
  import { readFileSync as readFileSync4 } from "node:fs";
13909
- var import_picocolors20 = __toESM(require_picocolors(), 1);
13902
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
13910
13903
  var update_default = defineCommand({
13911
13904
  meta: {
13912
13905
  name: "update",
@@ -13943,8 +13936,8 @@ var update_default = defineCommand({
13943
13936
  process.exit(1);
13944
13937
  }
13945
13938
  if (protectedBranches.includes(currentBranch)) {
13946
- projectHeading("update", "\uD83D\uDD03");
13947
- warn(`You're on ${import_picocolors20.default.bold(currentBranch)}, which is a protected branch. Updates (rebase) apply to feature branches.`);
13939
+ heading("\uD83D\uDD03 contrib update");
13940
+ warn(`You're on ${import_picocolors19.default.bold(currentBranch)}, which is a protected branch. Updates (rebase) apply to feature branches.`);
13948
13941
  await fetchAll();
13949
13942
  const { origin } = config;
13950
13943
  const remoteRef = `${origin}/${currentBranch}`;
@@ -13953,12 +13946,12 @@ var update_default = defineCommand({
13953
13946
  const hasCommits = localWork.unpushedCommits > 0;
13954
13947
  const hasAnything = hasCommits || dirty;
13955
13948
  if (!hasAnything) {
13956
- info(`No local changes found on ${import_picocolors20.default.bold(currentBranch)}.`);
13957
- info(`Use ${import_picocolors20.default.bold("contrib sync")} to sync protected branches, or ${import_picocolors20.default.bold("contrib start")} to create a feature branch.`);
13949
+ info(`No local changes found on ${import_picocolors19.default.bold(currentBranch)}.`);
13950
+ info(`Use ${import_picocolors19.default.bold("contrib sync")} to sync protected branches, or ${import_picocolors19.default.bold("contrib start")} to create a feature branch.`);
13958
13951
  process.exit(1);
13959
13952
  }
13960
13953
  if (hasCommits) {
13961
- info(`Found ${import_picocolors20.default.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${import_picocolors20.default.bold(currentBranch)}.`);
13954
+ info(`Found ${import_picocolors19.default.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${import_picocolors19.default.bold(currentBranch)}.`);
13962
13955
  }
13963
13956
  if (dirty) {
13964
13957
  info("You also have uncommitted changes in the working tree.");
@@ -13974,37 +13967,56 @@ var update_default = defineCommand({
13974
13967
  info("No changes made. You are still on your current branch.");
13975
13968
  return;
13976
13969
  }
13977
- const newBranchName = await promptForBranchName({
13978
- branchPrefixes: config.branchPrefixes,
13979
- useAI: isAIEnabled(config, args["no-ai"]),
13980
- model: args.model
13981
- });
13982
- if (!newBranchName) {
13983
- info("No changes made. You are still on your current branch.");
13984
- return;
13970
+ info(import_picocolors19.default.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
13971
+ const description = await inputPrompt("What are you going to work on?");
13972
+ let newBranchName = description;
13973
+ if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
13974
+ const copilotError = await checkCopilotAvailable();
13975
+ if (!copilotError) {
13976
+ const spinner = createSpinner("Generating branch name suggestion...");
13977
+ const suggested = await suggestBranchName(description, args.model);
13978
+ if (suggested) {
13979
+ spinner.success("Branch name suggestion ready.");
13980
+ console.log(`
13981
+ ${import_picocolors19.default.dim("AI suggestion:")} ${import_picocolors19.default.bold(import_picocolors19.default.cyan(suggested))}`);
13982
+ const accepted = await confirmPrompt(`Use ${import_picocolors19.default.bold(suggested)} as your branch name?`);
13983
+ newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
13984
+ } else {
13985
+ spinner.fail("AI did not return a suggestion.");
13986
+ newBranchName = await inputPrompt("Enter branch name", description);
13987
+ }
13988
+ }
13989
+ }
13990
+ if (!hasPrefix(newBranchName, config.branchPrefixes)) {
13991
+ const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors19.default.bold(newBranchName)}:`, config.branchPrefixes);
13992
+ newBranchName = formatBranchName(prefix, newBranchName);
13993
+ }
13994
+ if (!isValidBranchName(newBranchName)) {
13995
+ error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
13996
+ process.exit(1);
13985
13997
  }
13986
13998
  const branchResult = await createBranch(newBranchName);
13987
13999
  if (branchResult.exitCode !== 0) {
13988
14000
  error(`Failed to create branch: ${branchResult.stderr}`);
13989
14001
  process.exit(1);
13990
14002
  }
13991
- success(`Created ${import_picocolors20.default.bold(newBranchName)} with your changes.`);
14003
+ success(`Created ${import_picocolors19.default.bold(newBranchName)} with your changes.`);
13992
14004
  await updateLocalBranch(currentBranch, remoteRef);
13993
- info(`Reset ${import_picocolors20.default.bold(currentBranch)} back to ${import_picocolors20.default.bold(remoteRef)} — no damage done.`, "");
14005
+ info(`Reset ${import_picocolors19.default.bold(currentBranch)} back to ${import_picocolors19.default.bold(remoteRef)} — no damage done.`, "");
13994
14006
  console.log();
13995
- success(`You're now on ${import_picocolors20.default.bold(newBranchName)} with all your work intact.`);
13996
- info(`Run ${import_picocolors20.default.bold("contrib update")} again to rebase onto latest ${import_picocolors20.default.bold(baseBranch)}.`, "");
14007
+ success(`You're now on ${import_picocolors19.default.bold(newBranchName)} with all your work intact.`);
14008
+ info(`Run ${import_picocolors19.default.bold("contrib update")} again to rebase onto latest ${import_picocolors19.default.bold(baseBranch)}.`, "");
13997
14009
  return;
13998
14010
  }
13999
14011
  if (await hasUncommittedChanges()) {
14000
14012
  error("You have uncommitted changes. Please commit or stash them first.");
14001
14013
  process.exit(1);
14002
14014
  }
14003
- projectHeading("update", "\uD83D\uDD03");
14015
+ heading("\uD83D\uDD03 contrib update");
14004
14016
  const mergedPR = await getMergedPRForBranch(currentBranch);
14005
14017
  if (mergedPR) {
14006
- warn(`PR #${mergedPR.number} (${import_picocolors20.default.bold(mergedPR.title)}) has already been merged.`);
14007
- info(`Link: ${import_picocolors20.default.underline(mergedPR.url)}`, "");
14018
+ warn(`PR #${mergedPR.number} (${import_picocolors19.default.bold(mergedPR.title)}) has already been merged.`);
14019
+ info(`Link: ${import_picocolors19.default.underline(mergedPR.url)}`, "");
14008
14020
  const localWork = await hasLocalWork(syncSource.remote, currentBranch);
14009
14021
  const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
14010
14022
  if (hasWork) {
@@ -14017,29 +14029,49 @@ var update_default = defineCommand({
14017
14029
  const SAVE_NEW_BRANCH = "Save changes to a new branch";
14018
14030
  const DISCARD = "Discard all changes and clean up";
14019
14031
  const CANCEL = "Cancel";
14020
- const action = await selectPrompt(`${import_picocolors20.default.bold(currentBranch)} is stale but has local work. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
14032
+ const action = await selectPrompt(`${import_picocolors19.default.bold(currentBranch)} is stale but has local work. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
14021
14033
  if (action === CANCEL) {
14022
14034
  info("No changes made. You are still on your current branch.");
14023
14035
  return;
14024
14036
  }
14025
14037
  if (action === SAVE_NEW_BRANCH) {
14026
- const newBranchName = await promptForBranchName({
14027
- branchPrefixes: config.branchPrefixes,
14028
- useAI: isAIEnabled(config, args["no-ai"]),
14029
- model: args.model
14030
- });
14031
- if (!newBranchName) {
14032
- info("No changes made. You are still on your current branch.");
14033
- return;
14038
+ info(import_picocolors19.default.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
14039
+ const description = await inputPrompt("What are you going to work on?");
14040
+ let newBranchName = description;
14041
+ if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
14042
+ const spinner = createSpinner("Generating branch name suggestion...");
14043
+ const suggested = await suggestBranchName(description, args.model);
14044
+ if (suggested) {
14045
+ spinner.success("Branch name suggestion ready.");
14046
+ console.log(`
14047
+ ${import_picocolors19.default.dim("AI suggestion:")} ${import_picocolors19.default.bold(import_picocolors19.default.cyan(suggested))}`);
14048
+ const accepted = await confirmPrompt(`Use ${import_picocolors19.default.bold(suggested)} as your branch name?`);
14049
+ newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
14050
+ } else {
14051
+ spinner.fail("AI did not return a suggestion.");
14052
+ newBranchName = await inputPrompt("Enter branch name", description);
14053
+ }
14054
+ }
14055
+ if (!hasPrefix(newBranchName, config.branchPrefixes)) {
14056
+ const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors19.default.bold(newBranchName)}:`, config.branchPrefixes);
14057
+ newBranchName = formatBranchName(prefix, newBranchName);
14058
+ }
14059
+ if (!isValidBranchName(newBranchName)) {
14060
+ error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
14061
+ process.exit(1);
14034
14062
  }
14035
14063
  const staleUpstream = await getUpstreamRef();
14036
14064
  const staleUpstreamHash = staleUpstream ? await getCommitHash(staleUpstream) : null;
14065
+ if (await branchExists(newBranchName)) {
14066
+ error(`Branch ${import_picocolors19.default.bold(newBranchName)} already exists. Choose a different name.`);
14067
+ process.exit(1);
14068
+ }
14037
14069
  const renameResult = await renameBranch(currentBranch, newBranchName);
14038
14070
  if (renameResult.exitCode !== 0) {
14039
14071
  error(`Failed to rename branch: ${renameResult.stderr}`);
14040
14072
  process.exit(1);
14041
14073
  }
14042
- success(`Renamed ${import_picocolors20.default.bold(currentBranch)} → ${import_picocolors20.default.bold(newBranchName)}`);
14074
+ success(`Renamed ${import_picocolors19.default.bold(currentBranch)} → ${import_picocolors19.default.bold(newBranchName)}`);
14043
14075
  await unsetUpstream();
14044
14076
  await fetchRemote(syncSource.remote);
14045
14077
  let rebaseResult2;
@@ -14051,11 +14083,11 @@ var update_default = defineCommand({
14051
14083
  }
14052
14084
  if (rebaseResult2.exitCode !== 0) {
14053
14085
  warn("Rebase encountered conflicts. Resolve them manually, then run:");
14054
- info(` ${import_picocolors20.default.bold("git rebase --continue")}`, "");
14086
+ info(` ${import_picocolors19.default.bold("git rebase --continue")}`, "");
14055
14087
  } else {
14056
- success(`Rebased ${import_picocolors20.default.bold(newBranchName)} onto ${import_picocolors20.default.bold(syncSource.ref)}.`);
14088
+ success(`Rebased ${import_picocolors19.default.bold(newBranchName)} onto ${import_picocolors19.default.bold(syncSource.ref)}.`);
14057
14089
  }
14058
- info(`All your changes are preserved. Run ${import_picocolors20.default.bold("contrib submit")} when ready to create a new PR.`, "");
14090
+ info(`All your changes are preserved. Run ${import_picocolors19.default.bold("contrib submit")} when ready to create a new PR.`, "");
14059
14091
  return;
14060
14092
  }
14061
14093
  warn("Discarding local changes...");
@@ -14068,30 +14100,30 @@ var update_default = defineCommand({
14068
14100
  process.exit(1);
14069
14101
  }
14070
14102
  await updateLocalBranch(baseBranch, syncSource.ref);
14071
- success(`Synced ${import_picocolors20.default.bold(baseBranch)} with ${import_picocolors20.default.bold(syncSource.ref)}.`);
14072
- info(`Deleting stale branch ${import_picocolors20.default.bold(currentBranch)}...`);
14103
+ success(`Synced ${import_picocolors19.default.bold(baseBranch)} with ${import_picocolors19.default.bold(syncSource.ref)}.`);
14104
+ info(`Deleting stale branch ${import_picocolors19.default.bold(currentBranch)}...`);
14073
14105
  await forceDeleteBranch(currentBranch);
14074
- success(`Deleted ${import_picocolors20.default.bold(currentBranch)}.`);
14075
- info(`Run ${import_picocolors20.default.bold("contrib start")} to begin a new feature branch.`, "");
14106
+ success(`Deleted ${import_picocolors19.default.bold(currentBranch)}.`);
14107
+ info(`Run ${import_picocolors19.default.bold("contrib start")} to begin a new feature branch.`, "");
14076
14108
  return;
14077
14109
  }
14078
- info(`Updating ${import_picocolors20.default.bold(currentBranch)} with latest ${import_picocolors20.default.bold(baseBranch)}...`);
14110
+ info(`Updating ${import_picocolors19.default.bold(currentBranch)} with latest ${import_picocolors19.default.bold(baseBranch)}...`);
14079
14111
  await fetchRemote(syncSource.remote);
14080
14112
  if (!await refExists(syncSource.ref)) {
14081
- error(`Remote ref ${import_picocolors20.default.bold(syncSource.ref)} does not exist.`);
14113
+ error(`Remote ref ${import_picocolors19.default.bold(syncSource.ref)} does not exist.`);
14082
14114
  error("Run `git fetch --all` and verify your remote configuration.");
14083
14115
  process.exit(1);
14084
14116
  }
14085
14117
  await updateLocalBranch(baseBranch, syncSource.ref);
14086
14118
  const rebaseStrategy = await determineRebaseStrategy(currentBranch, syncSource.ref);
14087
14119
  if (rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase) {
14088
- info(import_picocolors20.default.dim(`Using --onto rebase (branch was based on a different ref)`));
14120
+ info(import_picocolors19.default.dim(`Using --onto rebase (branch was based on a different ref)`));
14089
14121
  }
14090
14122
  const rebaseResult = rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase ? await rebaseOnto(syncSource.ref, rebaseStrategy.ontoOldBase) : await rebase(syncSource.ref);
14091
14123
  if (rebaseResult.exitCode !== 0) {
14092
14124
  warn("Rebase hit conflicts. Resolve them manually.");
14093
14125
  console.log();
14094
- if (isAIEnabled(config, args["no-ai"])) {
14126
+ if (!args["no-ai"]) {
14095
14127
  const copilotError = await checkCopilotAvailable();
14096
14128
  if (!copilotError) {
14097
14129
  info("Fetching AI conflict resolution suggestions...");
@@ -14114,10 +14146,10 @@ ${content.slice(0, 2000)}
14114
14146
  if (suggestion) {
14115
14147
  spinner.success("AI conflict guidance ready.");
14116
14148
  console.log(`
14117
- ${import_picocolors20.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
14118
- console.log(import_picocolors20.default.dim("─".repeat(60)));
14149
+ ${import_picocolors19.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
14150
+ console.log(import_picocolors19.default.dim("─".repeat(60)));
14119
14151
  console.log(suggestion);
14120
- console.log(import_picocolors20.default.dim("─".repeat(60)));
14152
+ console.log(import_picocolors19.default.dim("─".repeat(60)));
14121
14153
  console.log();
14122
14154
  } else {
14123
14155
  spinner.fail("AI could not analyze the conflicts.");
@@ -14125,20 +14157,20 @@ ${import_picocolors20.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance
14125
14157
  }
14126
14158
  }
14127
14159
  }
14128
- console.log(import_picocolors20.default.bold("To resolve:"));
14160
+ console.log(import_picocolors19.default.bold("To resolve:"));
14129
14161
  console.log(` 1. Fix conflicts in the affected files`);
14130
- console.log(` 2. ${import_picocolors20.default.cyan("git add <resolved-files>")}`);
14131
- console.log(` 3. ${import_picocolors20.default.cyan("git rebase --continue")}`);
14162
+ console.log(` 2. ${import_picocolors19.default.cyan("git add <resolved-files>")}`);
14163
+ console.log(` 3. ${import_picocolors19.default.cyan("git rebase --continue")}`);
14132
14164
  console.log();
14133
- console.log(` Or abort: ${import_picocolors20.default.cyan("git rebase --abort")}`);
14165
+ console.log(` Or abort: ${import_picocolors19.default.cyan("git rebase --abort")}`);
14134
14166
  process.exit(1);
14135
14167
  }
14136
- success(`${import_picocolors20.default.bold(currentBranch)} has been rebased onto latest ${import_picocolors20.default.bold(baseBranch)}`);
14168
+ success(`${import_picocolors19.default.bold(currentBranch)} has been rebased onto latest ${import_picocolors19.default.bold(baseBranch)}`);
14137
14169
  }
14138
14170
  });
14139
14171
 
14140
14172
  // src/commands/validate.ts
14141
- var import_picocolors21 = __toESM(require_picocolors(), 1);
14173
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
14142
14174
  import { readFileSync as readFileSync5 } from "node:fs";
14143
14175
  var validate_default = defineCommand({
14144
14176
  meta: {
@@ -14178,7 +14210,7 @@ var validate_default = defineCommand({
14178
14210
  }
14179
14211
  const errors = getValidationError(convention);
14180
14212
  for (const line of errors) {
14181
- console.error(import_picocolors21.default.red(` ✗ ${line}`));
14213
+ console.error(import_picocolors20.default.red(` ✗ ${line}`));
14182
14214
  }
14183
14215
  process.exit(1);
14184
14216
  }
@@ -15573,7 +15605,7 @@ nodeFiglet.fontsSync = function() {
15573
15605
  };
15574
15606
 
15575
15607
  // src/ui/banner.ts
15576
- var import_picocolors22 = __toESM(require_picocolors(), 1);
15608
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
15577
15609
  var LOGO_BIG;
15578
15610
  try {
15579
15611
  LOGO_BIG = nodeFiglet.textSync(`Contribute
@@ -15595,14 +15627,14 @@ function getAuthor() {
15595
15627
  }
15596
15628
  function showBanner(variant = "small") {
15597
15629
  const logo = variant === "big" ? LOGO_BIG : LOGO_SMALL;
15598
- console.log(import_picocolors22.default.cyan(`
15630
+ console.log(import_picocolors21.default.cyan(`
15599
15631
  ${logo}`));
15600
- console.log(` ${import_picocolors22.default.dim(`v${getVersion()}`)} ${import_picocolors22.default.dim("—")} ${import_picocolors22.default.dim(`Built by ${getAuthor()}`)}`);
15632
+ console.log(` ${import_picocolors21.default.dim(`v${getVersion()}`)} ${import_picocolors21.default.dim("—")} ${import_picocolors21.default.dim(`Built by ${getAuthor()}`)}`);
15601
15633
  if (variant === "big") {
15602
15634
  console.log();
15603
- console.log(` ${import_picocolors22.default.yellow("Star")} ${import_picocolors22.default.cyan("https://github.com/warengonzaga/contribute-now")}`);
15604
- console.log(` ${import_picocolors22.default.green("Contribute")} ${import_picocolors22.default.cyan("https://github.com/warengonzaga/contribute-now/blob/main/CONTRIBUTING.md")}`);
15605
- console.log(` ${import_picocolors22.default.magenta("Sponsor")} ${import_picocolors22.default.cyan("https://warengonzaga.com/sponsor")}`);
15635
+ console.log(` ${import_picocolors21.default.yellow("Star")} ${import_picocolors21.default.cyan("https://github.com/warengonzaga/contribute-now")}`);
15636
+ console.log(` ${import_picocolors21.default.green("Contribute")} ${import_picocolors21.default.cyan("https://github.com/warengonzaga/contribute-now/blob/main/CONTRIBUTING.md")}`);
15637
+ console.log(` ${import_picocolors21.default.magenta("Sponsor")} ${import_picocolors21.default.cyan("https://warengonzaga.com/sponsor")}`);
15606
15638
  }
15607
15639
  console.log();
15608
15640
  }