contribute-now 0.6.2-dev.38e14f5 → 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.
- package/README.md +8 -3
- package/dist/index.js +737 -615
- 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
|
|
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
|
|
8787
|
+
function projectHeading(command, emoji) {
|
|
8788
|
+
const prefix = emoji ? `${import_picocolors.default.bold(emoji)} ` : "";
|
|
8780
8789
|
console.log(`
|
|
8781
|
-
${import_picocolors.default.bold(
|
|
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
|
-
|
|
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
|
|
@@ -10103,12 +10115,12 @@ async function multiSelectPrompt(message, choices) {
|
|
|
10103
10115
|
init_dist();
|
|
10104
10116
|
var CONVENTIONAL_COMMIT_SYSTEM_PROMPT = `Git commit message generator. Format: <type>[!][(<scope>)]: <description>
|
|
10105
10117
|
Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
|
|
10106
|
-
Rules: breaking (!) only for feat/fix/refactor/perf; imperative mood; max 72 chars; lowercase start; scope optional camelCase/kebab-case. Return ONLY the message line.
|
|
10118
|
+
Rules: breaking (!) only for feat/fix/refactor/perf; imperative mood; max 72 chars; lowercase start; scope optional camelCase/kebab-case. Do NOT use backticks, quotes, or markdown formatting around filenames, functions, or identifiers. Return ONLY the message line.
|
|
10107
10119
|
Examples: feat: add user auth | fix(auth): resolve token expiry | feat!: redesign auth API`;
|
|
10108
10120
|
var CLEAN_COMMIT_SYSTEM_PROMPT = `Git commit message generator. EXACT format: <emoji> <type>[!][ (<scope>)]: <description>
|
|
10109
10121
|
Spacing: EMOJI SPACE TYPE [SPACE OPENPAREN SCOPE CLOSEPAREN] COLON SPACE DESCRIPTION
|
|
10110
10122
|
Types: \uD83D\uDCE6 new, \uD83D\uDD27 update, \uD83D\uDDD1️ remove, \uD83D\uDD12 security, ⚙️ setup, ☕ chore, \uD83E\uDDEA test, \uD83D\uDCD6 docs, \uD83D\uDE80 release
|
|
10111
|
-
Rules: breaking (!) only for new/update/remove/security; imperative mood; max 72 chars; lowercase start; scope optional. Return ONLY the message line.
|
|
10123
|
+
Rules: breaking (!) only for new/update/remove/security; imperative mood; max 72 chars; lowercase start; scope optional. Do NOT use backticks, quotes, or markdown formatting around filenames, functions, or identifiers. Return ONLY the message line.
|
|
10112
10124
|
Correct: \uD83D\uDCE6 new: add user auth | \uD83D\uDD27 update (api): improve error handling | ⚙️ setup (ci): configure github actions
|
|
10113
10125
|
WRONG: ⚙️setup(ci): ... | \uD83D\uDD27 update(api): ... ← always space before scope parenthesis`;
|
|
10114
10126
|
function getGroupingSystemPrompt(convention) {
|
|
@@ -10133,6 +10145,7 @@ Rules:
|
|
|
10133
10145
|
- Each group should represent ONE logical change
|
|
10134
10146
|
- Every file must appear in exactly one group
|
|
10135
10147
|
- Commit messages must follow the convention, be concise, imperative, max 72 chars
|
|
10148
|
+
- Do not use backticks, quotes, or markdown formatting in commit messages
|
|
10136
10149
|
- Order groups so foundational changes come first (types, utils) and consumers come after
|
|
10137
10150
|
- Return ONLY the JSON array, nothing else`;
|
|
10138
10151
|
}
|
|
@@ -10253,7 +10266,7 @@ ${truncated}
|
|
|
10253
10266
|
return result.length > maxTotalChars ? `${result.slice(0, maxTotalChars - 15)}
|
|
10254
10267
|
...(truncated)` : result;
|
|
10255
10268
|
}
|
|
10256
|
-
async function
|
|
10269
|
+
async function checkCopilotAvailable2() {
|
|
10257
10270
|
try {
|
|
10258
10271
|
const client = await getManagedClient();
|
|
10259
10272
|
try {
|
|
@@ -10351,6 +10364,9 @@ function extractJson(raw) {
|
|
|
10351
10364
|
}
|
|
10352
10365
|
return text;
|
|
10353
10366
|
}
|
|
10367
|
+
function sanitizeGeneratedCommitMessage(message) {
|
|
10368
|
+
return message.replace(/`+/g, "").replace(/\s+/g, " ").trim();
|
|
10369
|
+
}
|
|
10354
10370
|
async function generateCommitMessage(diff, stagedFiles, model, convention = "clean-commit", context) {
|
|
10355
10371
|
try {
|
|
10356
10372
|
const isLarge = stagedFiles.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD;
|
|
@@ -10368,7 +10384,7 @@ Files (${stagedFiles.length}): ${stagedFiles.join(", ")}
|
|
|
10368
10384
|
Diff:
|
|
10369
10385
|
${diffContent}${multiFileHint}${squashHint}`;
|
|
10370
10386
|
const result = await callCopilot(getCommitSystemPrompt(convention), userMessage, model, isLarge ? COPILOT_LONG_TIMEOUT_MS : COPILOT_TIMEOUT_MS);
|
|
10371
|
-
return result
|
|
10387
|
+
return result ? sanitizeGeneratedCommitMessage(result) : null;
|
|
10372
10388
|
} catch {
|
|
10373
10389
|
return null;
|
|
10374
10390
|
}
|
|
@@ -10415,6 +10431,40 @@ ${conflictDiff.slice(0, 4000)}`;
|
|
|
10415
10431
|
return null;
|
|
10416
10432
|
}
|
|
10417
10433
|
}
|
|
10434
|
+
function normalizeCommitGroups(changedFiles, groups) {
|
|
10435
|
+
const changedSet = new Set(changedFiles);
|
|
10436
|
+
const assignedFiles = new Set;
|
|
10437
|
+
const unknownFiles = new Set;
|
|
10438
|
+
const duplicateFiles = new Set;
|
|
10439
|
+
const normalizedGroups = groups.map((group) => {
|
|
10440
|
+
const uniqueFiles = new Set;
|
|
10441
|
+
const files = [];
|
|
10442
|
+
for (const file of group.files) {
|
|
10443
|
+
if (!changedSet.has(file)) {
|
|
10444
|
+
unknownFiles.add(file);
|
|
10445
|
+
continue;
|
|
10446
|
+
}
|
|
10447
|
+
if (uniqueFiles.has(file) || assignedFiles.has(file)) {
|
|
10448
|
+
duplicateFiles.add(file);
|
|
10449
|
+
continue;
|
|
10450
|
+
}
|
|
10451
|
+
uniqueFiles.add(file);
|
|
10452
|
+
assignedFiles.add(file);
|
|
10453
|
+
files.push(file);
|
|
10454
|
+
}
|
|
10455
|
+
return {
|
|
10456
|
+
...group,
|
|
10457
|
+
files
|
|
10458
|
+
};
|
|
10459
|
+
}).filter((group) => group.files.length > 0);
|
|
10460
|
+
const unassignedFiles = changedFiles.filter((file) => !assignedFiles.has(file));
|
|
10461
|
+
return {
|
|
10462
|
+
groups: normalizedGroups,
|
|
10463
|
+
unknownFiles: [...unknownFiles],
|
|
10464
|
+
duplicateFiles: [...duplicateFiles],
|
|
10465
|
+
unassignedFiles
|
|
10466
|
+
};
|
|
10467
|
+
}
|
|
10418
10468
|
async function generateCommitGroups(files, diffs, model, convention = "clean-commit") {
|
|
10419
10469
|
const isLarge = files.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD;
|
|
10420
10470
|
const diffContent = isLarge ? createCompactDiff(files, diffs) : diffs.slice(0, 6000);
|
|
@@ -10455,7 +10505,10 @@ ${diffContent}${largeHint}`;
|
|
|
10455
10505
|
throw new Error("AI returned groups with invalid structure (missing files or message)");
|
|
10456
10506
|
}
|
|
10457
10507
|
}
|
|
10458
|
-
return groups
|
|
10508
|
+
return groups.map((group) => ({
|
|
10509
|
+
...group,
|
|
10510
|
+
message: sanitizeGeneratedCommitMessage(group.message)
|
|
10511
|
+
}));
|
|
10459
10512
|
}
|
|
10460
10513
|
async function generateCommitGroupsInBatches(files, diffs, model, convention = "clean-commit") {
|
|
10461
10514
|
const batchSize = BATCH_CONFIG.FALLBACK_BATCH_SIZE;
|
|
@@ -10490,7 +10543,11 @@ NOTE: Processing batch ${batchNum}/${totalBatches} of a large changeset. Group o
|
|
|
10490
10543
|
const batchFileSet = new Set(batchFiles);
|
|
10491
10544
|
const filteredFiles = group.files.filter((f3) => batchFileSet.has(f3));
|
|
10492
10545
|
if (filteredFiles.length > 0) {
|
|
10493
|
-
allGroups.push({
|
|
10546
|
+
allGroups.push({
|
|
10547
|
+
...group,
|
|
10548
|
+
files: filteredFiles,
|
|
10549
|
+
message: sanitizeGeneratedCommitMessage(group.message)
|
|
10550
|
+
});
|
|
10494
10551
|
}
|
|
10495
10552
|
}
|
|
10496
10553
|
}
|
|
@@ -10533,7 +10590,7 @@ ${diffContent}`;
|
|
|
10533
10590
|
return groups;
|
|
10534
10591
|
return groups.map((g3, i2) => ({
|
|
10535
10592
|
files: g3.files,
|
|
10536
|
-
message: typeof parsed[i2]?.message === "string" ? parsed[i2].message : g3.message
|
|
10593
|
+
message: typeof parsed[i2]?.message === "string" ? sanitizeGeneratedCommitMessage(parsed[i2].message) : g3.message
|
|
10537
10594
|
}));
|
|
10538
10595
|
} catch {
|
|
10539
10596
|
return groups;
|
|
@@ -10550,12 +10607,160 @@ Files: ${files.join(", ")}
|
|
|
10550
10607
|
Diff:
|
|
10551
10608
|
${diffContent}`;
|
|
10552
10609
|
const result = await callCopilot(getCommitSystemPrompt(convention), userMessage, model);
|
|
10553
|
-
return result
|
|
10610
|
+
return result ? sanitizeGeneratedCommitMessage(result) : null;
|
|
10554
10611
|
} catch {
|
|
10555
10612
|
return null;
|
|
10556
10613
|
}
|
|
10557
10614
|
}
|
|
10558
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
|
+
|
|
10559
10764
|
// src/utils/gh.ts
|
|
10560
10765
|
import { execFile as execFileCb2 } from "node:child_process";
|
|
10561
10766
|
function run2(args) {
|
|
@@ -10694,53 +10899,6 @@ async function getMergedPRForBranch(headBranch) {
|
|
|
10694
10899
|
}
|
|
10695
10900
|
}
|
|
10696
10901
|
|
|
10697
|
-
// src/utils/spinner.ts
|
|
10698
|
-
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
10699
|
-
var FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
10700
|
-
function createSpinner(text) {
|
|
10701
|
-
let frameIdx = 0;
|
|
10702
|
-
let currentText = text;
|
|
10703
|
-
let stopped = false;
|
|
10704
|
-
const clearLine = () => {
|
|
10705
|
-
process.stderr.write("\r\x1B[K");
|
|
10706
|
-
};
|
|
10707
|
-
const render = () => {
|
|
10708
|
-
if (stopped)
|
|
10709
|
-
return;
|
|
10710
|
-
const frame = import_picocolors6.default.cyan(FRAMES[frameIdx % FRAMES.length]);
|
|
10711
|
-
clearLine();
|
|
10712
|
-
process.stderr.write(`${frame} ${currentText}`);
|
|
10713
|
-
frameIdx++;
|
|
10714
|
-
};
|
|
10715
|
-
const timer = setInterval(render, 80);
|
|
10716
|
-
render();
|
|
10717
|
-
const stop = () => {
|
|
10718
|
-
if (stopped)
|
|
10719
|
-
return;
|
|
10720
|
-
stopped = true;
|
|
10721
|
-
clearInterval(timer);
|
|
10722
|
-
clearLine();
|
|
10723
|
-
};
|
|
10724
|
-
return {
|
|
10725
|
-
update(newText) {
|
|
10726
|
-
currentText = newText;
|
|
10727
|
-
},
|
|
10728
|
-
success(msg) {
|
|
10729
|
-
stop();
|
|
10730
|
-
process.stderr.write(`${import_picocolors6.default.green("✔")} ${msg}
|
|
10731
|
-
`);
|
|
10732
|
-
},
|
|
10733
|
-
fail(msg) {
|
|
10734
|
-
stop();
|
|
10735
|
-
process.stderr.write(`${import_picocolors6.default.red("✖")} ${msg}
|
|
10736
|
-
`);
|
|
10737
|
-
},
|
|
10738
|
-
stop() {
|
|
10739
|
-
stop();
|
|
10740
|
-
}
|
|
10741
|
-
};
|
|
10742
|
-
}
|
|
10743
|
-
|
|
10744
10902
|
// src/commands/clean.ts
|
|
10745
10903
|
async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
|
|
10746
10904
|
if (!config)
|
|
@@ -10753,44 +10911,22 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
|
|
|
10753
10911
|
warn("You have uncommitted changes in your working tree.");
|
|
10754
10912
|
}
|
|
10755
10913
|
if (localWork.unpushedCommits > 0) {
|
|
10756
|
-
warn(`You have ${
|
|
10914
|
+
warn(`You have ${import_picocolors8.default.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not pushed.`);
|
|
10757
10915
|
}
|
|
10758
10916
|
const SAVE_NEW_BRANCH = "Save changes to a new branch";
|
|
10759
10917
|
const DISCARD = "Discard all changes and clean up";
|
|
10760
10918
|
const CANCEL = "Skip this branch";
|
|
10761
|
-
const action = await selectPrompt(`${
|
|
10919
|
+
const action = await selectPrompt(`${import_picocolors8.default.bold(currentBranch)} has local changes. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
|
|
10762
10920
|
if (action === CANCEL)
|
|
10763
10921
|
return "skipped";
|
|
10764
10922
|
if (action === SAVE_NEW_BRANCH) {
|
|
10765
10923
|
if (!config)
|
|
10766
10924
|
return "skipped";
|
|
10767
|
-
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
|
|
10771
|
-
|
|
10772
|
-
const suggested = await suggestBranchName(description);
|
|
10773
|
-
if (suggested) {
|
|
10774
|
-
spinner.success("Branch name suggestion ready.");
|
|
10775
|
-
console.log(`
|
|
10776
|
-
${import_picocolors7.default.dim("AI suggestion:")} ${import_picocolors7.default.bold(import_picocolors7.default.cyan(suggested))}`);
|
|
10777
|
-
const accepted = await confirmPrompt(`Use ${import_picocolors7.default.bold(suggested)} as your branch name?`);
|
|
10778
|
-
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
10779
|
-
} else {
|
|
10780
|
-
spinner.fail("AI did not return a suggestion.");
|
|
10781
|
-
newBranchName = await inputPrompt("Enter branch name", description);
|
|
10782
|
-
}
|
|
10783
|
-
}
|
|
10784
|
-
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
10785
|
-
const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors7.default.bold(newBranchName)}:`, config.branchPrefixes);
|
|
10786
|
-
newBranchName = formatBranchName(prefix, newBranchName);
|
|
10787
|
-
}
|
|
10788
|
-
if (!isValidBranchName(newBranchName)) {
|
|
10789
|
-
error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
|
|
10790
|
-
return "skipped";
|
|
10791
|
-
}
|
|
10792
|
-
if (await branchExists(newBranchName)) {
|
|
10793
|
-
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) {
|
|
10794
10930
|
return "skipped";
|
|
10795
10931
|
}
|
|
10796
10932
|
const renameResult = await renameBranch(currentBranch, newBranchName);
|
|
@@ -10798,7 +10934,7 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
|
|
|
10798
10934
|
error(`Failed to rename branch: ${renameResult.stderr}`);
|
|
10799
10935
|
return "skipped";
|
|
10800
10936
|
}
|
|
10801
|
-
success(`Renamed ${
|
|
10937
|
+
success(`Renamed ${import_picocolors8.default.bold(currentBranch)} → ${import_picocolors8.default.bold(newBranchName)}`);
|
|
10802
10938
|
const syncSource2 = getSyncSource(config);
|
|
10803
10939
|
await fetchRemote(syncSource2.remote);
|
|
10804
10940
|
const savedUpstreamRef = await getUpstreamRef();
|
|
@@ -10806,10 +10942,10 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
|
|
|
10806
10942
|
if (rebaseResult.exitCode !== 0) {
|
|
10807
10943
|
await rebaseAbort();
|
|
10808
10944
|
warn("Rebase had conflicts — aborted to keep the repo in a clean state.");
|
|
10809
|
-
info(`Your work is saved on ${
|
|
10810
|
-
info(` ${
|
|
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}`)}`, "");
|
|
10811
10947
|
} else {
|
|
10812
|
-
success(`Rebased ${
|
|
10948
|
+
success(`Rebased ${import_picocolors8.default.bold(newBranchName)} onto ${import_picocolors8.default.bold(syncSource2.ref)}.`);
|
|
10813
10949
|
}
|
|
10814
10950
|
const coResult2 = await checkoutBranch(baseBranch);
|
|
10815
10951
|
if (coResult2.exitCode !== 0) {
|
|
@@ -10817,12 +10953,12 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
|
|
|
10817
10953
|
return "saved";
|
|
10818
10954
|
}
|
|
10819
10955
|
await updateLocalBranch(baseBranch, syncSource2.ref);
|
|
10820
|
-
success(`Synced ${
|
|
10956
|
+
success(`Synced ${import_picocolors8.default.bold(baseBranch)} with ${import_picocolors8.default.bold(syncSource2.ref)}.`);
|
|
10821
10957
|
return "saved";
|
|
10822
10958
|
}
|
|
10823
10959
|
}
|
|
10824
10960
|
const syncSource = getSyncSource(config);
|
|
10825
|
-
info(`Switching to ${
|
|
10961
|
+
info(`Switching to ${import_picocolors8.default.bold(baseBranch)} and syncing...`);
|
|
10826
10962
|
await fetchRemote(syncSource.remote);
|
|
10827
10963
|
await resetHard("HEAD");
|
|
10828
10964
|
const coResult = await checkoutBranch(baseBranch);
|
|
@@ -10831,7 +10967,7 @@ async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
|
|
|
10831
10967
|
return "skipped";
|
|
10832
10968
|
}
|
|
10833
10969
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
10834
|
-
success(`Synced ${
|
|
10970
|
+
success(`Synced ${import_picocolors8.default.bold(baseBranch)} with ${import_picocolors8.default.bold(syncSource.ref)}.`);
|
|
10835
10971
|
return "switched";
|
|
10836
10972
|
}
|
|
10837
10973
|
var clean_default = defineCommand({
|
|
@@ -10861,7 +10997,7 @@ var clean_default = defineCommand({
|
|
|
10861
10997
|
const { origin } = config;
|
|
10862
10998
|
const baseBranch = getBaseBranch(config);
|
|
10863
10999
|
let currentBranch = await getCurrentBranch();
|
|
10864
|
-
|
|
11000
|
+
projectHeading("clean", "\uD83E\uDDF9");
|
|
10865
11001
|
info(`Pruning ${origin} remote refs...`);
|
|
10866
11002
|
const pruneResult = await pruneRemote(origin);
|
|
10867
11003
|
if (pruneResult.exitCode === 0) {
|
|
@@ -10881,21 +11017,21 @@ var clean_default = defineCommand({
|
|
|
10881
11017
|
if (ghInstalled && ghAuthed) {
|
|
10882
11018
|
const mergedPR = await getMergedPRForBranch(currentBranch);
|
|
10883
11019
|
if (mergedPR) {
|
|
10884
|
-
warn(`PR #${mergedPR.number} (${
|
|
10885
|
-
info(`Link: ${
|
|
11020
|
+
warn(`PR #${mergedPR.number} (${import_picocolors8.default.bold(mergedPR.title)}) has already been merged.`);
|
|
11021
|
+
info(`Link: ${import_picocolors8.default.underline(mergedPR.url)}`, "");
|
|
10886
11022
|
goneCandidates.push(currentBranch);
|
|
10887
11023
|
}
|
|
10888
11024
|
}
|
|
10889
11025
|
}
|
|
10890
11026
|
if (mergedCandidates.length > 0) {
|
|
10891
11027
|
console.log(`
|
|
10892
|
-
${
|
|
11028
|
+
${import_picocolors8.default.bold("Merged branches to delete:")}`);
|
|
10893
11029
|
for (const b2 of mergedCandidates) {
|
|
10894
|
-
const marker = b2 === currentBranch ?
|
|
10895
|
-
console.log(` ${
|
|
11030
|
+
const marker = b2 === currentBranch ? import_picocolors8.default.yellow(" (current)") : "";
|
|
11031
|
+
console.log(` ${import_picocolors8.default.dim("•")} ${b2}${marker}`);
|
|
10896
11032
|
}
|
|
10897
11033
|
console.log();
|
|
10898
|
-
const ok = args.yes || await confirmPrompt(`Delete ${
|
|
11034
|
+
const ok = args.yes || await confirmPrompt(`Delete ${import_picocolors8.default.bold(String(mergedCandidates.length))} merged branch${mergedCandidates.length !== 1 ? "es" : ""}?`);
|
|
10899
11035
|
if (ok) {
|
|
10900
11036
|
for (const branch of mergedCandidates) {
|
|
10901
11037
|
if (branch === currentBranch) {
|
|
@@ -10912,7 +11048,7 @@ ${import_picocolors7.default.bold("Merged branches to delete:")}`);
|
|
|
10912
11048
|
}
|
|
10913
11049
|
const result = await deleteBranch(branch);
|
|
10914
11050
|
if (result.exitCode === 0) {
|
|
10915
|
-
success(` Deleted ${
|
|
11051
|
+
success(` Deleted ${import_picocolors8.default.bold(branch)}`);
|
|
10916
11052
|
} else {
|
|
10917
11053
|
warn(` Failed to delete ${branch}: ${result.stderr.trim()}`);
|
|
10918
11054
|
}
|
|
@@ -10923,13 +11059,13 @@ ${import_picocolors7.default.bold("Merged branches to delete:")}`);
|
|
|
10923
11059
|
}
|
|
10924
11060
|
if (goneCandidates.length > 0) {
|
|
10925
11061
|
console.log(`
|
|
10926
|
-
${
|
|
11062
|
+
${import_picocolors8.default.bold("Stale branches (remote deleted, likely squash-merged):")}`);
|
|
10927
11063
|
for (const b2 of goneCandidates) {
|
|
10928
|
-
const marker = b2 === currentBranch ?
|
|
10929
|
-
console.log(` ${
|
|
11064
|
+
const marker = b2 === currentBranch ? import_picocolors8.default.yellow(" (current)") : "";
|
|
11065
|
+
console.log(` ${import_picocolors8.default.dim("•")} ${b2}${marker}`);
|
|
10930
11066
|
}
|
|
10931
11067
|
console.log();
|
|
10932
|
-
const ok = args.yes || await confirmPrompt(`Delete ${
|
|
11068
|
+
const ok = args.yes || await confirmPrompt(`Delete ${import_picocolors8.default.bold(String(goneCandidates.length))} stale branch${goneCandidates.length !== 1 ? "es" : ""}?`);
|
|
10933
11069
|
if (ok) {
|
|
10934
11070
|
for (const branch of goneCandidates) {
|
|
10935
11071
|
if (branch === currentBranch) {
|
|
@@ -10946,7 +11082,7 @@ ${import_picocolors7.default.bold("Stale branches (remote deleted, likely squash
|
|
|
10946
11082
|
}
|
|
10947
11083
|
const result = await forceDeleteBranch(branch);
|
|
10948
11084
|
if (result.exitCode === 0) {
|
|
10949
|
-
success(` Deleted ${
|
|
11085
|
+
success(` Deleted ${import_picocolors8.default.bold(branch)}`);
|
|
10950
11086
|
} else {
|
|
10951
11087
|
warn(` Failed to delete ${branch}: ${result.stderr.trim()}`);
|
|
10952
11088
|
}
|
|
@@ -10961,13 +11097,13 @@ ${import_picocolors7.default.bold("Stale branches (remote deleted, likely squash
|
|
|
10961
11097
|
const finalBranch = await getCurrentBranch();
|
|
10962
11098
|
if (finalBranch && protectedBranches.has(finalBranch)) {
|
|
10963
11099
|
console.log();
|
|
10964
|
-
info(`You're on ${
|
|
11100
|
+
info(`You're on ${import_picocolors8.default.bold(finalBranch)}. Run ${import_picocolors8.default.bold("contrib start")} to begin a new feature.`);
|
|
10965
11101
|
}
|
|
10966
11102
|
}
|
|
10967
11103
|
});
|
|
10968
11104
|
|
|
10969
11105
|
// src/commands/commit.ts
|
|
10970
|
-
var
|
|
11106
|
+
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
10971
11107
|
|
|
10972
11108
|
// src/utils/convention.ts
|
|
10973
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;
|
|
@@ -10986,17 +11122,24 @@ var CONVENTION_FORMAT_HINTS = {
|
|
|
10986
11122
|
conventional: [
|
|
10987
11123
|
"Format: <type>[!][(<scope>)]: <description>",
|
|
10988
11124
|
"Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert",
|
|
10989
|
-
"Examples: feat: add login page | fix(auth): resolve token expiry | docs: update README"
|
|
11125
|
+
"Examples: feat: add login page | fix(auth): resolve token expiry | docs: update README",
|
|
11126
|
+
"Do not use backticks or markdown formatting in the message."
|
|
10990
11127
|
],
|
|
10991
11128
|
"clean-commit": [
|
|
10992
11129
|
"Format: <emoji> <type>[!][(<scope>)]: <description>",
|
|
10993
11130
|
"Types: \uD83D\uDCE6 new | \uD83D\uDD27 update | \uD83D\uDDD1️ remove | \uD83D\uDD12 security | ⚙️ setup | ☕ chore | \uD83E\uDDEA test | \uD83D\uDCD6 docs | \uD83D\uDE80 release",
|
|
10994
|
-
"Examples: \uD83D\uDCE6 new: user auth | \uD83D\uDD27 update (api): improve errors | ⚙️ setup (ci): add workflow"
|
|
11131
|
+
"Examples: \uD83D\uDCE6 new: user auth | \uD83D\uDD27 update (api): improve errors | ⚙️ setup (ci): add workflow",
|
|
11132
|
+
"Do not use backticks or markdown formatting in the message."
|
|
10995
11133
|
]
|
|
10996
11134
|
};
|
|
11135
|
+
function hasUnsupportedCommitMessageChars(message) {
|
|
11136
|
+
return message.includes("`");
|
|
11137
|
+
}
|
|
10997
11138
|
function validateCommitMessage(message, convention) {
|
|
10998
11139
|
if (convention === "none")
|
|
10999
11140
|
return true;
|
|
11141
|
+
if (hasUnsupportedCommitMessageChars(message))
|
|
11142
|
+
return false;
|
|
11000
11143
|
if (convention === "clean-commit")
|
|
11001
11144
|
return CLEAN_COMMIT_PATTERN.test(message);
|
|
11002
11145
|
if (convention === "conventional")
|
|
@@ -11008,6 +11151,7 @@ function getValidationError(convention) {
|
|
|
11008
11151
|
return [];
|
|
11009
11152
|
return [
|
|
11010
11153
|
`Commit message does not follow ${CONVENTION_LABELS[convention]} format.`,
|
|
11154
|
+
"Do not use backticks or markdown formatting in commit messages.",
|
|
11011
11155
|
...CONVENTION_FORMAT_HINTS[convention]
|
|
11012
11156
|
];
|
|
11013
11157
|
}
|
|
@@ -11045,8 +11189,13 @@ var commit_default = defineCommand({
|
|
|
11045
11189
|
error("No .contributerc.json found. Run `contrib setup` first.");
|
|
11046
11190
|
process.exit(1);
|
|
11047
11191
|
}
|
|
11048
|
-
|
|
11192
|
+
projectHeading("commit", "\uD83D\uDCBE");
|
|
11193
|
+
const aiEnabled = isAIEnabled(config, args["no-ai"]);
|
|
11049
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
|
+
}
|
|
11050
11199
|
await runGroupCommit(args.model, config);
|
|
11051
11200
|
return;
|
|
11052
11201
|
}
|
|
@@ -11058,9 +11207,9 @@ var commit_default = defineCommand({
|
|
|
11058
11207
|
process.exit(1);
|
|
11059
11208
|
}
|
|
11060
11209
|
console.log(`
|
|
11061
|
-
${
|
|
11210
|
+
${import_picocolors9.default.bold("Changed files:")}`);
|
|
11062
11211
|
for (const f3 of changedFiles) {
|
|
11063
|
-
console.log(` ${
|
|
11212
|
+
console.log(` ${import_picocolors9.default.dim("•")} ${f3}`);
|
|
11064
11213
|
}
|
|
11065
11214
|
const stageAction = await selectPrompt("No staged changes. How would you like to stage?", [
|
|
11066
11215
|
"Stage all changes",
|
|
@@ -11102,8 +11251,8 @@ ${import_picocolors8.default.bold("Changed files:")}`);
|
|
|
11102
11251
|
const dirs = new Set(stagedFiles.map((f3) => f3.split("/")[0]));
|
|
11103
11252
|
if (dirs.size > 1) {
|
|
11104
11253
|
console.log();
|
|
11105
|
-
warn(`You're staging ${
|
|
11106
|
-
info(
|
|
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."));
|
|
11107
11256
|
const choice = await selectPrompt("How would you like to proceed?", [
|
|
11108
11257
|
"Continue as single commit",
|
|
11109
11258
|
"Switch to group mode (AI splits into atomic commits)",
|
|
@@ -11119,9 +11268,9 @@ ${import_picocolors8.default.bold("Changed files:")}`);
|
|
|
11119
11268
|
}
|
|
11120
11269
|
}
|
|
11121
11270
|
let commitMessage = null;
|
|
11122
|
-
const useAI =
|
|
11271
|
+
const useAI = aiEnabled;
|
|
11123
11272
|
if (useAI) {
|
|
11124
|
-
const [copilotError, diff] = await Promise.all([
|
|
11273
|
+
const [copilotError, diff] = await Promise.all([checkCopilotAvailable2(), getStagedDiff()]);
|
|
11125
11274
|
if (copilotError) {
|
|
11126
11275
|
warn(`AI unavailable: ${copilotError}`);
|
|
11127
11276
|
warn("Falling back to manual commit message entry.");
|
|
@@ -11132,7 +11281,7 @@ ${import_picocolors8.default.bold("Changed files:")}`);
|
|
|
11132
11281
|
if (commitMessage) {
|
|
11133
11282
|
spinner.success("AI commit message generated.");
|
|
11134
11283
|
console.log(`
|
|
11135
|
-
${
|
|
11284
|
+
${import_picocolors9.default.dim("AI suggestion:")} ${import_picocolors9.default.bold(import_picocolors9.default.cyan(commitMessage))}`);
|
|
11136
11285
|
} else {
|
|
11137
11286
|
spinner.fail("AI did not return a commit message.");
|
|
11138
11287
|
warn("Falling back to manual entry.");
|
|
@@ -11158,7 +11307,7 @@ ${import_picocolors8.default.bold("Changed files:")}`);
|
|
|
11158
11307
|
if (regen) {
|
|
11159
11308
|
spinner.success("Commit message regenerated.");
|
|
11160
11309
|
console.log(`
|
|
11161
|
-
${
|
|
11310
|
+
${import_picocolors9.default.dim("AI suggestion:")} ${import_picocolors9.default.bold(import_picocolors9.default.cyan(regen))}`);
|
|
11162
11311
|
const ok = await confirmPrompt("Use this message?");
|
|
11163
11312
|
finalMessage = ok ? regen : await inputPrompt("Enter commit message manually");
|
|
11164
11313
|
} else {
|
|
@@ -11173,7 +11322,7 @@ ${import_picocolors8.default.bold("Changed files:")}`);
|
|
|
11173
11322
|
if (convention2 !== "none") {
|
|
11174
11323
|
console.log();
|
|
11175
11324
|
for (const hint of CONVENTION_FORMAT_HINTS[convention2]) {
|
|
11176
|
-
console.log(
|
|
11325
|
+
console.log(import_picocolors9.default.dim(hint));
|
|
11177
11326
|
}
|
|
11178
11327
|
console.log();
|
|
11179
11328
|
}
|
|
@@ -11197,12 +11346,21 @@ ${import_picocolors8.default.bold("Changed files:")}`);
|
|
|
11197
11346
|
error(`Failed to commit: ${result.stderr}`);
|
|
11198
11347
|
process.exit(1);
|
|
11199
11348
|
}
|
|
11200
|
-
success(`Committed: ${
|
|
11349
|
+
success(`Committed: ${import_picocolors9.default.bold(finalMessage)}`);
|
|
11201
11350
|
}
|
|
11202
11351
|
});
|
|
11352
|
+
function getFallbackGroupMessage(convention) {
|
|
11353
|
+
if (convention === "conventional") {
|
|
11354
|
+
return "chore: commit remaining changes";
|
|
11355
|
+
}
|
|
11356
|
+
if (convention === "clean-commit") {
|
|
11357
|
+
return "☕ chore: commit remaining changes";
|
|
11358
|
+
}
|
|
11359
|
+
return "commit remaining changes";
|
|
11360
|
+
}
|
|
11203
11361
|
async function runGroupCommit(model, config) {
|
|
11204
11362
|
const [copilotError, changedFiles] = await Promise.all([
|
|
11205
|
-
|
|
11363
|
+
checkCopilotAvailable2(),
|
|
11206
11364
|
getChangedFiles()
|
|
11207
11365
|
]);
|
|
11208
11366
|
if (copilotError) {
|
|
@@ -11214,9 +11372,9 @@ async function runGroupCommit(model, config) {
|
|
|
11214
11372
|
process.exit(1);
|
|
11215
11373
|
}
|
|
11216
11374
|
console.log(`
|
|
11217
|
-
${
|
|
11375
|
+
${import_picocolors9.default.bold("Changed files:")}`);
|
|
11218
11376
|
for (const f3 of changedFiles) {
|
|
11219
|
-
console.log(` ${
|
|
11377
|
+
console.log(` ${import_picocolors9.default.dim("•")} ${f3}`);
|
|
11220
11378
|
}
|
|
11221
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...`);
|
|
11222
11380
|
const diffs = await getFullDiffForFiles(changedFiles);
|
|
@@ -11237,15 +11395,25 @@ ${import_picocolors8.default.bold("Changed files:")}`);
|
|
|
11237
11395
|
error("AI could not produce commit groups. Try committing files manually.");
|
|
11238
11396
|
process.exit(1);
|
|
11239
11397
|
}
|
|
11240
|
-
const
|
|
11241
|
-
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
|
|
11245
|
-
}
|
|
11246
|
-
|
|
11398
|
+
const normalized = normalizeCommitGroups(changedFiles, groups);
|
|
11399
|
+
if (normalized.unknownFiles.length > 0) {
|
|
11400
|
+
warn(`AI suggested unknown file(s): ${normalized.unknownFiles.join(", ")} — removed from groups.`);
|
|
11401
|
+
}
|
|
11402
|
+
if (normalized.duplicateFiles.length > 0) {
|
|
11403
|
+
warn(`AI assigned duplicate file(s) across groups: ${normalized.duplicateFiles.join(", ")} — keeping the first assignment only.`);
|
|
11404
|
+
}
|
|
11405
|
+
let validGroups = normalized.groups;
|
|
11406
|
+
if (normalized.unassignedFiles.length > 0) {
|
|
11407
|
+
warn(`AI left ${normalized.unassignedFiles.length} file(s) ungrouped: ${normalized.unassignedFiles.join(", ")}. Creating a fallback group.`);
|
|
11408
|
+
const fallbackMessage = await regenerateGroupMessage(normalized.unassignedFiles, diffs, model, config.commitConvention) ?? getFallbackGroupMessage(config.commitConvention);
|
|
11409
|
+
validGroups = [
|
|
11410
|
+
...validGroups,
|
|
11411
|
+
{
|
|
11412
|
+
files: normalized.unassignedFiles,
|
|
11413
|
+
message: fallbackMessage
|
|
11414
|
+
}
|
|
11415
|
+
];
|
|
11247
11416
|
}
|
|
11248
|
-
let validGroups = groups.filter((g3) => g3.files.length > 0);
|
|
11249
11417
|
if (validGroups.length === 0) {
|
|
11250
11418
|
error("No valid groups remain after validation. Try committing files manually.");
|
|
11251
11419
|
process.exit(1);
|
|
@@ -11254,13 +11422,13 @@ ${import_picocolors8.default.bold("Changed files:")}`);
|
|
|
11254
11422
|
let commitAll = false;
|
|
11255
11423
|
while (!proceedToCommit) {
|
|
11256
11424
|
console.log(`
|
|
11257
|
-
${
|
|
11425
|
+
${import_picocolors9.default.bold(`AI suggested ${validGroups.length} commit group(s):`)}
|
|
11258
11426
|
`);
|
|
11259
11427
|
for (let i2 = 0;i2 < validGroups.length; i2++) {
|
|
11260
11428
|
const g3 = validGroups[i2];
|
|
11261
|
-
console.log(` ${
|
|
11429
|
+
console.log(` ${import_picocolors9.default.cyan(`Group ${i2 + 1}:`)} ${import_picocolors9.default.bold(g3.message)}`);
|
|
11262
11430
|
for (const f3 of g3.files) {
|
|
11263
|
-
console.log(` ${
|
|
11431
|
+
console.log(` ${import_picocolors9.default.dim("•")} ${f3}`);
|
|
11264
11432
|
}
|
|
11265
11433
|
console.log();
|
|
11266
11434
|
}
|
|
@@ -11291,7 +11459,17 @@ ${import_picocolors8.default.bold(`AI suggested ${validGroups.length} commit gro
|
|
|
11291
11459
|
if (commitAll) {
|
|
11292
11460
|
for (let i2 = 0;i2 < validGroups.length; i2++) {
|
|
11293
11461
|
const group = validGroups[i2];
|
|
11294
|
-
const
|
|
11462
|
+
const remainingChangedFiles = new Set(await getChangedFiles());
|
|
11463
|
+
const stageableFiles = group.files.filter((file) => remainingChangedFiles.has(file));
|
|
11464
|
+
const skippedFiles = group.files.filter((file) => !remainingChangedFiles.has(file));
|
|
11465
|
+
if (skippedFiles.length > 0) {
|
|
11466
|
+
warn(`Group ${i2 + 1} file(s) no longer have changes: ${skippedFiles.join(", ")}`);
|
|
11467
|
+
}
|
|
11468
|
+
if (stageableFiles.length === 0) {
|
|
11469
|
+
warn(`Skipped group ${i2 + 1}: no files remain to commit.`);
|
|
11470
|
+
continue;
|
|
11471
|
+
}
|
|
11472
|
+
const stageResult = await stageFiles(stageableFiles);
|
|
11295
11473
|
if (stageResult.exitCode !== 0) {
|
|
11296
11474
|
error(`Failed to stage group ${i2 + 1}: ${stageResult.stderr}`);
|
|
11297
11475
|
continue;
|
|
@@ -11300,20 +11478,20 @@ ${import_picocolors8.default.bold(`AI suggested ${validGroups.length} commit gro
|
|
|
11300
11478
|
if (commitResult.exitCode !== 0) {
|
|
11301
11479
|
const detail = (commitResult.stderr || commitResult.stdout).trim();
|
|
11302
11480
|
error(`Failed to commit group ${i2 + 1}: ${detail}`);
|
|
11303
|
-
await unstageFiles(
|
|
11481
|
+
await unstageFiles(stageableFiles);
|
|
11304
11482
|
continue;
|
|
11305
11483
|
}
|
|
11306
11484
|
committed++;
|
|
11307
|
-
success(`Committed group ${i2 + 1}: ${
|
|
11485
|
+
success(`Committed group ${i2 + 1}: ${import_picocolors9.default.bold(group.message)}`);
|
|
11308
11486
|
}
|
|
11309
11487
|
} else {
|
|
11310
11488
|
for (let i2 = 0;i2 < validGroups.length; i2++) {
|
|
11311
11489
|
const group = validGroups[i2];
|
|
11312
|
-
console.log(
|
|
11490
|
+
console.log(import_picocolors9.default.bold(`
|
|
11313
11491
|
── Group ${i2 + 1}/${validGroups.length} ──`));
|
|
11314
|
-
console.log(` ${
|
|
11492
|
+
console.log(` ${import_picocolors9.default.cyan(group.message)}`);
|
|
11315
11493
|
for (const f3 of group.files) {
|
|
11316
|
-
console.log(` ${
|
|
11494
|
+
console.log(` ${import_picocolors9.default.dim("•")} ${f3}`);
|
|
11317
11495
|
}
|
|
11318
11496
|
let message = group.message;
|
|
11319
11497
|
let actionDone = false;
|
|
@@ -11335,7 +11513,7 @@ ${import_picocolors8.default.bold(`AI suggested ${validGroups.length} commit gro
|
|
|
11335
11513
|
if (newMsg) {
|
|
11336
11514
|
message = newMsg;
|
|
11337
11515
|
group.message = newMsg;
|
|
11338
|
-
regenSpinner.success(`New message: ${
|
|
11516
|
+
regenSpinner.success(`New message: ${import_picocolors9.default.bold(message)}`);
|
|
11339
11517
|
} else {
|
|
11340
11518
|
regenSpinner.fail("AI could not generate a new message. Keeping current one.");
|
|
11341
11519
|
}
|
|
@@ -11360,7 +11538,18 @@ ${import_picocolors8.default.bold(`AI suggested ${validGroups.length} commit gro
|
|
|
11360
11538
|
continue;
|
|
11361
11539
|
}
|
|
11362
11540
|
}
|
|
11363
|
-
const
|
|
11541
|
+
const remainingChangedFiles = new Set(await getChangedFiles());
|
|
11542
|
+
const stageableFiles = group.files.filter((file) => remainingChangedFiles.has(file));
|
|
11543
|
+
const skippedFiles = group.files.filter((file) => !remainingChangedFiles.has(file));
|
|
11544
|
+
if (skippedFiles.length > 0) {
|
|
11545
|
+
warn(`Group ${i2 + 1} file(s) no longer have changes: ${skippedFiles.join(", ")}`);
|
|
11546
|
+
}
|
|
11547
|
+
if (stageableFiles.length === 0) {
|
|
11548
|
+
warn(`Skipped group ${i2 + 1}: no files remain to commit.`);
|
|
11549
|
+
actionDone = true;
|
|
11550
|
+
continue;
|
|
11551
|
+
}
|
|
11552
|
+
const stageResult = await stageFiles(stageableFiles);
|
|
11364
11553
|
if (stageResult.exitCode !== 0) {
|
|
11365
11554
|
error(`Failed to stage group ${i2 + 1}: ${stageResult.stderr}`);
|
|
11366
11555
|
actionDone = true;
|
|
@@ -11370,12 +11559,12 @@ ${import_picocolors8.default.bold(`AI suggested ${validGroups.length} commit gro
|
|
|
11370
11559
|
if (commitResult.exitCode !== 0) {
|
|
11371
11560
|
const detail = (commitResult.stderr || commitResult.stdout).trim();
|
|
11372
11561
|
error(`Failed to commit group ${i2 + 1}: ${detail}`);
|
|
11373
|
-
await unstageFiles(
|
|
11562
|
+
await unstageFiles(stageableFiles);
|
|
11374
11563
|
actionDone = true;
|
|
11375
11564
|
continue;
|
|
11376
11565
|
}
|
|
11377
11566
|
committed++;
|
|
11378
|
-
success(`Committed group ${i2 + 1}: ${
|
|
11567
|
+
success(`Committed group ${i2 + 1}: ${import_picocolors9.default.bold(message)}`);
|
|
11379
11568
|
actionDone = true;
|
|
11380
11569
|
}
|
|
11381
11570
|
}
|
|
@@ -11391,11 +11580,11 @@ ${import_picocolors8.default.bold(`AI suggested ${validGroups.length} commit gro
|
|
|
11391
11580
|
|
|
11392
11581
|
// src/commands/doctor.ts
|
|
11393
11582
|
import { execFile as execFileCb3 } from "node:child_process";
|
|
11394
|
-
var
|
|
11583
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
11395
11584
|
// package.json
|
|
11396
11585
|
var package_default = {
|
|
11397
11586
|
name: "contribute-now",
|
|
11398
|
-
version: "0.6.2-dev.
|
|
11587
|
+
version: "0.6.2-dev.967437a",
|
|
11399
11588
|
description: "Developer CLI that automates git workflows — branching, syncing, committing, and PRs — with multi-workflow and commit convention support.",
|
|
11400
11589
|
type: "module",
|
|
11401
11590
|
bin: {
|
|
@@ -11486,16 +11675,16 @@ async function getRepoInfoFromRemote(remote = "origin") {
|
|
|
11486
11675
|
}
|
|
11487
11676
|
|
|
11488
11677
|
// src/commands/doctor.ts
|
|
11489
|
-
var PASS = ` ${
|
|
11490
|
-
var FAIL = ` ${
|
|
11491
|
-
var WARN = ` ${
|
|
11678
|
+
var PASS = ` ${import_picocolors10.default.green("✔")} `;
|
|
11679
|
+
var FAIL = ` ${import_picocolors10.default.red("✗")} `;
|
|
11680
|
+
var WARN = ` ${import_picocolors10.default.yellow("⚠")} `;
|
|
11492
11681
|
function printReport(report) {
|
|
11493
11682
|
for (const section of report.sections) {
|
|
11494
11683
|
console.log(`
|
|
11495
|
-
${
|
|
11684
|
+
${import_picocolors10.default.bold(import_picocolors10.default.underline(section.title))}`);
|
|
11496
11685
|
for (const check of section.checks) {
|
|
11497
11686
|
const prefix = check.ok ? check.warning ? WARN : PASS : FAIL;
|
|
11498
|
-
const text = check.detail ? `${check.label} ${
|
|
11687
|
+
const text = check.detail ? `${check.label} ${import_picocolors10.default.dim(`— ${check.detail}`)}` : check.label;
|
|
11499
11688
|
console.log(`${prefix}${text}`);
|
|
11500
11689
|
}
|
|
11501
11690
|
}
|
|
@@ -11758,20 +11947,20 @@ var doctor_default = defineCommand({
|
|
|
11758
11947
|
console.log(toJson(report));
|
|
11759
11948
|
return;
|
|
11760
11949
|
}
|
|
11761
|
-
|
|
11950
|
+
projectHeading("doctor", "\uD83E\uDE7A");
|
|
11762
11951
|
printReport(report);
|
|
11763
11952
|
const total = report.sections.flatMap((s2) => s2.checks);
|
|
11764
11953
|
const failures = total.filter((c3) => !c3.ok);
|
|
11765
11954
|
const warnings = total.filter((c3) => c3.ok && c3.warning);
|
|
11766
11955
|
if (failures.length === 0 && warnings.length === 0) {
|
|
11767
|
-
console.log(` ${
|
|
11956
|
+
console.log(` ${import_picocolors10.default.green("All checks passed!")} No issues detected.
|
|
11768
11957
|
`);
|
|
11769
11958
|
} else {
|
|
11770
11959
|
if (failures.length > 0) {
|
|
11771
|
-
console.log(` ${
|
|
11960
|
+
console.log(` ${import_picocolors10.default.red(`${failures.length} issue${failures.length !== 1 ? "s" : ""} found.`)}`);
|
|
11772
11961
|
}
|
|
11773
11962
|
if (warnings.length > 0) {
|
|
11774
|
-
console.log(` ${
|
|
11963
|
+
console.log(` ${import_picocolors10.default.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}.`)}`);
|
|
11775
11964
|
}
|
|
11776
11965
|
console.log();
|
|
11777
11966
|
}
|
|
@@ -11781,7 +11970,7 @@ var doctor_default = defineCommand({
|
|
|
11781
11970
|
// src/commands/hook.ts
|
|
11782
11971
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
11783
11972
|
import { join as join4 } from "node:path";
|
|
11784
|
-
var
|
|
11973
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
11785
11974
|
var HOOK_MARKER = "# managed by contribute-now";
|
|
11786
11975
|
function getHooksDir(cwd = process.cwd()) {
|
|
11787
11976
|
return join4(cwd, ".git", "hooks");
|
|
@@ -11806,13 +11995,13 @@ esac
|
|
|
11806
11995
|
|
|
11807
11996
|
# Detect available package runner
|
|
11808
11997
|
if command -v contrib >/dev/null 2>&1; then
|
|
11809
|
-
contrib validate "$
|
|
11998
|
+
contrib validate --file "$commit_msg_file"
|
|
11810
11999
|
elif command -v bunx >/dev/null 2>&1; then
|
|
11811
|
-
bunx contrib validate "$
|
|
12000
|
+
bunx contrib validate --file "$commit_msg_file"
|
|
11812
12001
|
elif command -v pnpx >/dev/null 2>&1; then
|
|
11813
|
-
pnpx contrib validate "$
|
|
12002
|
+
pnpx contrib validate --file "$commit_msg_file"
|
|
11814
12003
|
elif command -v npx >/dev/null 2>&1; then
|
|
11815
|
-
npx contrib validate "$
|
|
12004
|
+
npx contrib validate --file "$commit_msg_file"
|
|
11816
12005
|
else
|
|
11817
12006
|
echo "Warning: No package runner found. Skipping commit message validation."
|
|
11818
12007
|
exit 0
|
|
@@ -11849,7 +12038,7 @@ var hook_default = defineCommand({
|
|
|
11849
12038
|
}
|
|
11850
12039
|
});
|
|
11851
12040
|
async function installHook() {
|
|
11852
|
-
|
|
12041
|
+
projectHeading("hook install", "\uD83E\uDE9D");
|
|
11853
12042
|
const config = readConfig();
|
|
11854
12043
|
if (!config) {
|
|
11855
12044
|
error("No .contributerc.json found. Run `contrib setup` first.");
|
|
@@ -11877,12 +12066,12 @@ async function installHook() {
|
|
|
11877
12066
|
}
|
|
11878
12067
|
writeFileSync3(hookPath, generateHookScript(), { mode: 493 });
|
|
11879
12068
|
success(`commit-msg hook installed.`);
|
|
11880
|
-
info(`Convention: ${
|
|
11881
|
-
info(`Path: ${
|
|
12069
|
+
info(`Convention: ${import_picocolors11.default.bold(CONVENTION_LABELS[config.commitConvention])}`, "");
|
|
12070
|
+
info(`Path: ${import_picocolors11.default.dim(hookPath)}`, "");
|
|
11882
12071
|
warn("Note: hooks can be bypassed with `git commit --no-verify`.");
|
|
11883
12072
|
}
|
|
11884
12073
|
async function uninstallHook() {
|
|
11885
|
-
|
|
12074
|
+
projectHeading("hook uninstall", "\uD83E\uDE9D");
|
|
11886
12075
|
const hookPath = getHookPath();
|
|
11887
12076
|
if (!existsSync4(hookPath)) {
|
|
11888
12077
|
info("No commit-msg hook found. Nothing to uninstall.");
|
|
@@ -11898,7 +12087,7 @@ async function uninstallHook() {
|
|
|
11898
12087
|
}
|
|
11899
12088
|
|
|
11900
12089
|
// src/commands/log.ts
|
|
11901
|
-
var
|
|
12090
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
11902
12091
|
var log_default = defineCommand({
|
|
11903
12092
|
meta: {
|
|
11904
12093
|
name: "log",
|
|
@@ -11968,14 +12157,14 @@ var log_default = defineCommand({
|
|
|
11968
12157
|
usingFallback = true;
|
|
11969
12158
|
}
|
|
11970
12159
|
}
|
|
11971
|
-
|
|
12160
|
+
projectHeading("log", "\uD83D\uDCDC");
|
|
11972
12161
|
printModeHeader(mode, currentBranch, compareRef, usingFallback);
|
|
11973
12162
|
if (mode === "local" || mode === "remote") {
|
|
11974
12163
|
if (!compareRef) {
|
|
11975
12164
|
console.log();
|
|
11976
|
-
console.log(
|
|
11977
|
-
console.log(
|
|
11978
|
-
console.log(
|
|
12165
|
+
console.log(import_picocolors12.default.yellow(" ⚠ Could not determine a comparison branch."));
|
|
12166
|
+
console.log(import_picocolors12.default.dim(" No upstream tracking set and no remote base branch found."));
|
|
12167
|
+
console.log(import_picocolors12.default.dim(` Use ${import_picocolors12.default.bold("contrib log --full")} to see the full commit history instead.`));
|
|
11979
12168
|
console.log();
|
|
11980
12169
|
printGuidance();
|
|
11981
12170
|
return;
|
|
@@ -12017,26 +12206,26 @@ async function resolveBaseBranchRef(config) {
|
|
|
12017
12206
|
}
|
|
12018
12207
|
function printModeHeader(mode, currentBranch, compareRef, usingFallback = false) {
|
|
12019
12208
|
const branch = currentBranch ?? "HEAD";
|
|
12020
|
-
const fallbackNote = usingFallback ?
|
|
12209
|
+
const fallbackNote = usingFallback ? import_picocolors12.default.yellow(" (no upstream — comparing against base branch)") : "";
|
|
12021
12210
|
console.log();
|
|
12022
12211
|
switch (mode) {
|
|
12023
12212
|
case "local":
|
|
12024
|
-
console.log(
|
|
12213
|
+
console.log(import_picocolors12.default.dim(` mode: ${import_picocolors12.default.bold("local")} — unpushed commits on ${import_picocolors12.default.bold(branch)}`) + fallbackNote);
|
|
12025
12214
|
if (compareRef) {
|
|
12026
|
-
console.log(
|
|
12215
|
+
console.log(import_picocolors12.default.dim(` comparing: ${import_picocolors12.default.bold(compareRef)} ➜ ${import_picocolors12.default.bold("HEAD")}`));
|
|
12027
12216
|
}
|
|
12028
12217
|
break;
|
|
12029
12218
|
case "remote":
|
|
12030
|
-
console.log(
|
|
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);
|
|
12031
12220
|
if (compareRef) {
|
|
12032
|
-
console.log(
|
|
12221
|
+
console.log(import_picocolors12.default.dim(` comparing: ${import_picocolors12.default.bold("HEAD")} ➜ ${import_picocolors12.default.bold(compareRef)}`));
|
|
12033
12222
|
}
|
|
12034
12223
|
break;
|
|
12035
12224
|
case "full":
|
|
12036
|
-
console.log(
|
|
12225
|
+
console.log(import_picocolors12.default.dim(` mode: ${import_picocolors12.default.bold("full")} — complete commit history for ${import_picocolors12.default.bold(branch)}`));
|
|
12037
12226
|
break;
|
|
12038
12227
|
case "all":
|
|
12039
|
-
console.log(
|
|
12228
|
+
console.log(import_picocolors12.default.dim(` mode: ${import_picocolors12.default.bold("all")} — commits across all branches`));
|
|
12040
12229
|
break;
|
|
12041
12230
|
}
|
|
12042
12231
|
}
|
|
@@ -12062,7 +12251,7 @@ async function renderScopedLog(options) {
|
|
|
12062
12251
|
}
|
|
12063
12252
|
console.log();
|
|
12064
12253
|
for (const entry of entries) {
|
|
12065
|
-
const hashStr =
|
|
12254
|
+
const hashStr = import_picocolors12.default.yellow(entry.hash);
|
|
12066
12255
|
const refsStr = entry.refs ? ` ${colorizeRefs(entry.refs, protectedBranches, currentBranch)}` : "";
|
|
12067
12256
|
const subjectStr = colorizeSubject(entry.subject);
|
|
12068
12257
|
console.log(` ${hashStr}${refsStr} ${subjectStr}`);
|
|
@@ -12073,9 +12262,9 @@ async function renderScopedLog(options) {
|
|
|
12073
12262
|
function printEmptyState(mode) {
|
|
12074
12263
|
console.log();
|
|
12075
12264
|
if (mode === "local") {
|
|
12076
|
-
console.log(
|
|
12265
|
+
console.log(import_picocolors12.default.dim(" No local unpushed commits — you're up to date with remote!"));
|
|
12077
12266
|
} else {
|
|
12078
|
-
console.log(
|
|
12267
|
+
console.log(import_picocolors12.default.dim(" No remote-only commits — your local branch is up to date!"));
|
|
12079
12268
|
}
|
|
12080
12269
|
console.log();
|
|
12081
12270
|
}
|
|
@@ -12084,7 +12273,7 @@ async function renderFullLog(options) {
|
|
|
12084
12273
|
if (showGraph) {
|
|
12085
12274
|
const lines = await getLogGraph({ count, all, branch: targetBranch });
|
|
12086
12275
|
if (lines.length === 0) {
|
|
12087
|
-
console.log(
|
|
12276
|
+
console.log(import_picocolors12.default.dim(" No commits found."));
|
|
12088
12277
|
console.log();
|
|
12089
12278
|
return false;
|
|
12090
12279
|
}
|
|
@@ -12095,13 +12284,13 @@ async function renderFullLog(options) {
|
|
|
12095
12284
|
} else {
|
|
12096
12285
|
const entries = await getLogEntries({ count, all, branch: targetBranch });
|
|
12097
12286
|
if (entries.length === 0) {
|
|
12098
|
-
console.log(
|
|
12287
|
+
console.log(import_picocolors12.default.dim(" No commits found."));
|
|
12099
12288
|
console.log();
|
|
12100
12289
|
return false;
|
|
12101
12290
|
}
|
|
12102
12291
|
console.log();
|
|
12103
12292
|
for (const entry of entries) {
|
|
12104
|
-
const hashStr =
|
|
12293
|
+
const hashStr = import_picocolors12.default.yellow(entry.hash);
|
|
12105
12294
|
const refsStr = entry.refs ? ` ${colorizeRefs(entry.refs, protectedBranches, currentBranch)}` : "";
|
|
12106
12295
|
const subjectStr = colorizeSubject(entry.subject);
|
|
12107
12296
|
console.log(` ${hashStr}${refsStr} ${subjectStr}`);
|
|
@@ -12113,42 +12302,42 @@ function printFooter(mode, count, targetBranch) {
|
|
|
12113
12302
|
console.log();
|
|
12114
12303
|
switch (mode) {
|
|
12115
12304
|
case "local":
|
|
12116
|
-
console.log(
|
|
12305
|
+
console.log(import_picocolors12.default.dim(` Showing up to ${count} unpushed commits`));
|
|
12117
12306
|
break;
|
|
12118
12307
|
case "remote":
|
|
12119
|
-
console.log(
|
|
12308
|
+
console.log(import_picocolors12.default.dim(` Showing up to ${count} remote-only commits`));
|
|
12120
12309
|
break;
|
|
12121
12310
|
case "full":
|
|
12122
|
-
console.log(
|
|
12311
|
+
console.log(import_picocolors12.default.dim(` Showing ${count} most recent commits${targetBranch ? ` (${targetBranch})` : ""}`));
|
|
12123
12312
|
break;
|
|
12124
12313
|
case "all":
|
|
12125
|
-
console.log(
|
|
12314
|
+
console.log(import_picocolors12.default.dim(` Showing ${count} most recent commits (all branches)`));
|
|
12126
12315
|
break;
|
|
12127
12316
|
}
|
|
12128
12317
|
}
|
|
12129
12318
|
function printGuidance() {
|
|
12130
12319
|
console.log();
|
|
12131
|
-
console.log(
|
|
12132
|
-
console.log(
|
|
12133
|
-
console.log(
|
|
12134
|
-
console.log(
|
|
12135
|
-
console.log(
|
|
12136
|
-
console.log(
|
|
12137
|
-
console.log(
|
|
12138
|
-
console.log(
|
|
12320
|
+
console.log(import_picocolors12.default.dim(" ─── quick guide ───"));
|
|
12321
|
+
console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log")} local unpushed commits (default)`));
|
|
12322
|
+
console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log --remote")} commits on remote not yet pulled`));
|
|
12323
|
+
console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log --full")} full history for the current branch`));
|
|
12324
|
+
console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log --all")} commits across all branches`));
|
|
12325
|
+
console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log -n 50")} change the commit limit (default: 20)`));
|
|
12326
|
+
console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log -b dev")} view log for a specific branch`));
|
|
12327
|
+
console.log(import_picocolors12.default.dim(` ${import_picocolors12.default.bold("contrib log --no-graph")} flat list without graph lines`));
|
|
12139
12328
|
console.log();
|
|
12140
12329
|
}
|
|
12141
12330
|
function colorizeGraphLine(line, protectedBranches, currentBranch) {
|
|
12142
12331
|
const match = line.match(/^([|/\\*\s_.-]*)([a-f0-9]{7,12})(\s+\(([^)]+)\))?\s*(.*)/);
|
|
12143
12332
|
if (!match) {
|
|
12144
|
-
return
|
|
12333
|
+
return import_picocolors12.default.cyan(line);
|
|
12145
12334
|
}
|
|
12146
12335
|
const [, graphPart = "", hash, , refs, subject = ""] = match;
|
|
12147
12336
|
const parts = [];
|
|
12148
12337
|
if (graphPart) {
|
|
12149
12338
|
parts.push(colorizeGraphChars(graphPart));
|
|
12150
12339
|
}
|
|
12151
|
-
parts.push(
|
|
12340
|
+
parts.push(import_picocolors12.default.yellow(hash));
|
|
12152
12341
|
if (refs) {
|
|
12153
12342
|
parts.push(` (${colorizeRefs(refs, protectedBranches, currentBranch)})`);
|
|
12154
12343
|
}
|
|
@@ -12159,15 +12348,15 @@ function colorizeGraphChars(graphPart) {
|
|
|
12159
12348
|
return graphPart.split("").map((ch) => {
|
|
12160
12349
|
switch (ch) {
|
|
12161
12350
|
case "*":
|
|
12162
|
-
return
|
|
12351
|
+
return import_picocolors12.default.green(ch);
|
|
12163
12352
|
case "|":
|
|
12164
|
-
return
|
|
12353
|
+
return import_picocolors12.default.cyan(ch);
|
|
12165
12354
|
case "/":
|
|
12166
12355
|
case "\\":
|
|
12167
|
-
return
|
|
12356
|
+
return import_picocolors12.default.cyan(ch);
|
|
12168
12357
|
case "-":
|
|
12169
12358
|
case "_":
|
|
12170
|
-
return
|
|
12359
|
+
return import_picocolors12.default.cyan(ch);
|
|
12171
12360
|
default:
|
|
12172
12361
|
return ch;
|
|
12173
12362
|
}
|
|
@@ -12179,45 +12368,45 @@ function colorizeRefs(refs, protectedBranches, currentBranch) {
|
|
|
12179
12368
|
if (trimmed.startsWith("HEAD ->") || trimmed === "HEAD") {
|
|
12180
12369
|
const branchName = trimmed.replace("HEAD -> ", "");
|
|
12181
12370
|
if (trimmed === "HEAD") {
|
|
12182
|
-
return
|
|
12371
|
+
return import_picocolors12.default.bold(import_picocolors12.default.cyan("HEAD"));
|
|
12183
12372
|
}
|
|
12184
|
-
return `${
|
|
12373
|
+
return `${import_picocolors12.default.bold(import_picocolors12.default.cyan("HEAD"))} ${import_picocolors12.default.dim("->")} ${colorizeRefName(branchName, protectedBranches, currentBranch)}`;
|
|
12185
12374
|
}
|
|
12186
12375
|
if (trimmed.startsWith("tag:")) {
|
|
12187
|
-
return
|
|
12376
|
+
return import_picocolors12.default.bold(import_picocolors12.default.magenta(trimmed));
|
|
12188
12377
|
}
|
|
12189
12378
|
return colorizeRefName(trimmed, protectedBranches, currentBranch);
|
|
12190
|
-
}).join(
|
|
12379
|
+
}).join(import_picocolors12.default.dim(", "));
|
|
12191
12380
|
}
|
|
12192
12381
|
function colorizeRefName(name, protectedBranches, currentBranch) {
|
|
12193
12382
|
const isRemote = name.includes("/");
|
|
12194
12383
|
const localName = isRemote ? name.split("/").slice(1).join("/") : name;
|
|
12195
12384
|
if (protectedBranches.includes(localName)) {
|
|
12196
|
-
return isRemote ?
|
|
12385
|
+
return isRemote ? import_picocolors12.default.bold(import_picocolors12.default.red(name)) : import_picocolors12.default.bold(import_picocolors12.default.red(name));
|
|
12197
12386
|
}
|
|
12198
12387
|
if (localName === currentBranch) {
|
|
12199
|
-
return
|
|
12388
|
+
return import_picocolors12.default.bold(import_picocolors12.default.green(name));
|
|
12200
12389
|
}
|
|
12201
12390
|
if (isRemote) {
|
|
12202
|
-
return
|
|
12391
|
+
return import_picocolors12.default.blue(name);
|
|
12203
12392
|
}
|
|
12204
|
-
return
|
|
12393
|
+
return import_picocolors12.default.green(name);
|
|
12205
12394
|
}
|
|
12206
12395
|
function colorizeSubject(subject) {
|
|
12207
12396
|
const emojiMatch = subject.match(/^((?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)+\s*)/u);
|
|
12208
12397
|
if (emojiMatch) {
|
|
12209
12398
|
const emoji = emojiMatch[1];
|
|
12210
12399
|
const rest = subject.slice(emoji.length);
|
|
12211
|
-
return `${emoji}${
|
|
12400
|
+
return `${emoji}${import_picocolors12.default.white(rest)}`;
|
|
12212
12401
|
}
|
|
12213
12402
|
if (subject.startsWith("Merge ")) {
|
|
12214
|
-
return
|
|
12403
|
+
return import_picocolors12.default.dim(subject);
|
|
12215
12404
|
}
|
|
12216
|
-
return
|
|
12405
|
+
return import_picocolors12.default.white(subject);
|
|
12217
12406
|
}
|
|
12218
12407
|
|
|
12219
12408
|
// src/commands/save.ts
|
|
12220
|
-
var
|
|
12409
|
+
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
12221
12410
|
import { execFile as execFileCb4 } from "node:child_process";
|
|
12222
12411
|
function gitRun(args) {
|
|
12223
12412
|
return new Promise((resolve2) => {
|
|
@@ -12279,7 +12468,7 @@ var save_default = defineCommand({
|
|
|
12279
12468
|
}
|
|
12280
12469
|
});
|
|
12281
12470
|
async function handleSave(message) {
|
|
12282
|
-
|
|
12471
|
+
projectHeading("save", "\uD83D\uDCBE");
|
|
12283
12472
|
const currentBranch = await getCurrentBranch();
|
|
12284
12473
|
const label = message ?? `work-in-progress on ${currentBranch ?? "unknown"}`;
|
|
12285
12474
|
const stashMsg = `contrib-save: ${label}`;
|
|
@@ -12292,11 +12481,11 @@ async function handleSave(message) {
|
|
|
12292
12481
|
info("No uncommitted changes to save.");
|
|
12293
12482
|
return;
|
|
12294
12483
|
}
|
|
12295
|
-
success(`Saved: ${
|
|
12296
|
-
info(`Use ${
|
|
12484
|
+
success(`Saved: ${import_picocolors13.default.dim(label)}`);
|
|
12485
|
+
info(`Use ${import_picocolors13.default.bold("contrib save --restore")} to bring them back.`, "");
|
|
12297
12486
|
}
|
|
12298
12487
|
async function handleRestore() {
|
|
12299
|
-
|
|
12488
|
+
projectHeading("save --restore", "\uD83D\uDCBE");
|
|
12300
12489
|
const stashes = await getStashList();
|
|
12301
12490
|
if (stashes.length === 0) {
|
|
12302
12491
|
info("No saved changes found.");
|
|
@@ -12309,7 +12498,7 @@ async function handleRestore() {
|
|
|
12309
12498
|
warn("You may have conflicts. Resolve them and run `git stash drop` when done.");
|
|
12310
12499
|
process.exit(1);
|
|
12311
12500
|
}
|
|
12312
|
-
success(`Restored: ${
|
|
12501
|
+
success(`Restored: ${import_picocolors13.default.dim(stashes[0].message)}`);
|
|
12313
12502
|
return;
|
|
12314
12503
|
}
|
|
12315
12504
|
const choices = stashes.map((s2) => `${s2.index} ${s2.message}`);
|
|
@@ -12322,10 +12511,10 @@ async function handleRestore() {
|
|
|
12322
12511
|
process.exit(1);
|
|
12323
12512
|
}
|
|
12324
12513
|
const match = stashes.find((s2) => String(s2.index) === idx);
|
|
12325
|
-
success(`Restored: ${
|
|
12514
|
+
success(`Restored: ${import_picocolors13.default.dim(match?.message ?? "saved changes")}`);
|
|
12326
12515
|
}
|
|
12327
12516
|
async function handleList() {
|
|
12328
|
-
|
|
12517
|
+
projectHeading("save --list", "\uD83D\uDCBE");
|
|
12329
12518
|
const stashes = await getStashList();
|
|
12330
12519
|
if (stashes.length === 0) {
|
|
12331
12520
|
info("No saved changes.");
|
|
@@ -12333,16 +12522,16 @@ async function handleList() {
|
|
|
12333
12522
|
}
|
|
12334
12523
|
console.log();
|
|
12335
12524
|
for (const s2 of stashes) {
|
|
12336
|
-
const idx =
|
|
12525
|
+
const idx = import_picocolors13.default.dim(`[${s2.index}]`);
|
|
12337
12526
|
const msg = s2.message;
|
|
12338
12527
|
console.log(` ${idx} ${msg}`);
|
|
12339
12528
|
}
|
|
12340
12529
|
console.log();
|
|
12341
|
-
info(`Use ${
|
|
12342
|
-
info(`Use ${
|
|
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.`, "");
|
|
12343
12532
|
}
|
|
12344
12533
|
async function handleDrop() {
|
|
12345
|
-
|
|
12534
|
+
projectHeading("save --drop", "\uD83D\uDCBE");
|
|
12346
12535
|
const stashes = await getStashList();
|
|
12347
12536
|
if (stashes.length === 0) {
|
|
12348
12537
|
info("No saved changes to drop.");
|
|
@@ -12357,7 +12546,7 @@ async function handleDrop() {
|
|
|
12357
12546
|
process.exit(1);
|
|
12358
12547
|
}
|
|
12359
12548
|
const match = stashes.find((s2) => String(s2.index) === idx);
|
|
12360
|
-
success(`Dropped: ${
|
|
12549
|
+
success(`Dropped: ${import_picocolors13.default.dim(match?.message ?? "saved changes")}`);
|
|
12361
12550
|
}
|
|
12362
12551
|
async function getStashList() {
|
|
12363
12552
|
const result = await gitRun(["stash", "list"]);
|
|
@@ -12374,7 +12563,7 @@ async function getStashList() {
|
|
|
12374
12563
|
}
|
|
12375
12564
|
|
|
12376
12565
|
// src/commands/setup.ts
|
|
12377
|
-
var
|
|
12566
|
+
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
12378
12567
|
async function shouldContinueSetupWithExistingConfig(options) {
|
|
12379
12568
|
const {
|
|
12380
12569
|
existingConfig,
|
|
@@ -12422,7 +12611,7 @@ var setup_default = defineCommand({
|
|
|
12422
12611
|
error("Not inside a git repository. Run this command from within a git repo.");
|
|
12423
12612
|
process.exit(1);
|
|
12424
12613
|
}
|
|
12425
|
-
|
|
12614
|
+
projectHeading("setup", "\uD83D\uDD27");
|
|
12426
12615
|
const existingConfig = readConfig();
|
|
12427
12616
|
const shouldContinue = await shouldContinueSetupWithExistingConfig({
|
|
12428
12617
|
existingConfig,
|
|
@@ -12447,7 +12636,7 @@ var setup_default = defineCommand({
|
|
|
12447
12636
|
workflow = "github-flow";
|
|
12448
12637
|
else if (workflowChoice.startsWith("Git Flow"))
|
|
12449
12638
|
workflow = "git-flow";
|
|
12450
|
-
info(`Workflow: ${
|
|
12639
|
+
info(`Workflow: ${import_picocolors14.default.bold(WORKFLOW_DESCRIPTIONS[workflow])}`);
|
|
12451
12640
|
const conventionChoice = await selectPrompt("Which commit convention should this project use?", [
|
|
12452
12641
|
`${CONVENTION_DESCRIPTIONS["clean-commit"]} (recommended)`,
|
|
12453
12642
|
CONVENTION_DESCRIPTIONS.conventional,
|
|
@@ -12458,6 +12647,7 @@ var setup_default = defineCommand({
|
|
|
12458
12647
|
commitConvention = "conventional";
|
|
12459
12648
|
else if (conventionChoice.includes("No commit"))
|
|
12460
12649
|
commitConvention = "none";
|
|
12650
|
+
const enableAI = await confirmPrompt("Enable AI-assisted features like commit messages, branch naming, PR text, and conflict guidance?");
|
|
12461
12651
|
const remotes = await getRemotes();
|
|
12462
12652
|
if (remotes.length === 0) {
|
|
12463
12653
|
error("No git remotes found. Add a remote first (e.g., git remote add origin <url>).");
|
|
@@ -12511,15 +12701,15 @@ var setup_default = defineCommand({
|
|
|
12511
12701
|
detectedRole = roleChoice;
|
|
12512
12702
|
detectionSource = "user selection";
|
|
12513
12703
|
} else {
|
|
12514
|
-
info(`Detected role: ${
|
|
12515
|
-
const confirmed = await confirmPrompt(`Role detected as ${
|
|
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?`);
|
|
12516
12706
|
if (!confirmed) {
|
|
12517
12707
|
const roleChoice = await selectPrompt("Select your role:", ["maintainer", "contributor"]);
|
|
12518
12708
|
detectedRole = roleChoice;
|
|
12519
12709
|
}
|
|
12520
12710
|
}
|
|
12521
12711
|
const defaultConfig = getDefaultConfig();
|
|
12522
|
-
info(
|
|
12712
|
+
info(import_picocolors14.default.dim("Tip: press Enter to keep the default branch name shown in each prompt."));
|
|
12523
12713
|
const mainBranchDefault = defaultConfig.mainBranch;
|
|
12524
12714
|
const mainBranch = await inputPrompt(`Main branch name (default: ${mainBranchDefault} — press Enter to keep)`, mainBranchDefault);
|
|
12525
12715
|
let devBranch;
|
|
@@ -12545,7 +12735,7 @@ var setup_default = defineCommand({
|
|
|
12545
12735
|
error("Setup cannot continue without the upstream remote for contributors.");
|
|
12546
12736
|
process.exit(1);
|
|
12547
12737
|
}
|
|
12548
|
-
success(`Added remote ${
|
|
12738
|
+
success(`Added remote ${import_picocolors14.default.bold(upstreamRemote)} → ${upstreamUrl}`);
|
|
12549
12739
|
} else {
|
|
12550
12740
|
error("An upstream remote URL is required for contributors.");
|
|
12551
12741
|
info("Add it manually: git remote add upstream <url>", "");
|
|
@@ -12561,22 +12751,23 @@ var setup_default = defineCommand({
|
|
|
12561
12751
|
upstream: upstreamRemote,
|
|
12562
12752
|
origin: originRemote,
|
|
12563
12753
|
branchPrefixes: defaultConfig.branchPrefixes,
|
|
12564
|
-
commitConvention
|
|
12754
|
+
commitConvention,
|
|
12755
|
+
aiEnabled: enableAI
|
|
12565
12756
|
};
|
|
12566
12757
|
writeConfig(config);
|
|
12567
12758
|
success(`Config written to .contributerc.json`);
|
|
12568
12759
|
const syncRemote = config.role === "contributor" ? config.upstream : config.origin;
|
|
12569
|
-
info(`Fetching ${
|
|
12760
|
+
info(`Fetching ${import_picocolors14.default.bold(syncRemote)} to verify branch configuration...`, "");
|
|
12570
12761
|
await fetchRemote(syncRemote);
|
|
12571
12762
|
const mainRef = `${syncRemote}/${config.mainBranch}`;
|
|
12572
12763
|
if (!await refExists(mainRef)) {
|
|
12573
|
-
warn(`Main branch ref ${
|
|
12764
|
+
warn(`Main branch ref ${import_picocolors14.default.bold(mainRef)} not found on remote.`);
|
|
12574
12765
|
warn("Config was saved — verify the branch name and re-run setup if needed.");
|
|
12575
12766
|
}
|
|
12576
12767
|
if (config.devBranch) {
|
|
12577
12768
|
const devRef = `${syncRemote}/${config.devBranch}`;
|
|
12578
12769
|
if (!await refExists(devRef)) {
|
|
12579
|
-
warn(`Dev branch ref ${
|
|
12770
|
+
warn(`Dev branch ref ${import_picocolors14.default.bold(devRef)} not found on remote.`);
|
|
12580
12771
|
warn("Config was saved — verify the branch name and re-run setup if needed.");
|
|
12581
12772
|
}
|
|
12582
12773
|
}
|
|
@@ -12584,31 +12775,33 @@ var setup_default = defineCommand({
|
|
|
12584
12775
|
info("Added .contributerc.json to .gitignore to avoid committing personal config.");
|
|
12585
12776
|
}
|
|
12586
12777
|
console.log();
|
|
12587
|
-
info(`Workflow: ${
|
|
12588
|
-
info(`Convention: ${
|
|
12589
|
-
info(`
|
|
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)}`);
|
|
12590
12782
|
if (config.devBranch) {
|
|
12591
|
-
info(`Main: ${
|
|
12783
|
+
info(`Main: ${import_picocolors14.default.bold(config.mainBranch)} | Dev: ${import_picocolors14.default.bold(config.devBranch)}`);
|
|
12592
12784
|
} else {
|
|
12593
|
-
info(`Main: ${
|
|
12785
|
+
info(`Main: ${import_picocolors14.default.bold(config.mainBranch)}`);
|
|
12594
12786
|
}
|
|
12595
|
-
info(`Origin: ${
|
|
12787
|
+
info(`Origin: ${import_picocolors14.default.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${import_picocolors14.default.bold(config.upstream)}` : ""}`);
|
|
12596
12788
|
}
|
|
12597
12789
|
});
|
|
12598
12790
|
function logConfigSummary(config) {
|
|
12599
|
-
info(`Workflow: ${
|
|
12600
|
-
info(`Convention: ${
|
|
12601
|
-
info(`
|
|
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)}`);
|
|
12602
12795
|
if (config.devBranch) {
|
|
12603
|
-
info(`Main: ${
|
|
12796
|
+
info(`Main: ${import_picocolors14.default.bold(config.mainBranch)} | Dev: ${import_picocolors14.default.bold(config.devBranch)}`);
|
|
12604
12797
|
} else {
|
|
12605
|
-
info(`Main: ${
|
|
12798
|
+
info(`Main: ${import_picocolors14.default.bold(config.mainBranch)}`);
|
|
12606
12799
|
}
|
|
12607
|
-
info(`Origin: ${
|
|
12800
|
+
info(`Origin: ${import_picocolors14.default.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${import_picocolors14.default.bold(config.upstream)}` : ""}`);
|
|
12608
12801
|
}
|
|
12609
12802
|
|
|
12610
12803
|
// src/commands/start.ts
|
|
12611
|
-
var
|
|
12804
|
+
var import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
12612
12805
|
var start_default = defineCommand({
|
|
12613
12806
|
meta: {
|
|
12614
12807
|
name: "start",
|
|
@@ -12648,57 +12841,28 @@ var start_default = defineCommand({
|
|
|
12648
12841
|
const { branchPrefixes } = config;
|
|
12649
12842
|
const baseBranch = getBaseBranch(config);
|
|
12650
12843
|
const syncSource = getSyncSource(config);
|
|
12651
|
-
let branchName = args.name;
|
|
12652
|
-
|
|
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
|
+
});
|
|
12653
12852
|
if (!branchName) {
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
error("A branch name or description is required.");
|
|
12657
|
-
process.exit(1);
|
|
12658
|
-
}
|
|
12659
|
-
branchName = branchName.trim();
|
|
12660
|
-
}
|
|
12661
|
-
const useAI = !args["no-ai"] && looksLikeNaturalLanguage(branchName);
|
|
12662
|
-
if (useAI) {
|
|
12663
|
-
const spinner = createSpinner("Generating branch name suggestion...");
|
|
12664
|
-
const suggested = await suggestBranchName(branchName, args.model);
|
|
12665
|
-
if (suggested) {
|
|
12666
|
-
spinner.success("Branch name suggestion ready.");
|
|
12667
|
-
console.log(`
|
|
12668
|
-
${import_picocolors14.default.dim("AI suggestion:")} ${import_picocolors14.default.bold(import_picocolors14.default.cyan(suggested))}`);
|
|
12669
|
-
const accepted = await confirmPrompt(`Use ${import_picocolors14.default.bold(suggested)} as your branch name?`);
|
|
12670
|
-
if (accepted) {
|
|
12671
|
-
branchName = suggested;
|
|
12672
|
-
} else {
|
|
12673
|
-
branchName = await inputPrompt("Enter branch name", branchName);
|
|
12674
|
-
}
|
|
12675
|
-
} else {
|
|
12676
|
-
spinner.fail("AI did not return a branch name suggestion.");
|
|
12677
|
-
}
|
|
12678
|
-
}
|
|
12679
|
-
if (!hasPrefix(branchName, branchPrefixes)) {
|
|
12680
|
-
const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors14.default.bold(branchName)}:`, branchPrefixes);
|
|
12681
|
-
branchName = formatBranchName(prefix, branchName);
|
|
12682
|
-
}
|
|
12683
|
-
if (!isValidBranchName(branchName)) {
|
|
12684
|
-
error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
|
|
12685
|
-
process.exit(1);
|
|
12686
|
-
}
|
|
12687
|
-
info(`Creating branch: ${import_picocolors14.default.bold(branchName)}`);
|
|
12688
|
-
if (await branchExists(branchName)) {
|
|
12689
|
-
error(`Branch ${import_picocolors14.default.bold(branchName)} already exists.`);
|
|
12690
|
-
info(` Use ${import_picocolors14.default.bold(`git checkout ${branchName}`)} to switch to it, or choose a different name.`, "");
|
|
12691
|
-
process.exit(1);
|
|
12853
|
+
warn("Start cancelled.");
|
|
12854
|
+
process.exit(0);
|
|
12692
12855
|
}
|
|
12856
|
+
info(`Creating branch: ${import_picocolors15.default.bold(branchName)}`);
|
|
12693
12857
|
await fetchRemote(syncSource.remote);
|
|
12694
12858
|
if (!await refExists(syncSource.ref)) {
|
|
12695
|
-
warn(`Remote ref ${
|
|
12859
|
+
warn(`Remote ref ${import_picocolors15.default.bold(syncSource.ref)} not found. Creating branch from local ${import_picocolors15.default.bold(baseBranch)}.`);
|
|
12696
12860
|
}
|
|
12697
12861
|
const currentBranch = await getCurrentBranch();
|
|
12698
12862
|
if (currentBranch === baseBranch && await refExists(syncSource.ref)) {
|
|
12699
12863
|
const ahead = await countCommitsAhead(baseBranch, syncSource.ref);
|
|
12700
12864
|
if (ahead > 0) {
|
|
12701
|
-
warn(`You are on ${
|
|
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)}.`);
|
|
12702
12866
|
info(" Syncing will discard those commits. Consider backing them up first (e.g. create a branch).");
|
|
12703
12867
|
const proceed = await confirmPrompt("Discard local commits and sync to remote?");
|
|
12704
12868
|
if (!proceed) {
|
|
@@ -12715,10 +12879,10 @@ var start_default = defineCommand({
|
|
|
12715
12879
|
error(`Failed to create branch: ${result2.stderr}`);
|
|
12716
12880
|
process.exit(1);
|
|
12717
12881
|
}
|
|
12718
|
-
success(`Created ${
|
|
12882
|
+
success(`Created ${import_picocolors15.default.bold(branchName)} from ${import_picocolors15.default.bold(syncSource.ref)}`);
|
|
12719
12883
|
return;
|
|
12720
12884
|
}
|
|
12721
|
-
error(`Failed to update ${
|
|
12885
|
+
error(`Failed to update ${import_picocolors15.default.bold(baseBranch)}: ${updateResult.stderr}`);
|
|
12722
12886
|
info("Make sure your base branch exists locally or the remote ref is available.", "");
|
|
12723
12887
|
process.exit(1);
|
|
12724
12888
|
}
|
|
@@ -12727,12 +12891,12 @@ var start_default = defineCommand({
|
|
|
12727
12891
|
error(`Failed to create branch: ${result.stderr}`);
|
|
12728
12892
|
process.exit(1);
|
|
12729
12893
|
}
|
|
12730
|
-
success(`Created ${
|
|
12894
|
+
success(`Created ${import_picocolors15.default.bold(branchName)} from latest ${import_picocolors15.default.bold(baseBranch)}`);
|
|
12731
12895
|
}
|
|
12732
12896
|
});
|
|
12733
12897
|
|
|
12734
12898
|
// src/commands/status.ts
|
|
12735
|
-
var
|
|
12899
|
+
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
12736
12900
|
var status_default = defineCommand({
|
|
12737
12901
|
meta: {
|
|
12738
12902
|
name: "status",
|
|
@@ -12748,9 +12912,9 @@ var status_default = defineCommand({
|
|
|
12748
12912
|
error("No .contributerc.json found. Run `contrib setup` first.");
|
|
12749
12913
|
process.exit(1);
|
|
12750
12914
|
}
|
|
12751
|
-
|
|
12752
|
-
console.log(` ${
|
|
12753
|
-
console.log(` ${
|
|
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)}`);
|
|
12754
12918
|
console.log();
|
|
12755
12919
|
await fetchAll();
|
|
12756
12920
|
const currentBranch = await getCurrentBranch();
|
|
@@ -12759,7 +12923,7 @@ var status_default = defineCommand({
|
|
|
12759
12923
|
const isContributor = config.role === "contributor";
|
|
12760
12924
|
const [dirty, fileStatus] = await Promise.all([hasUncommittedChanges(), getFileStatus()]);
|
|
12761
12925
|
if (dirty) {
|
|
12762
|
-
console.log(` ${
|
|
12926
|
+
console.log(` ${import_picocolors16.default.yellow("⚠")} ${import_picocolors16.default.yellow("Uncommitted changes in working tree")}`);
|
|
12763
12927
|
console.log();
|
|
12764
12928
|
}
|
|
12765
12929
|
const mainRemote = `${origin}/${mainBranch}`;
|
|
@@ -12778,16 +12942,16 @@ var status_default = defineCommand({
|
|
|
12778
12942
|
if (isFeatureBranch) {
|
|
12779
12943
|
const branchDiv = await getDivergence(currentBranch, baseBranch);
|
|
12780
12944
|
const branchLine = formatStatus(currentBranch, baseBranch, branchDiv.ahead, branchDiv.behind);
|
|
12781
|
-
console.log(branchLine +
|
|
12945
|
+
console.log(branchLine + import_picocolors16.default.dim(` (current ${import_picocolors16.default.green("*")})`));
|
|
12782
12946
|
branchStatus = await detectBranchStatus(currentBranch, baseBranch);
|
|
12783
12947
|
if (branchStatus.merged) {
|
|
12784
|
-
console.log(` ${
|
|
12948
|
+
console.log(` ${import_picocolors16.default.green("✓")} ${import_picocolors16.default.green("Branch merged")} — ${import_picocolors16.default.dim(branchStatus.mergedReason ?? "all commits reachable from base")}`);
|
|
12785
12949
|
}
|
|
12786
12950
|
if (branchStatus.stale) {
|
|
12787
|
-
console.log(` ${
|
|
12951
|
+
console.log(` ${import_picocolors16.default.yellow("⏳")} ${import_picocolors16.default.yellow("Branch is stale")} — ${import_picocolors16.default.dim(`last commit ${branchStatus.staleDaysAgo} days ago`)}`);
|
|
12788
12952
|
}
|
|
12789
12953
|
} else if (currentBranch) {
|
|
12790
|
-
console.log(
|
|
12954
|
+
console.log(import_picocolors16.default.dim(` (on ${import_picocolors16.default.bold(currentBranch)} branch)`));
|
|
12791
12955
|
}
|
|
12792
12956
|
let branchesAligned = true;
|
|
12793
12957
|
{
|
|
@@ -12817,20 +12981,20 @@ var status_default = defineCommand({
|
|
|
12817
12981
|
}
|
|
12818
12982
|
branchesAligned = groups.size === 1;
|
|
12819
12983
|
console.log();
|
|
12820
|
-
console.log(` ${
|
|
12984
|
+
console.log(` ${import_picocolors16.default.bold("\uD83D\uDD17 Branch Alignment")}`);
|
|
12821
12985
|
for (const [hash, names] of groups) {
|
|
12822
12986
|
const short = hash.slice(0, 7);
|
|
12823
|
-
const nameStr = names.map((n2) =>
|
|
12824
|
-
console.log(` ${
|
|
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}`);
|
|
12825
12989
|
const subject = await getCommitSubject(hash);
|
|
12826
12990
|
if (subject) {
|
|
12827
|
-
console.log(` ${
|
|
12991
|
+
console.log(` ${import_picocolors16.default.dim(subject)}`);
|
|
12828
12992
|
}
|
|
12829
12993
|
}
|
|
12830
12994
|
if (branchesAligned) {
|
|
12831
|
-
console.log(` ${
|
|
12995
|
+
console.log(` ${import_picocolors16.default.green("✓")} ${import_picocolors16.default.green("All branches aligned")} ${import_picocolors16.default.dim("— ready to start")}`);
|
|
12832
12996
|
} else {
|
|
12833
|
-
console.log(` ${
|
|
12997
|
+
console.log(` ${import_picocolors16.default.yellow("⚠")} ${import_picocolors16.default.yellow("Branches are not fully aligned")}`);
|
|
12834
12998
|
}
|
|
12835
12999
|
}
|
|
12836
13000
|
}
|
|
@@ -12838,70 +13002,70 @@ var status_default = defineCommand({
|
|
|
12838
13002
|
if (hasFiles) {
|
|
12839
13003
|
console.log();
|
|
12840
13004
|
if (fileStatus.staged.length > 0) {
|
|
12841
|
-
console.log(` ${
|
|
13005
|
+
console.log(` ${import_picocolors16.default.green("Staged for commit:")}`);
|
|
12842
13006
|
for (const { file, status } of fileStatus.staged) {
|
|
12843
|
-
console.log(` ${
|
|
13007
|
+
console.log(` ${import_picocolors16.default.green("+")} ${import_picocolors16.default.dim(`${status}:`)} ${file}`);
|
|
12844
13008
|
}
|
|
12845
13009
|
}
|
|
12846
13010
|
if (fileStatus.modified.length > 0) {
|
|
12847
|
-
console.log(` ${
|
|
13011
|
+
console.log(` ${import_picocolors16.default.yellow("Unstaged changes:")}`);
|
|
12848
13012
|
for (const { file, status } of fileStatus.modified) {
|
|
12849
|
-
console.log(` ${
|
|
13013
|
+
console.log(` ${import_picocolors16.default.yellow("~")} ${import_picocolors16.default.dim(`${status}:`)} ${file}`);
|
|
12850
13014
|
}
|
|
12851
13015
|
}
|
|
12852
13016
|
if (fileStatus.untracked.length > 0) {
|
|
12853
|
-
console.log(` ${
|
|
13017
|
+
console.log(` ${import_picocolors16.default.red("Untracked files:")}`);
|
|
12854
13018
|
for (const file of fileStatus.untracked) {
|
|
12855
|
-
console.log(` ${
|
|
13019
|
+
console.log(` ${import_picocolors16.default.red("?")} ${file}`);
|
|
12856
13020
|
}
|
|
12857
13021
|
}
|
|
12858
13022
|
} else if (!dirty) {
|
|
12859
|
-
console.log(` ${
|
|
13023
|
+
console.log(` ${import_picocolors16.default.green("✓")} ${import_picocolors16.default.dim("Working tree clean")}`);
|
|
12860
13024
|
}
|
|
12861
13025
|
const tips = [];
|
|
12862
13026
|
if (!branchesAligned) {
|
|
12863
|
-
tips.push(`Run ${
|
|
13027
|
+
tips.push(`Run ${import_picocolors16.default.bold("contrib sync")} to align your local branches with the remote`);
|
|
12864
13028
|
}
|
|
12865
13029
|
if (fileStatus.staged.length > 0) {
|
|
12866
|
-
tips.push(`Run ${
|
|
13030
|
+
tips.push(`Run ${import_picocolors16.default.bold("contrib commit")} to commit staged changes`);
|
|
12867
13031
|
}
|
|
12868
13032
|
if (fileStatus.modified.length > 0 || fileStatus.untracked.length > 0) {
|
|
12869
|
-
tips.push(`Run ${
|
|
13033
|
+
tips.push(`Run ${import_picocolors16.default.bold("contrib commit")} to stage and commit changes`);
|
|
12870
13034
|
}
|
|
12871
13035
|
if (isFeatureBranch && branchStatus) {
|
|
12872
13036
|
if (branchStatus.merged) {
|
|
12873
|
-
tips.push(`Run ${
|
|
13037
|
+
tips.push(`Run ${import_picocolors16.default.bold("contrib clean")} to delete this merged branch`);
|
|
12874
13038
|
} else if (branchStatus.stale) {
|
|
12875
|
-
tips.push(`Run ${
|
|
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`);
|
|
12876
13040
|
} else if (fileStatus.staged.length === 0 && fileStatus.modified.length === 0 && fileStatus.untracked.length === 0) {
|
|
12877
13041
|
const branchDiv = await getDivergence(currentBranch, `${origin}/${currentBranch}`);
|
|
12878
13042
|
if (branchDiv.ahead > 0) {
|
|
12879
|
-
tips.push(`Run ${
|
|
13043
|
+
tips.push(`Run ${import_picocolors16.default.bold("contrib submit")} to push and create/update your PR`);
|
|
12880
13044
|
}
|
|
12881
13045
|
}
|
|
12882
13046
|
}
|
|
12883
13047
|
if (tips.length > 0) {
|
|
12884
13048
|
console.log();
|
|
12885
|
-
console.log(` ${
|
|
13049
|
+
console.log(` ${import_picocolors16.default.dim("\uD83D\uDCA1 Tip:")}`);
|
|
12886
13050
|
for (const tip of tips) {
|
|
12887
|
-
console.log(` ${
|
|
13051
|
+
console.log(` ${import_picocolors16.default.dim(tip)}`);
|
|
12888
13052
|
}
|
|
12889
13053
|
}
|
|
12890
13054
|
console.log();
|
|
12891
13055
|
}
|
|
12892
13056
|
});
|
|
12893
13057
|
function formatStatus(branch, base, ahead, behind) {
|
|
12894
|
-
const label =
|
|
13058
|
+
const label = import_picocolors16.default.bold(branch.padEnd(20));
|
|
12895
13059
|
if (ahead === 0 && behind === 0) {
|
|
12896
|
-
return ` ${
|
|
13060
|
+
return ` ${import_picocolors16.default.green("✓")} ${label} ${import_picocolors16.default.dim(`in sync with ${base}`)}`;
|
|
12897
13061
|
}
|
|
12898
13062
|
if (ahead > 0 && behind === 0) {
|
|
12899
|
-
return ` ${
|
|
13063
|
+
return ` ${import_picocolors16.default.yellow("↑")} ${label} ${import_picocolors16.default.yellow(`${ahead} commit${ahead !== 1 ? "s" : ""} ahead of ${base}`)}`;
|
|
12900
13064
|
}
|
|
12901
13065
|
if (behind > 0 && ahead === 0) {
|
|
12902
|
-
return ` ${
|
|
13066
|
+
return ` ${import_picocolors16.default.red("↓")} ${label} ${import_picocolors16.default.red(`${behind} commit${behind !== 1 ? "s" : ""} behind ${base}`)}`;
|
|
12903
13067
|
}
|
|
12904
|
-
return ` ${
|
|
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)}`;
|
|
12905
13069
|
}
|
|
12906
13070
|
var STALE_THRESHOLD_DAYS = 14;
|
|
12907
13071
|
async function detectBranchStatus(branch, baseBranch) {
|
|
@@ -12947,34 +13111,45 @@ async function detectBranchStatus(branch, baseBranch) {
|
|
|
12947
13111
|
}
|
|
12948
13112
|
|
|
12949
13113
|
// src/commands/submit.ts
|
|
12950
|
-
var
|
|
13114
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
12951
13115
|
async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
12952
|
-
info(`Checking out ${
|
|
13116
|
+
info(`Checking out ${import_picocolors17.default.bold(baseBranch)}...`);
|
|
12953
13117
|
const coResult = await checkoutBranch(baseBranch);
|
|
12954
13118
|
if (coResult.exitCode !== 0) {
|
|
12955
13119
|
error(`Failed to checkout ${baseBranch}: ${coResult.stderr}`);
|
|
12956
13120
|
process.exit(1);
|
|
12957
13121
|
}
|
|
12958
|
-
info(`Squash merging ${
|
|
13122
|
+
info(`Squash merging ${import_picocolors17.default.bold(featureBranch)} into ${import_picocolors17.default.bold(baseBranch)}...`);
|
|
12959
13123
|
const mergeResult = await mergeSquash(featureBranch);
|
|
12960
13124
|
if (mergeResult.exitCode !== 0) {
|
|
12961
13125
|
error(`Squash merge failed: ${mergeResult.stderr}`);
|
|
12962
13126
|
process.exit(1);
|
|
12963
13127
|
}
|
|
12964
13128
|
let message = options?.defaultMsg;
|
|
12965
|
-
if (!message) {
|
|
12966
|
-
const copilotError = await
|
|
13129
|
+
if (!message && options?.useAI !== false) {
|
|
13130
|
+
const copilotError = await checkCopilotAvailable2();
|
|
12967
13131
|
if (!copilotError) {
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
|
|
13132
|
+
while (!message) {
|
|
13133
|
+
const spinner = createSpinner("Generating AI commit message for squash merge...");
|
|
13134
|
+
const [stagedDiff, stagedFiles] = await Promise.all([getStagedDiff(), getStagedFiles()]);
|
|
13135
|
+
const aiMsg = await generateCommitMessage(stagedDiff, stagedFiles, options?.model, options?.convention ?? "clean-commit", "squash-merge");
|
|
13136
|
+
if (aiMsg) {
|
|
13137
|
+
message = aiMsg;
|
|
13138
|
+
spinner.success("AI commit message generated.");
|
|
13139
|
+
console.log(`
|
|
13140
|
+
${import_picocolors17.default.dim("AI suggestion:")} ${import_picocolors17.default.bold(import_picocolors17.default.cyan(message))}`);
|
|
13141
|
+
break;
|
|
13142
|
+
}
|
|
12977
13143
|
spinner.fail("AI did not return a commit message.");
|
|
13144
|
+
const retryAction = await selectPrompt("AI could not generate a commit message. What would you like to do?", ["Try again with AI", "Write manually", "Cancel"]);
|
|
13145
|
+
if (retryAction === "Try again with AI") {
|
|
13146
|
+
continue;
|
|
13147
|
+
}
|
|
13148
|
+
if (retryAction === "Cancel") {
|
|
13149
|
+
warn("Squash merge commit cancelled.");
|
|
13150
|
+
process.exit(0);
|
|
13151
|
+
}
|
|
13152
|
+
break;
|
|
12978
13153
|
}
|
|
12979
13154
|
} else {
|
|
12980
13155
|
warn(`AI unavailable: ${copilotError}`);
|
|
@@ -12983,12 +13158,11 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
12983
13158
|
let finalMsg = null;
|
|
12984
13159
|
if (message) {
|
|
12985
13160
|
while (!finalMsg) {
|
|
12986
|
-
const
|
|
12987
|
-
|
|
12988
|
-
|
|
12989
|
-
|
|
12990
|
-
|
|
12991
|
-
]);
|
|
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);
|
|
12992
13166
|
if (action === "Accept this message") {
|
|
12993
13167
|
finalMsg = message;
|
|
12994
13168
|
} else if (action === "Edit this message") {
|
|
@@ -13001,10 +13175,10 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
13001
13175
|
message = regen;
|
|
13002
13176
|
spinner.success("Commit message regenerated.");
|
|
13003
13177
|
console.log(`
|
|
13004
|
-
${
|
|
13178
|
+
${import_picocolors17.default.dim("AI suggestion:")} ${import_picocolors17.default.bold(import_picocolors17.default.cyan(regen))}`);
|
|
13005
13179
|
} else {
|
|
13006
13180
|
spinner.fail("Regeneration failed.");
|
|
13007
|
-
|
|
13181
|
+
continue;
|
|
13008
13182
|
}
|
|
13009
13183
|
} else {
|
|
13010
13184
|
finalMsg = await inputPrompt("Enter commit message");
|
|
@@ -13018,13 +13192,13 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
13018
13192
|
error(`Commit failed: ${commitResult.stderr}`);
|
|
13019
13193
|
process.exit(1);
|
|
13020
13194
|
}
|
|
13021
|
-
info(`Pushing ${
|
|
13195
|
+
info(`Pushing ${import_picocolors17.default.bold(baseBranch)} to ${origin}...`);
|
|
13022
13196
|
const pushResult = await pushBranch(origin, baseBranch);
|
|
13023
13197
|
if (pushResult.exitCode !== 0) {
|
|
13024
13198
|
error(`Failed to push ${baseBranch}: ${pushResult.stderr}`);
|
|
13025
13199
|
process.exit(1);
|
|
13026
13200
|
}
|
|
13027
|
-
info(`Deleting local branch ${
|
|
13201
|
+
info(`Deleting local branch ${import_picocolors17.default.bold(featureBranch)}...`);
|
|
13028
13202
|
const delLocal = await forceDeleteBranch(featureBranch);
|
|
13029
13203
|
if (delLocal.exitCode !== 0) {
|
|
13030
13204
|
warn(`Could not delete local branch: ${delLocal.stderr.trim()}`);
|
|
@@ -13032,14 +13206,14 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
13032
13206
|
const remoteBranchRef = `${origin}/${featureBranch}`;
|
|
13033
13207
|
const remoteExists = await branchExists(remoteBranchRef);
|
|
13034
13208
|
if (remoteExists) {
|
|
13035
|
-
info(`Deleting remote branch ${
|
|
13209
|
+
info(`Deleting remote branch ${import_picocolors17.default.bold(featureBranch)}...`);
|
|
13036
13210
|
const delRemote = await deleteRemoteBranch(origin, featureBranch);
|
|
13037
13211
|
if (delRemote.exitCode !== 0) {
|
|
13038
13212
|
warn(`Could not delete remote branch: ${delRemote.stderr.trim()}`);
|
|
13039
13213
|
}
|
|
13040
13214
|
}
|
|
13041
|
-
success(`Squash merged ${
|
|
13042
|
-
info(`Run ${
|
|
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.`, "");
|
|
13043
13217
|
}
|
|
13044
13218
|
var submit_default = defineCommand({
|
|
13045
13219
|
meta: {
|
|
@@ -13052,6 +13226,18 @@ var submit_default = defineCommand({
|
|
|
13052
13226
|
description: "Create PR as draft",
|
|
13053
13227
|
default: false
|
|
13054
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
|
+
},
|
|
13055
13241
|
"no-ai": {
|
|
13056
13242
|
type: "boolean",
|
|
13057
13243
|
description: "Skip AI PR description generation",
|
|
@@ -13074,6 +13260,7 @@ var submit_default = defineCommand({
|
|
|
13074
13260
|
process.exit(1);
|
|
13075
13261
|
}
|
|
13076
13262
|
const { origin } = config;
|
|
13263
|
+
const aiEnabled = isAIEnabled(config, args["no-ai"]);
|
|
13077
13264
|
const baseBranch = getBaseBranch(config);
|
|
13078
13265
|
const protectedBranches = getProtectedBranches(config);
|
|
13079
13266
|
const currentBranch = await getCurrentBranch();
|
|
@@ -13082,8 +13269,8 @@ var submit_default = defineCommand({
|
|
|
13082
13269
|
process.exit(1);
|
|
13083
13270
|
}
|
|
13084
13271
|
if (protectedBranches.includes(currentBranch)) {
|
|
13085
|
-
|
|
13086
|
-
warn(`You're on ${
|
|
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.`);
|
|
13087
13274
|
await fetchAll();
|
|
13088
13275
|
const remoteRef = `${origin}/${currentBranch}`;
|
|
13089
13276
|
const localWork = await hasLocalWork(origin, currentBranch);
|
|
@@ -13092,11 +13279,11 @@ var submit_default = defineCommand({
|
|
|
13092
13279
|
const hasAnything = hasCommits || dirty;
|
|
13093
13280
|
if (!hasAnything) {
|
|
13094
13281
|
error("No local changes or commits to move. Switch to a feature branch first.");
|
|
13095
|
-
info(` Run ${
|
|
13282
|
+
info(` Run ${import_picocolors17.default.bold("contrib start")} to create a new feature branch.`, "");
|
|
13096
13283
|
process.exit(1);
|
|
13097
13284
|
}
|
|
13098
13285
|
if (hasCommits) {
|
|
13099
|
-
info(`Found ${
|
|
13286
|
+
info(`Found ${import_picocolors17.default.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${import_picocolors17.default.bold(currentBranch)}.`);
|
|
13100
13287
|
}
|
|
13101
13288
|
if (dirty) {
|
|
13102
13289
|
info("You also have uncommitted changes in the working tree.");
|
|
@@ -13112,58 +13299,35 @@ var submit_default = defineCommand({
|
|
|
13112
13299
|
info("No changes made. You are still on your current branch.");
|
|
13113
13300
|
return;
|
|
13114
13301
|
}
|
|
13115
|
-
|
|
13116
|
-
|
|
13117
|
-
|
|
13118
|
-
|
|
13119
|
-
|
|
13120
|
-
|
|
13121
|
-
|
|
13122
|
-
|
|
13123
|
-
if (suggested) {
|
|
13124
|
-
spinner.success("Branch name suggestion ready.");
|
|
13125
|
-
console.log(`
|
|
13126
|
-
${import_picocolors16.default.dim("AI suggestion:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(suggested))}`);
|
|
13127
|
-
const accepted = await confirmPrompt(`Use ${import_picocolors16.default.bold(suggested)} as your branch name?`);
|
|
13128
|
-
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
13129
|
-
} else {
|
|
13130
|
-
spinner.fail("AI did not return a suggestion.");
|
|
13131
|
-
newBranchName = await inputPrompt("Enter branch name", description);
|
|
13132
|
-
}
|
|
13133
|
-
}
|
|
13134
|
-
}
|
|
13135
|
-
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
13136
|
-
const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors16.default.bold(newBranchName)}:`, config.branchPrefixes);
|
|
13137
|
-
newBranchName = formatBranchName(prefix, newBranchName);
|
|
13138
|
-
}
|
|
13139
|
-
if (!isValidBranchName(newBranchName)) {
|
|
13140
|
-
error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
|
|
13141
|
-
process.exit(1);
|
|
13142
|
-
}
|
|
13143
|
-
if (await branchExists(newBranchName)) {
|
|
13144
|
-
error(`Branch ${import_picocolors16.default.bold(newBranchName)} already exists. Choose a different name.`);
|
|
13145
|
-
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;
|
|
13146
13310
|
}
|
|
13147
13311
|
const branchResult = await createBranch(newBranchName);
|
|
13148
13312
|
if (branchResult.exitCode !== 0) {
|
|
13149
13313
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
13150
13314
|
process.exit(1);
|
|
13151
13315
|
}
|
|
13152
|
-
success(`Created ${
|
|
13316
|
+
success(`Created ${import_picocolors17.default.bold(newBranchName)} with your changes.`);
|
|
13153
13317
|
await updateLocalBranch(currentBranch, remoteRef);
|
|
13154
|
-
info(`Reset ${
|
|
13318
|
+
info(`Reset ${import_picocolors17.default.bold(currentBranch)} back to ${import_picocolors17.default.bold(remoteRef)} — no damage done.`, "");
|
|
13155
13319
|
console.log();
|
|
13156
|
-
success(`You're now on ${
|
|
13157
|
-
info(`Run ${
|
|
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.`, "");
|
|
13158
13322
|
return;
|
|
13159
13323
|
}
|
|
13160
|
-
|
|
13324
|
+
projectHeading("submit", "\uD83D\uDE80");
|
|
13161
13325
|
const ghInstalled = await checkGhInstalled();
|
|
13162
13326
|
const ghAuthed = ghInstalled && await checkGhAuth();
|
|
13163
13327
|
if (ghInstalled && ghAuthed) {
|
|
13164
13328
|
const mergedPR = await getMergedPRForBranch(currentBranch);
|
|
13165
13329
|
if (mergedPR) {
|
|
13166
|
-
warn(`PR #${mergedPR.number} (${
|
|
13330
|
+
warn(`PR #${mergedPR.number} (${import_picocolors17.default.bold(mergedPR.title)}) was already merged.`);
|
|
13167
13331
|
const localWork = await hasLocalWork(origin, currentBranch);
|
|
13168
13332
|
const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
|
|
13169
13333
|
if (hasWork) {
|
|
@@ -13171,7 +13335,7 @@ var submit_default = defineCommand({
|
|
|
13171
13335
|
warn("You have uncommitted changes in your working tree.");
|
|
13172
13336
|
}
|
|
13173
13337
|
if (localWork.unpushedCommits > 0) {
|
|
13174
|
-
warn(`You have ${
|
|
13338
|
+
warn(`You have ${import_picocolors17.default.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not in the merged PR.`);
|
|
13175
13339
|
}
|
|
13176
13340
|
const SAVE_NEW_BRANCH = "Save changes to a new branch";
|
|
13177
13341
|
const DISCARD = "Discard all changes and clean up";
|
|
@@ -13182,46 +13346,26 @@ var submit_default = defineCommand({
|
|
|
13182
13346
|
return;
|
|
13183
13347
|
}
|
|
13184
13348
|
if (action === SAVE_NEW_BRANCH) {
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
console.log(`
|
|
13194
|
-
${import_picocolors16.default.dim("AI suggestion:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(suggested))}`);
|
|
13195
|
-
const accepted = await confirmPrompt(`Use ${import_picocolors16.default.bold(suggested)} as your branch name?`);
|
|
13196
|
-
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
13197
|
-
} else {
|
|
13198
|
-
spinner.fail("AI did not return a suggestion.");
|
|
13199
|
-
newBranchName = await inputPrompt("Enter branch name", description);
|
|
13200
|
-
}
|
|
13201
|
-
}
|
|
13202
|
-
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
13203
|
-
const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors16.default.bold(newBranchName)}:`, config.branchPrefixes);
|
|
13204
|
-
newBranchName = formatBranchName(prefix, newBranchName);
|
|
13205
|
-
}
|
|
13206
|
-
if (!isValidBranchName(newBranchName)) {
|
|
13207
|
-
error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
|
|
13208
|
-
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;
|
|
13209
13357
|
}
|
|
13210
13358
|
const staleUpstream = await getUpstreamRef();
|
|
13211
13359
|
const staleUpstreamHash = staleUpstream ? await getCommitHash(staleUpstream) : null;
|
|
13212
|
-
if (await branchExists(newBranchName)) {
|
|
13213
|
-
error(`Branch ${import_picocolors16.default.bold(newBranchName)} already exists. Choose a different name.`);
|
|
13214
|
-
process.exit(1);
|
|
13215
|
-
}
|
|
13216
13360
|
const renameResult = await renameBranch(currentBranch, newBranchName);
|
|
13217
13361
|
if (renameResult.exitCode !== 0) {
|
|
13218
13362
|
error(`Failed to rename branch: ${renameResult.stderr}`);
|
|
13219
13363
|
process.exit(1);
|
|
13220
13364
|
}
|
|
13221
|
-
success(`Renamed ${
|
|
13365
|
+
success(`Renamed ${import_picocolors17.default.bold(currentBranch)} → ${import_picocolors17.default.bold(newBranchName)}`);
|
|
13222
13366
|
await unsetUpstream();
|
|
13223
13367
|
const syncSource2 = getSyncSource(config);
|
|
13224
|
-
info(`Syncing ${
|
|
13368
|
+
info(`Syncing ${import_picocolors17.default.bold(newBranchName)} with latest ${import_picocolors17.default.bold(baseBranch)}...`);
|
|
13225
13369
|
await fetchRemote(syncSource2.remote);
|
|
13226
13370
|
let rebaseResult;
|
|
13227
13371
|
if (staleUpstreamHash) {
|
|
@@ -13232,17 +13376,17 @@ var submit_default = defineCommand({
|
|
|
13232
13376
|
}
|
|
13233
13377
|
if (rebaseResult.exitCode !== 0) {
|
|
13234
13378
|
warn("Rebase encountered conflicts. Resolve them manually, then run:");
|
|
13235
|
-
info(` ${
|
|
13379
|
+
info(` ${import_picocolors17.default.bold("git rebase --continue")}`, "");
|
|
13236
13380
|
} else {
|
|
13237
|
-
success(`Rebased ${
|
|
13381
|
+
success(`Rebased ${import_picocolors17.default.bold(newBranchName)} onto ${import_picocolors17.default.bold(syncSource2.ref)}.`);
|
|
13238
13382
|
}
|
|
13239
|
-
info(`All your changes are preserved. Run ${
|
|
13383
|
+
info(`All your changes are preserved. Run ${import_picocolors17.default.bold("contrib submit")} when ready to create a new PR.`, "");
|
|
13240
13384
|
return;
|
|
13241
13385
|
}
|
|
13242
13386
|
warn("Discarding local changes...");
|
|
13243
13387
|
}
|
|
13244
13388
|
const syncSource = getSyncSource(config);
|
|
13245
|
-
info(`Switching to ${
|
|
13389
|
+
info(`Switching to ${import_picocolors17.default.bold(baseBranch)} and syncing...`);
|
|
13246
13390
|
await fetchRemote(syncSource.remote);
|
|
13247
13391
|
await resetHard("HEAD");
|
|
13248
13392
|
const coResult = await checkoutBranch(baseBranch);
|
|
@@ -13251,23 +13395,23 @@ var submit_default = defineCommand({
|
|
|
13251
13395
|
process.exit(1);
|
|
13252
13396
|
}
|
|
13253
13397
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
13254
|
-
success(`Synced ${
|
|
13255
|
-
info(`Deleting stale branch ${
|
|
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)}...`);
|
|
13256
13400
|
const delResult = await forceDeleteBranch(currentBranch);
|
|
13257
13401
|
if (delResult.exitCode === 0) {
|
|
13258
|
-
success(`Deleted ${
|
|
13402
|
+
success(`Deleted ${import_picocolors17.default.bold(currentBranch)}.`);
|
|
13259
13403
|
} else {
|
|
13260
13404
|
warn(`Could not delete branch: ${delResult.stderr.trim()}`);
|
|
13261
13405
|
}
|
|
13262
13406
|
console.log();
|
|
13263
|
-
info(`You're now on ${
|
|
13407
|
+
info(`You're now on ${import_picocolors17.default.bold(baseBranch)}. Run ${import_picocolors17.default.bold("contrib start")} to begin a new feature.`);
|
|
13264
13408
|
return;
|
|
13265
13409
|
}
|
|
13266
13410
|
}
|
|
13267
13411
|
if (ghInstalled && ghAuthed) {
|
|
13268
13412
|
const existingPR = await getPRForBranch(currentBranch);
|
|
13269
13413
|
if (existingPR) {
|
|
13270
|
-
info(`Pushing ${
|
|
13414
|
+
info(`Pushing ${import_picocolors17.default.bold(currentBranch)} to ${origin}...`);
|
|
13271
13415
|
const pushResult2 = await pushSetUpstream(origin, currentBranch);
|
|
13272
13416
|
if (pushResult2.exitCode !== 0) {
|
|
13273
13417
|
error(`Failed to push: ${pushResult2.stderr}`);
|
|
@@ -13278,8 +13422,8 @@ var submit_default = defineCommand({
|
|
|
13278
13422
|
}
|
|
13279
13423
|
process.exit(1);
|
|
13280
13424
|
}
|
|
13281
|
-
success(`Pushed changes to existing PR #${existingPR.number}: ${
|
|
13282
|
-
console.log(` ${
|
|
13425
|
+
success(`Pushed changes to existing PR #${existingPR.number}: ${import_picocolors17.default.bold(existingPR.title)}`);
|
|
13426
|
+
console.log(` ${import_picocolors17.default.cyan(existingPR.url)}`);
|
|
13283
13427
|
return;
|
|
13284
13428
|
}
|
|
13285
13429
|
}
|
|
@@ -13287,7 +13431,7 @@ var submit_default = defineCommand({
|
|
|
13287
13431
|
let prBody = null;
|
|
13288
13432
|
async function tryGenerateAI() {
|
|
13289
13433
|
const [copilotError, commits, diff] = await Promise.all([
|
|
13290
|
-
|
|
13434
|
+
checkCopilotAvailable2(),
|
|
13291
13435
|
getLog(baseBranch, "HEAD"),
|
|
13292
13436
|
getLogDiff(baseBranch, "HEAD")
|
|
13293
13437
|
]);
|
|
@@ -13299,10 +13443,10 @@ var submit_default = defineCommand({
|
|
|
13299
13443
|
prBody = result.body;
|
|
13300
13444
|
spinner.success("PR description generated.");
|
|
13301
13445
|
console.log(`
|
|
13302
|
-
${
|
|
13446
|
+
${import_picocolors17.default.dim("AI title:")} ${import_picocolors17.default.bold(import_picocolors17.default.cyan(prTitle))}`);
|
|
13303
13447
|
console.log(`
|
|
13304
|
-
${
|
|
13305
|
-
console.log(
|
|
13448
|
+
${import_picocolors17.default.dim("AI body preview:")}`);
|
|
13449
|
+
console.log(import_picocolors17.default.dim(prBody.slice(0, 300) + (prBody.length > 300 ? "..." : "")));
|
|
13306
13450
|
} else {
|
|
13307
13451
|
spinner.fail("AI did not return a PR description.");
|
|
13308
13452
|
}
|
|
@@ -13315,7 +13459,23 @@ ${import_picocolors16.default.dim("AI body preview:")}`);
|
|
|
13315
13459
|
const REGENERATE = "Regenerate AI description";
|
|
13316
13460
|
let submitAction = "cancel";
|
|
13317
13461
|
const isMaintainer = config.role === "maintainer";
|
|
13318
|
-
if (
|
|
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) {
|
|
13319
13479
|
const maintainerChoice = await selectPrompt("How would you like to submit your changes?", ["Create a PR", SQUASH_LOCAL, CANCEL]);
|
|
13320
13480
|
if (maintainerChoice === CANCEL) {
|
|
13321
13481
|
warn("Submit cancelled.");
|
|
@@ -13324,12 +13484,13 @@ ${import_picocolors16.default.dim("AI body preview:")}`);
|
|
|
13324
13484
|
if (maintainerChoice === SQUASH_LOCAL) {
|
|
13325
13485
|
await performSquashMerge(origin, baseBranch, currentBranch, {
|
|
13326
13486
|
model: args.model,
|
|
13327
|
-
convention: config.commitConvention
|
|
13487
|
+
convention: config.commitConvention,
|
|
13488
|
+
useAI: aiEnabled
|
|
13328
13489
|
});
|
|
13329
13490
|
return;
|
|
13330
13491
|
}
|
|
13331
13492
|
}
|
|
13332
|
-
if (
|
|
13493
|
+
if (aiEnabled) {
|
|
13333
13494
|
await tryGenerateAI();
|
|
13334
13495
|
}
|
|
13335
13496
|
let actionResolved = false;
|
|
@@ -13368,7 +13529,7 @@ ${import_picocolors16.default.dim("AI body preview:")}`);
|
|
|
13368
13529
|
}
|
|
13369
13530
|
} else {
|
|
13370
13531
|
const choices = [];
|
|
13371
|
-
if (
|
|
13532
|
+
if (aiEnabled)
|
|
13372
13533
|
choices.push(REGENERATE);
|
|
13373
13534
|
choices.push("Write title & body manually", "Use gh --fill (auto-fill from commits)", CANCEL);
|
|
13374
13535
|
const action = await selectPrompt("How would you like to create the PR?", choices);
|
|
@@ -13392,7 +13553,7 @@ ${import_picocolors16.default.dim("AI body preview:")}`);
|
|
|
13392
13553
|
warn("Submit cancelled.");
|
|
13393
13554
|
return;
|
|
13394
13555
|
}
|
|
13395
|
-
info(`Pushing ${
|
|
13556
|
+
info(`Pushing ${import_picocolors17.default.bold(currentBranch)} to ${origin}...`);
|
|
13396
13557
|
const pushResult = await pushSetUpstream(origin, currentBranch);
|
|
13397
13558
|
if (pushResult.exitCode !== 0) {
|
|
13398
13559
|
error(`Failed to push: ${pushResult.stderr}`);
|
|
@@ -13411,7 +13572,7 @@ ${import_picocolors16.default.dim("AI body preview:")}`);
|
|
|
13411
13572
|
const prUrl = `https://github.com/${repoInfo.owner}/${repoInfo.repo}/compare/${baseBranch}...${currentBranch}?expand=1`;
|
|
13412
13573
|
console.log();
|
|
13413
13574
|
info("Create your PR manually:", "");
|
|
13414
|
-
console.log(` ${
|
|
13575
|
+
console.log(` ${import_picocolors17.default.cyan(prUrl)}`);
|
|
13415
13576
|
} else {
|
|
13416
13577
|
info("gh CLI not available. Create your PR manually on GitHub.", "");
|
|
13417
13578
|
}
|
|
@@ -13445,7 +13606,7 @@ ${import_picocolors16.default.dim("AI body preview:")}`);
|
|
|
13445
13606
|
});
|
|
13446
13607
|
|
|
13447
13608
|
// src/commands/switch.ts
|
|
13448
|
-
var
|
|
13609
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
13449
13610
|
var switch_default = defineCommand({
|
|
13450
13611
|
meta: {
|
|
13451
13612
|
name: "switch",
|
|
@@ -13466,7 +13627,7 @@ var switch_default = defineCommand({
|
|
|
13466
13627
|
const config = readConfig();
|
|
13467
13628
|
const protectedBranches = config ? getProtectedBranches(config) : ["main", "master"];
|
|
13468
13629
|
const currentBranch = await getCurrentBranch();
|
|
13469
|
-
|
|
13630
|
+
projectHeading("switch", "\uD83D\uDD00");
|
|
13470
13631
|
let targetBranch = args.name;
|
|
13471
13632
|
if (!targetBranch) {
|
|
13472
13633
|
const localBranches = await getLocalBranches();
|
|
@@ -13477,11 +13638,11 @@ var switch_default = defineCommand({
|
|
|
13477
13638
|
const choices = localBranches.filter((b2) => b2.name !== currentBranch).map((b2) => {
|
|
13478
13639
|
const labels = [];
|
|
13479
13640
|
if (protectedBranches.includes(b2.name))
|
|
13480
|
-
labels.push(
|
|
13641
|
+
labels.push(import_picocolors18.default.red("protected"));
|
|
13481
13642
|
if (b2.upstream)
|
|
13482
|
-
labels.push(
|
|
13643
|
+
labels.push(import_picocolors18.default.dim(`→ ${b2.upstream}`));
|
|
13483
13644
|
if (b2.gone)
|
|
13484
|
-
labels.push(
|
|
13645
|
+
labels.push(import_picocolors18.default.red("remote gone"));
|
|
13485
13646
|
const suffix = labels.length > 0 ? ` ${labels.join(" · ")}` : "";
|
|
13486
13647
|
return `${b2.name}${suffix}`;
|
|
13487
13648
|
});
|
|
@@ -13493,7 +13654,7 @@ var switch_default = defineCommand({
|
|
|
13493
13654
|
targetBranch = selected.split(/\s{2,}/)[0].trim();
|
|
13494
13655
|
}
|
|
13495
13656
|
if (targetBranch === currentBranch) {
|
|
13496
|
-
info(`Already on ${
|
|
13657
|
+
info(`Already on ${import_picocolors18.default.bold(targetBranch)}.`);
|
|
13497
13658
|
return;
|
|
13498
13659
|
}
|
|
13499
13660
|
if (await hasUncommittedChanges()) {
|
|
@@ -13512,7 +13673,7 @@ var switch_default = defineCommand({
|
|
|
13512
13673
|
const stashMsg = `contrib-save: auto-save from ${currentBranch}`;
|
|
13513
13674
|
try {
|
|
13514
13675
|
await exec("git", ["stash", "push", "-m", stashMsg]);
|
|
13515
|
-
info(`Saved changes: ${
|
|
13676
|
+
info(`Saved changes: ${import_picocolors18.default.dim(stashMsg)}`);
|
|
13516
13677
|
} catch {
|
|
13517
13678
|
error("Failed to save changes. Please commit or save manually.");
|
|
13518
13679
|
process.exit(1);
|
|
@@ -13528,9 +13689,9 @@ var switch_default = defineCommand({
|
|
|
13528
13689
|
}
|
|
13529
13690
|
process.exit(1);
|
|
13530
13691
|
}
|
|
13531
|
-
success(`Switched to ${
|
|
13532
|
-
info(`Your changes from ${
|
|
13533
|
-
info(`Use ${
|
|
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.`, "");
|
|
13534
13695
|
return;
|
|
13535
13696
|
}
|
|
13536
13697
|
const result = await checkoutBranch(targetBranch);
|
|
@@ -13538,12 +13699,12 @@ var switch_default = defineCommand({
|
|
|
13538
13699
|
error(`Failed to switch to ${targetBranch}: ${result.stderr}`);
|
|
13539
13700
|
process.exit(1);
|
|
13540
13701
|
}
|
|
13541
|
-
success(`Switched to ${
|
|
13702
|
+
success(`Switched to ${import_picocolors18.default.bold(targetBranch)}`);
|
|
13542
13703
|
}
|
|
13543
13704
|
});
|
|
13544
13705
|
|
|
13545
13706
|
// src/commands/sync.ts
|
|
13546
|
-
var
|
|
13707
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
13547
13708
|
var sync_default = defineCommand({
|
|
13548
13709
|
meta: {
|
|
13549
13710
|
name: "sync",
|
|
@@ -13582,7 +13743,7 @@ var sync_default = defineCommand({
|
|
|
13582
13743
|
error("You have uncommitted changes. Please commit or stash them before syncing.");
|
|
13583
13744
|
process.exit(1);
|
|
13584
13745
|
}
|
|
13585
|
-
|
|
13746
|
+
projectHeading(`sync (${workflow}, ${role})`, "\uD83D\uDD04");
|
|
13586
13747
|
const baseBranch = getBaseBranch(config);
|
|
13587
13748
|
const syncSource = getSyncSource(config);
|
|
13588
13749
|
info(`Fetching ${syncSource.remote}...`);
|
|
@@ -13595,24 +13756,24 @@ var sync_default = defineCommand({
|
|
|
13595
13756
|
await fetchRemote(origin);
|
|
13596
13757
|
}
|
|
13597
13758
|
if (!await refExists(syncSource.ref)) {
|
|
13598
|
-
error(`Remote ref ${
|
|
13759
|
+
error(`Remote ref ${import_picocolors19.default.bold(syncSource.ref)} does not exist.`);
|
|
13599
13760
|
info("This can happen if the branch was renamed or deleted on the remote.", "");
|
|
13600
|
-
info(`Check your config: the base branch may need updating via ${
|
|
13761
|
+
info(`Check your config: the base branch may need updating via ${import_picocolors19.default.bold("contrib setup")}.`, "");
|
|
13601
13762
|
process.exit(1);
|
|
13602
13763
|
}
|
|
13603
13764
|
let allowMergeCommit = false;
|
|
13604
13765
|
const div = await getDivergence(baseBranch, syncSource.ref);
|
|
13605
13766
|
if (div.ahead > 0 || div.behind > 0) {
|
|
13606
|
-
info(`${
|
|
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}`);
|
|
13607
13768
|
} else {
|
|
13608
|
-
info(`${
|
|
13769
|
+
info(`${import_picocolors19.default.bold(baseBranch)} is already in sync with ${syncSource.ref}`);
|
|
13609
13770
|
}
|
|
13610
13771
|
if (div.ahead > 0) {
|
|
13611
13772
|
const currentBranch = await getCurrentBranch();
|
|
13612
13773
|
const protectedBranches = getProtectedBranches(config);
|
|
13613
13774
|
const isOnProtected = currentBranch && protectedBranches.includes(currentBranch);
|
|
13614
13775
|
if (isOnProtected) {
|
|
13615
|
-
warn(`You have ${
|
|
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.`);
|
|
13616
13777
|
info("Pulling now could create a merge commit, which breaks clean history.");
|
|
13617
13778
|
console.log();
|
|
13618
13779
|
const MOVE_BRANCH = "Move my commits to a new feature branch, then sync";
|
|
@@ -13628,44 +13789,21 @@ var sync_default = defineCommand({
|
|
|
13628
13789
|
return;
|
|
13629
13790
|
}
|
|
13630
13791
|
if (action === MOVE_BRANCH) {
|
|
13631
|
-
|
|
13632
|
-
|
|
13633
|
-
|
|
13634
|
-
|
|
13635
|
-
|
|
13636
|
-
|
|
13637
|
-
|
|
13638
|
-
|
|
13639
|
-
if (suggested) {
|
|
13640
|
-
spinner.success("Branch name suggestion ready.");
|
|
13641
|
-
console.log(`
|
|
13642
|
-
${import_picocolors18.default.dim("AI suggestion:")} ${import_picocolors18.default.bold(import_picocolors18.default.cyan(suggested))}`);
|
|
13643
|
-
const accepted = await confirmPrompt(`Use ${import_picocolors18.default.bold(suggested)} as your branch name?`);
|
|
13644
|
-
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
13645
|
-
} else {
|
|
13646
|
-
spinner.fail("AI did not return a suggestion.");
|
|
13647
|
-
newBranchName = await inputPrompt("Enter branch name", description);
|
|
13648
|
-
}
|
|
13649
|
-
}
|
|
13650
|
-
}
|
|
13651
|
-
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
13652
|
-
const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors18.default.bold(newBranchName)}:`, config.branchPrefixes);
|
|
13653
|
-
newBranchName = formatBranchName(prefix, newBranchName);
|
|
13654
|
-
}
|
|
13655
|
-
if (!isValidBranchName(newBranchName)) {
|
|
13656
|
-
error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
|
|
13657
|
-
process.exit(1);
|
|
13658
|
-
}
|
|
13659
|
-
if (await branchExists(newBranchName)) {
|
|
13660
|
-
error(`Branch ${import_picocolors18.default.bold(newBranchName)} already exists. Choose a different name.`);
|
|
13661
|
-
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;
|
|
13662
13800
|
}
|
|
13663
13801
|
const branchResult = await createBranch(newBranchName);
|
|
13664
13802
|
if (branchResult.exitCode !== 0) {
|
|
13665
13803
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
13666
13804
|
process.exit(1);
|
|
13667
13805
|
}
|
|
13668
|
-
success(`Created ${
|
|
13806
|
+
success(`Created ${import_picocolors19.default.bold(newBranchName)} with your commits.`);
|
|
13669
13807
|
const coResult2 = await checkoutBranch(baseBranch);
|
|
13670
13808
|
if (coResult2.exitCode !== 0) {
|
|
13671
13809
|
error(`Failed to checkout ${baseBranch}: ${coResult2.stderr}`);
|
|
@@ -13673,11 +13811,11 @@ var sync_default = defineCommand({
|
|
|
13673
13811
|
}
|
|
13674
13812
|
const remoteRef = syncSource.ref;
|
|
13675
13813
|
await updateLocalBranch(baseBranch, remoteRef);
|
|
13676
|
-
success(`Reset ${
|
|
13677
|
-
success(`${
|
|
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}`);
|
|
13678
13816
|
console.log();
|
|
13679
|
-
info(`Your commits are safe on ${
|
|
13680
|
-
info(`Run ${
|
|
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)}.`, "");
|
|
13681
13819
|
return;
|
|
13682
13820
|
}
|
|
13683
13821
|
allowMergeCommit = true;
|
|
@@ -13685,7 +13823,7 @@ var sync_default = defineCommand({
|
|
|
13685
13823
|
}
|
|
13686
13824
|
}
|
|
13687
13825
|
if (!args.yes) {
|
|
13688
|
-
const ok = await confirmPrompt(`This will pull ${
|
|
13826
|
+
const ok = await confirmPrompt(`This will pull ${import_picocolors19.default.bold(syncSource.ref)} into local ${import_picocolors19.default.bold(baseBranch)}.`);
|
|
13689
13827
|
if (!ok)
|
|
13690
13828
|
process.exit(0);
|
|
13691
13829
|
}
|
|
@@ -13699,8 +13837,8 @@ var sync_default = defineCommand({
|
|
|
13699
13837
|
if (allowMergeCommit) {
|
|
13700
13838
|
error(`Pull failed: ${pullResult.stderr.trim()}`);
|
|
13701
13839
|
} else {
|
|
13702
|
-
error(`Fast-forward pull failed. Your local ${
|
|
13703
|
-
info(`Use ${
|
|
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.`, "");
|
|
13704
13842
|
}
|
|
13705
13843
|
process.exit(1);
|
|
13706
13844
|
}
|
|
@@ -13708,7 +13846,7 @@ var sync_default = defineCommand({
|
|
|
13708
13846
|
if (hasDevBranch(workflow) && role === "maintainer") {
|
|
13709
13847
|
const mainDiv = await getDivergence(config.mainBranch, `${origin}/${config.mainBranch}`);
|
|
13710
13848
|
if (mainDiv.behind > 0) {
|
|
13711
|
-
info(`Also syncing ${
|
|
13849
|
+
info(`Also syncing ${import_picocolors19.default.bold(config.mainBranch)}...`);
|
|
13712
13850
|
const mainCoResult = await checkoutBranch(config.mainBranch);
|
|
13713
13851
|
if (mainCoResult.exitCode === 0) {
|
|
13714
13852
|
const mainPullResult = await pullFastForwardOnly(origin, config.mainBranch);
|
|
@@ -13746,20 +13884,20 @@ var sync_default = defineCommand({
|
|
|
13746
13884
|
groups.get(hash).push(name);
|
|
13747
13885
|
}
|
|
13748
13886
|
console.log();
|
|
13749
|
-
console.log(` ${
|
|
13887
|
+
console.log(` ${import_picocolors19.default.bold("\uD83D\uDD17 Branch Alignment")}`);
|
|
13750
13888
|
for (const [hash, names] of groups) {
|
|
13751
13889
|
const short = hash.slice(0, 7);
|
|
13752
|
-
const nameStr = names.map((n2) =>
|
|
13753
|
-
console.log(` ${
|
|
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}`);
|
|
13754
13892
|
const subject = await getCommitSubject(hash);
|
|
13755
13893
|
if (subject) {
|
|
13756
|
-
console.log(` ${
|
|
13894
|
+
console.log(` ${import_picocolors19.default.dim(subject)}`);
|
|
13757
13895
|
}
|
|
13758
13896
|
}
|
|
13759
13897
|
if (groups.size === 1) {
|
|
13760
|
-
console.log(` ${
|
|
13898
|
+
console.log(` ${import_picocolors19.default.green("✓")} ${import_picocolors19.default.green("All branches aligned")} ${import_picocolors19.default.dim("— ready to start")}`);
|
|
13761
13899
|
} else {
|
|
13762
|
-
console.log(` ${
|
|
13900
|
+
console.log(` ${import_picocolors19.default.yellow("⚠")} ${import_picocolors19.default.yellow("Branches are not fully aligned")}`);
|
|
13763
13901
|
}
|
|
13764
13902
|
}
|
|
13765
13903
|
}
|
|
@@ -13768,7 +13906,7 @@ var sync_default = defineCommand({
|
|
|
13768
13906
|
|
|
13769
13907
|
// src/commands/update.ts
|
|
13770
13908
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
13771
|
-
var
|
|
13909
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
13772
13910
|
var update_default = defineCommand({
|
|
13773
13911
|
meta: {
|
|
13774
13912
|
name: "update",
|
|
@@ -13805,8 +13943,8 @@ var update_default = defineCommand({
|
|
|
13805
13943
|
process.exit(1);
|
|
13806
13944
|
}
|
|
13807
13945
|
if (protectedBranches.includes(currentBranch)) {
|
|
13808
|
-
|
|
13809
|
-
warn(`You're on ${
|
|
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.`);
|
|
13810
13948
|
await fetchAll();
|
|
13811
13949
|
const { origin } = config;
|
|
13812
13950
|
const remoteRef = `${origin}/${currentBranch}`;
|
|
@@ -13815,12 +13953,12 @@ var update_default = defineCommand({
|
|
|
13815
13953
|
const hasCommits = localWork.unpushedCommits > 0;
|
|
13816
13954
|
const hasAnything = hasCommits || dirty;
|
|
13817
13955
|
if (!hasAnything) {
|
|
13818
|
-
info(`No local changes found on ${
|
|
13819
|
-
info(`Use ${
|
|
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.`);
|
|
13820
13958
|
process.exit(1);
|
|
13821
13959
|
}
|
|
13822
13960
|
if (hasCommits) {
|
|
13823
|
-
info(`Found ${
|
|
13961
|
+
info(`Found ${import_picocolors20.default.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${import_picocolors20.default.bold(currentBranch)}.`);
|
|
13824
13962
|
}
|
|
13825
13963
|
if (dirty) {
|
|
13826
13964
|
info("You also have uncommitted changes in the working tree.");
|
|
@@ -13836,56 +13974,37 @@ var update_default = defineCommand({
|
|
|
13836
13974
|
info("No changes made. You are still on your current branch.");
|
|
13837
13975
|
return;
|
|
13838
13976
|
}
|
|
13839
|
-
|
|
13840
|
-
|
|
13841
|
-
|
|
13842
|
-
|
|
13843
|
-
|
|
13844
|
-
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
if (suggested) {
|
|
13848
|
-
spinner.success("Branch name suggestion ready.");
|
|
13849
|
-
console.log(`
|
|
13850
|
-
${import_picocolors19.default.dim("AI suggestion:")} ${import_picocolors19.default.bold(import_picocolors19.default.cyan(suggested))}`);
|
|
13851
|
-
const accepted = await confirmPrompt(`Use ${import_picocolors19.default.bold(suggested)} as your branch name?`);
|
|
13852
|
-
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
13853
|
-
} else {
|
|
13854
|
-
spinner.fail("AI did not return a suggestion.");
|
|
13855
|
-
newBranchName = await inputPrompt("Enter branch name", description);
|
|
13856
|
-
}
|
|
13857
|
-
}
|
|
13858
|
-
}
|
|
13859
|
-
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
13860
|
-
const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors19.default.bold(newBranchName)}:`, config.branchPrefixes);
|
|
13861
|
-
newBranchName = formatBranchName(prefix, newBranchName);
|
|
13862
|
-
}
|
|
13863
|
-
if (!isValidBranchName(newBranchName)) {
|
|
13864
|
-
error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
|
|
13865
|
-
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;
|
|
13866
13985
|
}
|
|
13867
13986
|
const branchResult = await createBranch(newBranchName);
|
|
13868
13987
|
if (branchResult.exitCode !== 0) {
|
|
13869
13988
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
13870
13989
|
process.exit(1);
|
|
13871
13990
|
}
|
|
13872
|
-
success(`Created ${
|
|
13991
|
+
success(`Created ${import_picocolors20.default.bold(newBranchName)} with your changes.`);
|
|
13873
13992
|
await updateLocalBranch(currentBranch, remoteRef);
|
|
13874
|
-
info(`Reset ${
|
|
13993
|
+
info(`Reset ${import_picocolors20.default.bold(currentBranch)} back to ${import_picocolors20.default.bold(remoteRef)} — no damage done.`, "");
|
|
13875
13994
|
console.log();
|
|
13876
|
-
success(`You're now on ${
|
|
13877
|
-
info(`Run ${
|
|
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)}.`, "");
|
|
13878
13997
|
return;
|
|
13879
13998
|
}
|
|
13880
13999
|
if (await hasUncommittedChanges()) {
|
|
13881
14000
|
error("You have uncommitted changes. Please commit or stash them first.");
|
|
13882
14001
|
process.exit(1);
|
|
13883
14002
|
}
|
|
13884
|
-
|
|
14003
|
+
projectHeading("update", "\uD83D\uDD03");
|
|
13885
14004
|
const mergedPR = await getMergedPRForBranch(currentBranch);
|
|
13886
14005
|
if (mergedPR) {
|
|
13887
|
-
warn(`PR #${mergedPR.number} (${
|
|
13888
|
-
info(`Link: ${
|
|
14006
|
+
warn(`PR #${mergedPR.number} (${import_picocolors20.default.bold(mergedPR.title)}) has already been merged.`);
|
|
14007
|
+
info(`Link: ${import_picocolors20.default.underline(mergedPR.url)}`, "");
|
|
13889
14008
|
const localWork = await hasLocalWork(syncSource.remote, currentBranch);
|
|
13890
14009
|
const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
|
|
13891
14010
|
if (hasWork) {
|
|
@@ -13898,49 +14017,29 @@ var update_default = defineCommand({
|
|
|
13898
14017
|
const SAVE_NEW_BRANCH = "Save changes to a new branch";
|
|
13899
14018
|
const DISCARD = "Discard all changes and clean up";
|
|
13900
14019
|
const CANCEL = "Cancel";
|
|
13901
|
-
const action = await selectPrompt(`${
|
|
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]);
|
|
13902
14021
|
if (action === CANCEL) {
|
|
13903
14022
|
info("No changes made. You are still on your current branch.");
|
|
13904
14023
|
return;
|
|
13905
14024
|
}
|
|
13906
14025
|
if (action === SAVE_NEW_BRANCH) {
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
|
|
13912
|
-
|
|
13913
|
-
|
|
13914
|
-
|
|
13915
|
-
console.log(`
|
|
13916
|
-
${import_picocolors19.default.dim("AI suggestion:")} ${import_picocolors19.default.bold(import_picocolors19.default.cyan(suggested))}`);
|
|
13917
|
-
const accepted = await confirmPrompt(`Use ${import_picocolors19.default.bold(suggested)} as your branch name?`);
|
|
13918
|
-
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
13919
|
-
} else {
|
|
13920
|
-
spinner.fail("AI did not return a suggestion.");
|
|
13921
|
-
newBranchName = await inputPrompt("Enter branch name", description);
|
|
13922
|
-
}
|
|
13923
|
-
}
|
|
13924
|
-
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
13925
|
-
const prefix = await selectPrompt(`Choose a branch type for ${import_picocolors19.default.bold(newBranchName)}:`, config.branchPrefixes);
|
|
13926
|
-
newBranchName = formatBranchName(prefix, newBranchName);
|
|
13927
|
-
}
|
|
13928
|
-
if (!isValidBranchName(newBranchName)) {
|
|
13929
|
-
error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
|
|
13930
|
-
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;
|
|
13931
14034
|
}
|
|
13932
14035
|
const staleUpstream = await getUpstreamRef();
|
|
13933
14036
|
const staleUpstreamHash = staleUpstream ? await getCommitHash(staleUpstream) : null;
|
|
13934
|
-
if (await branchExists(newBranchName)) {
|
|
13935
|
-
error(`Branch ${import_picocolors19.default.bold(newBranchName)} already exists. Choose a different name.`);
|
|
13936
|
-
process.exit(1);
|
|
13937
|
-
}
|
|
13938
14037
|
const renameResult = await renameBranch(currentBranch, newBranchName);
|
|
13939
14038
|
if (renameResult.exitCode !== 0) {
|
|
13940
14039
|
error(`Failed to rename branch: ${renameResult.stderr}`);
|
|
13941
14040
|
process.exit(1);
|
|
13942
14041
|
}
|
|
13943
|
-
success(`Renamed ${
|
|
14042
|
+
success(`Renamed ${import_picocolors20.default.bold(currentBranch)} → ${import_picocolors20.default.bold(newBranchName)}`);
|
|
13944
14043
|
await unsetUpstream();
|
|
13945
14044
|
await fetchRemote(syncSource.remote);
|
|
13946
14045
|
let rebaseResult2;
|
|
@@ -13952,11 +14051,11 @@ var update_default = defineCommand({
|
|
|
13952
14051
|
}
|
|
13953
14052
|
if (rebaseResult2.exitCode !== 0) {
|
|
13954
14053
|
warn("Rebase encountered conflicts. Resolve them manually, then run:");
|
|
13955
|
-
info(` ${
|
|
14054
|
+
info(` ${import_picocolors20.default.bold("git rebase --continue")}`, "");
|
|
13956
14055
|
} else {
|
|
13957
|
-
success(`Rebased ${
|
|
14056
|
+
success(`Rebased ${import_picocolors20.default.bold(newBranchName)} onto ${import_picocolors20.default.bold(syncSource.ref)}.`);
|
|
13958
14057
|
}
|
|
13959
|
-
info(`All your changes are preserved. Run ${
|
|
14058
|
+
info(`All your changes are preserved. Run ${import_picocolors20.default.bold("contrib submit")} when ready to create a new PR.`, "");
|
|
13960
14059
|
return;
|
|
13961
14060
|
}
|
|
13962
14061
|
warn("Discarding local changes...");
|
|
@@ -13969,30 +14068,30 @@ var update_default = defineCommand({
|
|
|
13969
14068
|
process.exit(1);
|
|
13970
14069
|
}
|
|
13971
14070
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
13972
|
-
success(`Synced ${
|
|
13973
|
-
info(`Deleting stale branch ${
|
|
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)}...`);
|
|
13974
14073
|
await forceDeleteBranch(currentBranch);
|
|
13975
|
-
success(`Deleted ${
|
|
13976
|
-
info(`Run ${
|
|
14074
|
+
success(`Deleted ${import_picocolors20.default.bold(currentBranch)}.`);
|
|
14075
|
+
info(`Run ${import_picocolors20.default.bold("contrib start")} to begin a new feature branch.`, "");
|
|
13977
14076
|
return;
|
|
13978
14077
|
}
|
|
13979
|
-
info(`Updating ${
|
|
14078
|
+
info(`Updating ${import_picocolors20.default.bold(currentBranch)} with latest ${import_picocolors20.default.bold(baseBranch)}...`);
|
|
13980
14079
|
await fetchRemote(syncSource.remote);
|
|
13981
14080
|
if (!await refExists(syncSource.ref)) {
|
|
13982
|
-
error(`Remote ref ${
|
|
14081
|
+
error(`Remote ref ${import_picocolors20.default.bold(syncSource.ref)} does not exist.`);
|
|
13983
14082
|
error("Run `git fetch --all` and verify your remote configuration.");
|
|
13984
14083
|
process.exit(1);
|
|
13985
14084
|
}
|
|
13986
14085
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
13987
14086
|
const rebaseStrategy = await determineRebaseStrategy(currentBranch, syncSource.ref);
|
|
13988
14087
|
if (rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase) {
|
|
13989
|
-
info(
|
|
14088
|
+
info(import_picocolors20.default.dim(`Using --onto rebase (branch was based on a different ref)`));
|
|
13990
14089
|
}
|
|
13991
14090
|
const rebaseResult = rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase ? await rebaseOnto(syncSource.ref, rebaseStrategy.ontoOldBase) : await rebase(syncSource.ref);
|
|
13992
14091
|
if (rebaseResult.exitCode !== 0) {
|
|
13993
14092
|
warn("Rebase hit conflicts. Resolve them manually.");
|
|
13994
14093
|
console.log();
|
|
13995
|
-
if (
|
|
14094
|
+
if (isAIEnabled(config, args["no-ai"])) {
|
|
13996
14095
|
const copilotError = await checkCopilotAvailable();
|
|
13997
14096
|
if (!copilotError) {
|
|
13998
14097
|
info("Fetching AI conflict resolution suggestions...");
|
|
@@ -14015,10 +14114,10 @@ ${content.slice(0, 2000)}
|
|
|
14015
14114
|
if (suggestion) {
|
|
14016
14115
|
spinner.success("AI conflict guidance ready.");
|
|
14017
14116
|
console.log(`
|
|
14018
|
-
${
|
|
14019
|
-
console.log(
|
|
14117
|
+
${import_picocolors20.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
|
|
14118
|
+
console.log(import_picocolors20.default.dim("─".repeat(60)));
|
|
14020
14119
|
console.log(suggestion);
|
|
14021
|
-
console.log(
|
|
14120
|
+
console.log(import_picocolors20.default.dim("─".repeat(60)));
|
|
14022
14121
|
console.log();
|
|
14023
14122
|
} else {
|
|
14024
14123
|
spinner.fail("AI could not analyze the conflicts.");
|
|
@@ -14026,20 +14125,21 @@ ${import_picocolors19.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance
|
|
|
14026
14125
|
}
|
|
14027
14126
|
}
|
|
14028
14127
|
}
|
|
14029
|
-
console.log(
|
|
14128
|
+
console.log(import_picocolors20.default.bold("To resolve:"));
|
|
14030
14129
|
console.log(` 1. Fix conflicts in the affected files`);
|
|
14031
|
-
console.log(` 2. ${
|
|
14032
|
-
console.log(` 3. ${
|
|
14130
|
+
console.log(` 2. ${import_picocolors20.default.cyan("git add <resolved-files>")}`);
|
|
14131
|
+
console.log(` 3. ${import_picocolors20.default.cyan("git rebase --continue")}`);
|
|
14033
14132
|
console.log();
|
|
14034
|
-
console.log(` Or abort: ${
|
|
14133
|
+
console.log(` Or abort: ${import_picocolors20.default.cyan("git rebase --abort")}`);
|
|
14035
14134
|
process.exit(1);
|
|
14036
14135
|
}
|
|
14037
|
-
success(`${
|
|
14136
|
+
success(`${import_picocolors20.default.bold(currentBranch)} has been rebased onto latest ${import_picocolors20.default.bold(baseBranch)}`);
|
|
14038
14137
|
}
|
|
14039
14138
|
});
|
|
14040
14139
|
|
|
14041
14140
|
// src/commands/validate.ts
|
|
14042
|
-
var
|
|
14141
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
14142
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
14043
14143
|
var validate_default = defineCommand({
|
|
14044
14144
|
meta: {
|
|
14045
14145
|
name: "validate",
|
|
@@ -14049,7 +14149,11 @@ var validate_default = defineCommand({
|
|
|
14049
14149
|
message: {
|
|
14050
14150
|
type: "positional",
|
|
14051
14151
|
description: "The commit message to validate",
|
|
14052
|
-
required:
|
|
14152
|
+
required: false
|
|
14153
|
+
},
|
|
14154
|
+
file: {
|
|
14155
|
+
type: "string",
|
|
14156
|
+
description: "Path to a commit message file; only the first line is validated"
|
|
14053
14157
|
}
|
|
14054
14158
|
},
|
|
14055
14159
|
async run({ args }) {
|
|
@@ -14063,14 +14167,18 @@ var validate_default = defineCommand({
|
|
|
14063
14167
|
info('Commit convention is set to "none". All messages are accepted.');
|
|
14064
14168
|
process.exit(0);
|
|
14065
14169
|
}
|
|
14066
|
-
const message = args.message;
|
|
14170
|
+
const message = args.file ? readFileSync5(args.file, "utf-8").split(/\r?\n/, 1)[0] ?? "" : args.message;
|
|
14171
|
+
if (!message) {
|
|
14172
|
+
error("No commit message provided. Pass a message or use --file <path>.");
|
|
14173
|
+
process.exit(1);
|
|
14174
|
+
}
|
|
14067
14175
|
if (validateCommitMessage(message, convention)) {
|
|
14068
14176
|
success(`Valid ${CONVENTION_LABELS[convention]} message.`);
|
|
14069
14177
|
process.exit(0);
|
|
14070
14178
|
}
|
|
14071
14179
|
const errors = getValidationError(convention);
|
|
14072
14180
|
for (const line of errors) {
|
|
14073
|
-
console.error(
|
|
14181
|
+
console.error(import_picocolors21.default.red(` ✗ ${line}`));
|
|
14074
14182
|
}
|
|
14075
14183
|
process.exit(1);
|
|
14076
14184
|
}
|
|
@@ -15465,7 +15573,7 @@ nodeFiglet.fontsSync = function() {
|
|
|
15465
15573
|
};
|
|
15466
15574
|
|
|
15467
15575
|
// src/ui/banner.ts
|
|
15468
|
-
var
|
|
15576
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
15469
15577
|
var LOGO_BIG;
|
|
15470
15578
|
try {
|
|
15471
15579
|
LOGO_BIG = nodeFiglet.textSync(`Contribute
|
|
@@ -15487,19 +15595,33 @@ function getAuthor() {
|
|
|
15487
15595
|
}
|
|
15488
15596
|
function showBanner(variant = "small") {
|
|
15489
15597
|
const logo = variant === "big" ? LOGO_BIG : LOGO_SMALL;
|
|
15490
|
-
console.log(
|
|
15598
|
+
console.log(import_picocolors22.default.cyan(`
|
|
15491
15599
|
${logo}`));
|
|
15492
|
-
console.log(` ${
|
|
15600
|
+
console.log(` ${import_picocolors22.default.dim(`v${getVersion()}`)} ${import_picocolors22.default.dim("—")} ${import_picocolors22.default.dim(`Built by ${getAuthor()}`)}`);
|
|
15493
15601
|
if (variant === "big") {
|
|
15494
15602
|
console.log();
|
|
15495
|
-
console.log(` ${
|
|
15496
|
-
console.log(` ${
|
|
15497
|
-
console.log(` ${
|
|
15603
|
+
console.log(` ${import_picocolors22.default.yellow("Star")} ${import_picocolors22.default.cyan("https://github.com/warengonzaga/contribute-now")}`);
|
|
15604
|
+
console.log(` ${import_picocolors22.default.green("Contribute")} ${import_picocolors22.default.cyan("https://github.com/warengonzaga/contribute-now/blob/main/CONTRIBUTING.md")}`);
|
|
15605
|
+
console.log(` ${import_picocolors22.default.magenta("Sponsor")} ${import_picocolors22.default.cyan("https://warengonzaga.com/sponsor")}`);
|
|
15498
15606
|
}
|
|
15499
15607
|
console.log();
|
|
15500
15608
|
}
|
|
15501
15609
|
|
|
15502
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);
|
|
15503
15625
|
var isVersion = process.argv.includes("--version") || process.argv.includes("-v");
|
|
15504
15626
|
if (!isVersion) {
|
|
15505
15627
|
const subCommands = [
|