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.
Files changed (2) hide show
  1. package/dist/index.js +64 -25
  2. 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?.trim() ?? null;
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({ ...group, files: filteredFiles });
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?.trim() ?? null;
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.313af88",
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 "$commit_msg"
11902
+ contrib validate --file "$commit_msg_file"
11884
11903
  elif command -v bunx >/dev/null 2>&1; then
11885
- bunx contrib validate "$commit_msg"
11904
+ bunx contrib validate --file "$commit_msg_file"
11886
11905
  elif command -v pnpx >/dev/null 2>&1; then
11887
- pnpx contrib validate "$commit_msg"
11906
+ pnpx contrib validate --file "$commit_msg_file"
11888
11907
  elif command -v npx >/dev/null 2>&1; then
11889
- npx contrib validate "$commit_msg"
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
- const spinner = createSpinner("Generating AI commit message for squash merge...");
13043
- const [stagedDiff, stagedFiles] = await Promise.all([getStagedDiff(), getStagedFiles()]);
13044
- const aiMsg = await generateCommitMessage(stagedDiff, stagedFiles, options?.model, options?.convention ?? "clean-commit", "squash-merge");
13045
- if (aiMsg) {
13046
- message = aiMsg;
13047
- spinner.success("AI commit message generated.");
13048
- console.log(`
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
- } else {
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
- finalMsg = await inputPrompt("Enter commit message");
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: true
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.313af88",
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": {