contribute-now 0.6.2-dev.3d69403 → 0.6.2-dev.967437a

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