contribute-now 0.6.2-dev.313af88 → 0.6.2-dev.3d69403
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/dist/index.js +64 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10103,12 +10103,12 @@ async function multiSelectPrompt(message, choices) {
|
|
|
10103
10103
|
init_dist();
|
|
10104
10104
|
var CONVENTIONAL_COMMIT_SYSTEM_PROMPT = `Git commit message generator. Format: <type>[!][(<scope>)]: <description>
|
|
10105
10105
|
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.
|
|
10106
|
+
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
10107
|
Examples: feat: add user auth | fix(auth): resolve token expiry | feat!: redesign auth API`;
|
|
10108
10108
|
var CLEAN_COMMIT_SYSTEM_PROMPT = `Git commit message generator. EXACT format: <emoji> <type>[!][ (<scope>)]: <description>
|
|
10109
10109
|
Spacing: EMOJI SPACE TYPE [SPACE OPENPAREN SCOPE CLOSEPAREN] COLON SPACE DESCRIPTION
|
|
10110
10110
|
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.
|
|
10111
|
+
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
10112
|
Correct: \uD83D\uDCE6 new: add user auth | \uD83D\uDD27 update (api): improve error handling | ⚙️ setup (ci): configure github actions
|
|
10113
10113
|
WRONG: ⚙️setup(ci): ... | \uD83D\uDD27 update(api): ... ← always space before scope parenthesis`;
|
|
10114
10114
|
function getGroupingSystemPrompt(convention) {
|
|
@@ -10133,6 +10133,7 @@ Rules:
|
|
|
10133
10133
|
- Each group should represent ONE logical change
|
|
10134
10134
|
- Every file must appear in exactly one group
|
|
10135
10135
|
- Commit messages must follow the convention, be concise, imperative, max 72 chars
|
|
10136
|
+
- Do not use backticks, quotes, or markdown formatting in commit messages
|
|
10136
10137
|
- Order groups so foundational changes come first (types, utils) and consumers come after
|
|
10137
10138
|
- Return ONLY the JSON array, nothing else`;
|
|
10138
10139
|
}
|
|
@@ -10351,6 +10352,9 @@ function extractJson(raw) {
|
|
|
10351
10352
|
}
|
|
10352
10353
|
return text;
|
|
10353
10354
|
}
|
|
10355
|
+
function sanitizeGeneratedCommitMessage(message) {
|
|
10356
|
+
return message.replace(/`+/g, "").replace(/\s+/g, " ").trim();
|
|
10357
|
+
}
|
|
10354
10358
|
async function generateCommitMessage(diff, stagedFiles, model, convention = "clean-commit", context) {
|
|
10355
10359
|
try {
|
|
10356
10360
|
const isLarge = stagedFiles.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD;
|
|
@@ -10368,7 +10372,7 @@ Files (${stagedFiles.length}): ${stagedFiles.join(", ")}
|
|
|
10368
10372
|
Diff:
|
|
10369
10373
|
${diffContent}${multiFileHint}${squashHint}`;
|
|
10370
10374
|
const result = await callCopilot(getCommitSystemPrompt(convention), userMessage, model, isLarge ? COPILOT_LONG_TIMEOUT_MS : COPILOT_TIMEOUT_MS);
|
|
10371
|
-
return result
|
|
10375
|
+
return result ? sanitizeGeneratedCommitMessage(result) : null;
|
|
10372
10376
|
} catch {
|
|
10373
10377
|
return null;
|
|
10374
10378
|
}
|
|
@@ -10489,7 +10493,10 @@ ${diffContent}${largeHint}`;
|
|
|
10489
10493
|
throw new Error("AI returned groups with invalid structure (missing files or message)");
|
|
10490
10494
|
}
|
|
10491
10495
|
}
|
|
10492
|
-
return groups
|
|
10496
|
+
return groups.map((group) => ({
|
|
10497
|
+
...group,
|
|
10498
|
+
message: sanitizeGeneratedCommitMessage(group.message)
|
|
10499
|
+
}));
|
|
10493
10500
|
}
|
|
10494
10501
|
async function generateCommitGroupsInBatches(files, diffs, model, convention = "clean-commit") {
|
|
10495
10502
|
const batchSize = BATCH_CONFIG.FALLBACK_BATCH_SIZE;
|
|
@@ -10524,7 +10531,11 @@ NOTE: Processing batch ${batchNum}/${totalBatches} of a large changeset. Group o
|
|
|
10524
10531
|
const batchFileSet = new Set(batchFiles);
|
|
10525
10532
|
const filteredFiles = group.files.filter((f3) => batchFileSet.has(f3));
|
|
10526
10533
|
if (filteredFiles.length > 0) {
|
|
10527
|
-
allGroups.push({
|
|
10534
|
+
allGroups.push({
|
|
10535
|
+
...group,
|
|
10536
|
+
files: filteredFiles,
|
|
10537
|
+
message: sanitizeGeneratedCommitMessage(group.message)
|
|
10538
|
+
});
|
|
10528
10539
|
}
|
|
10529
10540
|
}
|
|
10530
10541
|
}
|
|
@@ -10567,7 +10578,7 @@ ${diffContent}`;
|
|
|
10567
10578
|
return groups;
|
|
10568
10579
|
return groups.map((g3, i2) => ({
|
|
10569
10580
|
files: g3.files,
|
|
10570
|
-
message: typeof parsed[i2]?.message === "string" ? parsed[i2].message : g3.message
|
|
10581
|
+
message: typeof parsed[i2]?.message === "string" ? sanitizeGeneratedCommitMessage(parsed[i2].message) : g3.message
|
|
10571
10582
|
}));
|
|
10572
10583
|
} catch {
|
|
10573
10584
|
return groups;
|
|
@@ -10584,7 +10595,7 @@ Files: ${files.join(", ")}
|
|
|
10584
10595
|
Diff:
|
|
10585
10596
|
${diffContent}`;
|
|
10586
10597
|
const result = await callCopilot(getCommitSystemPrompt(convention), userMessage, model);
|
|
10587
|
-
return result
|
|
10598
|
+
return result ? sanitizeGeneratedCommitMessage(result) : null;
|
|
10588
10599
|
} catch {
|
|
10589
10600
|
return null;
|
|
10590
10601
|
}
|
|
@@ -11020,17 +11031,24 @@ var CONVENTION_FORMAT_HINTS = {
|
|
|
11020
11031
|
conventional: [
|
|
11021
11032
|
"Format: <type>[!][(<scope>)]: <description>",
|
|
11022
11033
|
"Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert",
|
|
11023
|
-
"Examples: feat: add login page | fix(auth): resolve token expiry | docs: update README"
|
|
11034
|
+
"Examples: feat: add login page | fix(auth): resolve token expiry | docs: update README",
|
|
11035
|
+
"Do not use backticks or markdown formatting in the message."
|
|
11024
11036
|
],
|
|
11025
11037
|
"clean-commit": [
|
|
11026
11038
|
"Format: <emoji> <type>[!][(<scope>)]: <description>",
|
|
11027
11039
|
"Types: \uD83D\uDCE6 new | \uD83D\uDD27 update | \uD83D\uDDD1️ remove | \uD83D\uDD12 security | ⚙️ setup | ☕ chore | \uD83E\uDDEA test | \uD83D\uDCD6 docs | \uD83D\uDE80 release",
|
|
11028
|
-
"Examples: \uD83D\uDCE6 new: user auth | \uD83D\uDD27 update (api): improve errors | ⚙️ setup (ci): add workflow"
|
|
11040
|
+
"Examples: \uD83D\uDCE6 new: user auth | \uD83D\uDD27 update (api): improve errors | ⚙️ setup (ci): add workflow",
|
|
11041
|
+
"Do not use backticks or markdown formatting in the message."
|
|
11029
11042
|
]
|
|
11030
11043
|
};
|
|
11044
|
+
function hasUnsupportedCommitMessageChars(message) {
|
|
11045
|
+
return message.includes("`");
|
|
11046
|
+
}
|
|
11031
11047
|
function validateCommitMessage(message, convention) {
|
|
11032
11048
|
if (convention === "none")
|
|
11033
11049
|
return true;
|
|
11050
|
+
if (hasUnsupportedCommitMessageChars(message))
|
|
11051
|
+
return false;
|
|
11034
11052
|
if (convention === "clean-commit")
|
|
11035
11053
|
return CLEAN_COMMIT_PATTERN.test(message);
|
|
11036
11054
|
if (convention === "conventional")
|
|
@@ -11042,6 +11060,7 @@ function getValidationError(convention) {
|
|
|
11042
11060
|
return [];
|
|
11043
11061
|
return [
|
|
11044
11062
|
`Commit message does not follow ${CONVENTION_LABELS[convention]} format.`,
|
|
11063
|
+
"Do not use backticks or markdown formatting in commit messages.",
|
|
11045
11064
|
...CONVENTION_FORMAT_HINTS[convention]
|
|
11046
11065
|
];
|
|
11047
11066
|
}
|
|
@@ -11469,7 +11488,7 @@ var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
|
11469
11488
|
// package.json
|
|
11470
11489
|
var package_default = {
|
|
11471
11490
|
name: "contribute-now",
|
|
11472
|
-
version: "0.6.2-dev.
|
|
11491
|
+
version: "0.6.2-dev.3d69403",
|
|
11473
11492
|
description: "Developer CLI that automates git workflows — branching, syncing, committing, and PRs — with multi-workflow and commit convention support.",
|
|
11474
11493
|
type: "module",
|
|
11475
11494
|
bin: {
|
|
@@ -11880,13 +11899,13 @@ esac
|
|
|
11880
11899
|
|
|
11881
11900
|
# Detect available package runner
|
|
11882
11901
|
if command -v contrib >/dev/null 2>&1; then
|
|
11883
|
-
contrib validate "$
|
|
11902
|
+
contrib validate --file "$commit_msg_file"
|
|
11884
11903
|
elif command -v bunx >/dev/null 2>&1; then
|
|
11885
|
-
bunx contrib validate "$
|
|
11904
|
+
bunx contrib validate --file "$commit_msg_file"
|
|
11886
11905
|
elif command -v pnpx >/dev/null 2>&1; then
|
|
11887
|
-
pnpx contrib validate "$
|
|
11906
|
+
pnpx contrib validate --file "$commit_msg_file"
|
|
11888
11907
|
elif command -v npx >/dev/null 2>&1; then
|
|
11889
|
-
npx contrib validate "$
|
|
11908
|
+
npx contrib validate --file "$commit_msg_file"
|
|
11890
11909
|
else
|
|
11891
11910
|
echo "Warning: No package runner found. Skipping commit message validation."
|
|
11892
11911
|
exit 0
|
|
@@ -13039,16 +13058,27 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
13039
13058
|
if (!message) {
|
|
13040
13059
|
const copilotError = await checkCopilotAvailable();
|
|
13041
13060
|
if (!copilotError) {
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13061
|
+
while (!message) {
|
|
13062
|
+
const spinner = createSpinner("Generating AI commit message for squash merge...");
|
|
13063
|
+
const [stagedDiff, stagedFiles] = await Promise.all([getStagedDiff(), getStagedFiles()]);
|
|
13064
|
+
const aiMsg = await generateCommitMessage(stagedDiff, stagedFiles, options?.model, options?.convention ?? "clean-commit", "squash-merge");
|
|
13065
|
+
if (aiMsg) {
|
|
13066
|
+
message = aiMsg;
|
|
13067
|
+
spinner.success("AI commit message generated.");
|
|
13068
|
+
console.log(`
|
|
13049
13069
|
${import_picocolors16.default.dim("AI suggestion:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(message))}`);
|
|
13050
|
-
|
|
13070
|
+
break;
|
|
13071
|
+
}
|
|
13051
13072
|
spinner.fail("AI did not return a commit message.");
|
|
13073
|
+
const retryAction = await selectPrompt("AI could not generate a commit message. What would you like to do?", ["Try again with AI", "Write manually", "Cancel"]);
|
|
13074
|
+
if (retryAction === "Try again with AI") {
|
|
13075
|
+
continue;
|
|
13076
|
+
}
|
|
13077
|
+
if (retryAction === "Cancel") {
|
|
13078
|
+
warn("Squash merge commit cancelled.");
|
|
13079
|
+
process.exit(0);
|
|
13080
|
+
}
|
|
13081
|
+
break;
|
|
13052
13082
|
}
|
|
13053
13083
|
} else {
|
|
13054
13084
|
warn(`AI unavailable: ${copilotError}`);
|
|
@@ -13078,7 +13108,7 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
13078
13108
|
${import_picocolors16.default.dim("AI suggestion:")} ${import_picocolors16.default.bold(import_picocolors16.default.cyan(regen))}`);
|
|
13079
13109
|
} else {
|
|
13080
13110
|
spinner.fail("Regeneration failed.");
|
|
13081
|
-
|
|
13111
|
+
continue;
|
|
13082
13112
|
}
|
|
13083
13113
|
} else {
|
|
13084
13114
|
finalMsg = await inputPrompt("Enter commit message");
|
|
@@ -14114,6 +14144,7 @@ ${import_picocolors19.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance
|
|
|
14114
14144
|
|
|
14115
14145
|
// src/commands/validate.ts
|
|
14116
14146
|
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
14147
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
14117
14148
|
var validate_default = defineCommand({
|
|
14118
14149
|
meta: {
|
|
14119
14150
|
name: "validate",
|
|
@@ -14123,7 +14154,11 @@ var validate_default = defineCommand({
|
|
|
14123
14154
|
message: {
|
|
14124
14155
|
type: "positional",
|
|
14125
14156
|
description: "The commit message to validate",
|
|
14126
|
-
required:
|
|
14157
|
+
required: false
|
|
14158
|
+
},
|
|
14159
|
+
file: {
|
|
14160
|
+
type: "string",
|
|
14161
|
+
description: "Path to a commit message file; only the first line is validated"
|
|
14127
14162
|
}
|
|
14128
14163
|
},
|
|
14129
14164
|
async run({ args }) {
|
|
@@ -14137,7 +14172,11 @@ var validate_default = defineCommand({
|
|
|
14137
14172
|
info('Commit convention is set to "none". All messages are accepted.');
|
|
14138
14173
|
process.exit(0);
|
|
14139
14174
|
}
|
|
14140
|
-
const message = args.message;
|
|
14175
|
+
const message = args.file ? readFileSync5(args.file, "utf-8").split(/\r?\n/, 1)[0] ?? "" : args.message;
|
|
14176
|
+
if (!message) {
|
|
14177
|
+
error("No commit message provided. Pass a message or use --file <path>.");
|
|
14178
|
+
process.exit(1);
|
|
14179
|
+
}
|
|
14141
14180
|
if (validateCommitMessage(message, convention)) {
|
|
14142
14181
|
success(`Valid ${CONVENTION_LABELS[convention]} message.`);
|
|
14143
14182
|
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "contribute-now",
|
|
3
|
-
"version": "0.6.2-dev.
|
|
3
|
+
"version": "0.6.2-dev.3d69403",
|
|
4
4
|
"description": "Developer CLI that automates git workflows — branching, syncing, committing, and PRs — with multi-workflow and commit convention support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|