contribute-now 0.4.0-dev.c791252 → 0.4.0-dev.d48d9e6
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 +252 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { defineCommand } from "citty";
|
|
|
10
10
|
import pc2 from "picocolors";
|
|
11
11
|
|
|
12
12
|
// src/utils/config.ts
|
|
13
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
var CONFIG_FILENAME = ".contributerc.json";
|
|
16
16
|
function getConfigPath(cwd = process.cwd()) {
|
|
@@ -86,6 +86,23 @@ function isGitignored(cwd = process.cwd()) {
|
|
|
86
86
|
return false;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
function ensureGitignored(cwd = process.cwd()) {
|
|
90
|
+
if (isGitignored(cwd))
|
|
91
|
+
return false;
|
|
92
|
+
const gitignorePath = join(cwd, ".gitignore");
|
|
93
|
+
const line = `${CONFIG_FILENAME}
|
|
94
|
+
`;
|
|
95
|
+
if (!existsSync(gitignorePath)) {
|
|
96
|
+
writeFileSync(gitignorePath, line, "utf-8");
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
const content = readFileSync(gitignorePath, "utf-8");
|
|
100
|
+
const needsLeadingNewline = content.length > 0 && !content.endsWith(`
|
|
101
|
+
`);
|
|
102
|
+
appendFileSync(gitignorePath, `${needsLeadingNewline ? `
|
|
103
|
+
` : ""}${line}`, "utf-8");
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
89
106
|
function getDefaultConfig() {
|
|
90
107
|
return {
|
|
91
108
|
workflow: "clean-flow",
|
|
@@ -953,6 +970,68 @@ function withTimeout(promise, ms) {
|
|
|
953
970
|
}
|
|
954
971
|
var COPILOT_TIMEOUT_MS = 30000;
|
|
955
972
|
var COPILOT_LONG_TIMEOUT_MS = 90000;
|
|
973
|
+
var BATCH_CONFIG = {
|
|
974
|
+
LARGE_CHANGESET_THRESHOLD: 15,
|
|
975
|
+
COMPACT_PER_FILE_CHARS: 300,
|
|
976
|
+
MAX_COMPACT_PAYLOAD: 1e4,
|
|
977
|
+
FALLBACK_BATCH_SIZE: 15
|
|
978
|
+
};
|
|
979
|
+
function parseDiffByFile(rawDiff) {
|
|
980
|
+
const sections = new Map;
|
|
981
|
+
const headerPattern = /^diff --git a\/(.+?) b\//gm;
|
|
982
|
+
const positions = [];
|
|
983
|
+
for (let match = headerPattern.exec(rawDiff);match !== null; match = headerPattern.exec(rawDiff)) {
|
|
984
|
+
positions.push({ file: match[1], start: match.index });
|
|
985
|
+
}
|
|
986
|
+
for (let i = 0;i < positions.length; i++) {
|
|
987
|
+
const { file, start } = positions[i];
|
|
988
|
+
const end = i + 1 < positions.length ? positions[i + 1].start : rawDiff.length;
|
|
989
|
+
sections.set(file, rawDiff.slice(start, end));
|
|
990
|
+
}
|
|
991
|
+
return sections;
|
|
992
|
+
}
|
|
993
|
+
function extractDiffStats(diffSection) {
|
|
994
|
+
let added = 0;
|
|
995
|
+
let removed = 0;
|
|
996
|
+
for (const line of diffSection.split(`
|
|
997
|
+
`)) {
|
|
998
|
+
if (line.startsWith("+") && !line.startsWith("+++"))
|
|
999
|
+
added++;
|
|
1000
|
+
if (line.startsWith("-") && !line.startsWith("---"))
|
|
1001
|
+
removed++;
|
|
1002
|
+
}
|
|
1003
|
+
return { added, removed };
|
|
1004
|
+
}
|
|
1005
|
+
function createCompactDiff(files, rawDiff, maxTotalChars = BATCH_CONFIG.MAX_COMPACT_PAYLOAD) {
|
|
1006
|
+
if (files.length === 0)
|
|
1007
|
+
return "";
|
|
1008
|
+
const diffSections = parseDiffByFile(rawDiff);
|
|
1009
|
+
const perFileBudget = Math.min(BATCH_CONFIG.COMPACT_PER_FILE_CHARS, Math.floor(maxTotalChars / files.length));
|
|
1010
|
+
const parts = [];
|
|
1011
|
+
for (const file of files) {
|
|
1012
|
+
const section = diffSections.get(file);
|
|
1013
|
+
if (section) {
|
|
1014
|
+
const stats = extractDiffStats(section);
|
|
1015
|
+
const header = `[${file}] (+${stats.added}/-${stats.removed})`;
|
|
1016
|
+
if (section.length <= perFileBudget) {
|
|
1017
|
+
parts.push(`${header}
|
|
1018
|
+
${section}`);
|
|
1019
|
+
} else {
|
|
1020
|
+
const truncated = section.slice(0, perFileBudget - header.length - 20);
|
|
1021
|
+
parts.push(`${header}
|
|
1022
|
+
${truncated}
|
|
1023
|
+
...(truncated)`);
|
|
1024
|
+
}
|
|
1025
|
+
} else {
|
|
1026
|
+
parts.push(`[${file}] (new/binary file — no diff available)`);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
const result = parts.join(`
|
|
1030
|
+
|
|
1031
|
+
`);
|
|
1032
|
+
return result.length > maxTotalChars ? `${result.slice(0, maxTotalChars - 15)}
|
|
1033
|
+
...(truncated)` : result;
|
|
1034
|
+
}
|
|
956
1035
|
async function checkCopilotAvailable() {
|
|
957
1036
|
try {
|
|
958
1037
|
const client = await getManagedClient();
|
|
@@ -1053,16 +1132,18 @@ function extractJson(raw) {
|
|
|
1053
1132
|
}
|
|
1054
1133
|
async function generateCommitMessage(diff, stagedFiles, model, convention = "clean-commit") {
|
|
1055
1134
|
try {
|
|
1135
|
+
const isLarge = stagedFiles.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD;
|
|
1056
1136
|
const multiFileHint = stagedFiles.length > 1 ? `
|
|
1057
1137
|
|
|
1058
1138
|
IMPORTANT: Multiple files are staged. Generate ONE commit message that captures the high-level purpose of ALL changes together. Focus on the overall intent, not individual file changes. Be specific but concise — do not list every file.` : "";
|
|
1139
|
+
const diffContent = isLarge ? createCompactDiff(stagedFiles, diff) : diff.slice(0, 4000);
|
|
1059
1140
|
const userMessage = `Generate a commit message for these staged changes:
|
|
1060
1141
|
|
|
1061
|
-
Files: ${stagedFiles.join(", ")}
|
|
1142
|
+
Files (${stagedFiles.length}): ${stagedFiles.join(", ")}
|
|
1062
1143
|
|
|
1063
1144
|
Diff:
|
|
1064
|
-
${
|
|
1065
|
-
const result = await callCopilot(getCommitSystemPrompt(convention), userMessage, model);
|
|
1145
|
+
${diffContent}${multiFileHint}`;
|
|
1146
|
+
const result = await callCopilot(getCommitSystemPrompt(convention), userMessage, model, isLarge ? COPILOT_LONG_TIMEOUT_MS : COPILOT_TIMEOUT_MS);
|
|
1066
1147
|
return result?.trim() ?? null;
|
|
1067
1148
|
} catch {
|
|
1068
1149
|
return null;
|
|
@@ -1111,16 +1192,23 @@ ${conflictDiff.slice(0, 4000)}`;
|
|
|
1111
1192
|
}
|
|
1112
1193
|
}
|
|
1113
1194
|
async function generateCommitGroups(files, diffs, model, convention = "clean-commit") {
|
|
1195
|
+
const isLarge = files.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD;
|
|
1196
|
+
const diffContent = isLarge ? createCompactDiff(files, diffs) : diffs.slice(0, 6000);
|
|
1197
|
+
const largeHint = isLarge ? `
|
|
1198
|
+
|
|
1199
|
+
NOTE: This is a large changeset (${files.length} files). Compact diffs are provided for every file. Focus on creating well-organized logical groups.` : "";
|
|
1114
1200
|
const userMessage = `Group these changed files into logical atomic commits:
|
|
1115
1201
|
|
|
1116
1202
|
Files:
|
|
1117
1203
|
${files.join(`
|
|
1118
1204
|
`)}
|
|
1119
1205
|
|
|
1120
|
-
Diffs
|
|
1121
|
-
${
|
|
1206
|
+
Diffs:
|
|
1207
|
+
${diffContent}${largeHint}`;
|
|
1122
1208
|
const result = await callCopilot(getGroupingSystemPrompt(convention), userMessage, model, COPILOT_LONG_TIMEOUT_MS);
|
|
1123
1209
|
if (!result) {
|
|
1210
|
+
if (isLarge)
|
|
1211
|
+
return generateCommitGroupsInBatches(files, diffs, model, convention);
|
|
1124
1212
|
throw new Error("AI returned an empty response");
|
|
1125
1213
|
}
|
|
1126
1214
|
const cleaned = extractJson(result);
|
|
@@ -1128,10 +1216,14 @@ ${diffs.slice(0, 6000)}`;
|
|
|
1128
1216
|
try {
|
|
1129
1217
|
parsed = JSON.parse(cleaned);
|
|
1130
1218
|
} catch {
|
|
1219
|
+
if (isLarge)
|
|
1220
|
+
return generateCommitGroupsInBatches(files, diffs, model, convention);
|
|
1131
1221
|
throw new Error(`AI response is not valid JSON. Raw start: "${result.slice(0, 120)}..."`);
|
|
1132
1222
|
}
|
|
1133
1223
|
const groups = parsed;
|
|
1134
1224
|
if (!Array.isArray(groups) || groups.length === 0) {
|
|
1225
|
+
if (isLarge)
|
|
1226
|
+
return generateCommitGroupsInBatches(files, diffs, model, convention);
|
|
1135
1227
|
throw new Error("AI response was not a valid JSON array of commit groups");
|
|
1136
1228
|
}
|
|
1137
1229
|
for (const group of groups) {
|
|
@@ -1141,7 +1233,51 @@ ${diffs.slice(0, 6000)}`;
|
|
|
1141
1233
|
}
|
|
1142
1234
|
return groups;
|
|
1143
1235
|
}
|
|
1236
|
+
async function generateCommitGroupsInBatches(files, diffs, model, convention = "clean-commit") {
|
|
1237
|
+
const batchSize = BATCH_CONFIG.FALLBACK_BATCH_SIZE;
|
|
1238
|
+
const allGroups = [];
|
|
1239
|
+
const diffSections = parseDiffByFile(diffs);
|
|
1240
|
+
for (let i = 0;i < files.length; i += batchSize) {
|
|
1241
|
+
const batchFiles = files.slice(i, i + batchSize);
|
|
1242
|
+
const batchDiff = batchFiles.map((f) => diffSections.get(f) ?? "").filter(Boolean).join(`
|
|
1243
|
+
`);
|
|
1244
|
+
const batchDiffContent = batchFiles.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD ? createCompactDiff(batchFiles, batchDiff) : batchDiff.slice(0, 6000);
|
|
1245
|
+
const batchNum = Math.floor(i / batchSize) + 1;
|
|
1246
|
+
const totalBatches = Math.ceil(files.length / batchSize);
|
|
1247
|
+
const userMessage = `Group these changed files into logical atomic commits:
|
|
1248
|
+
|
|
1249
|
+
Files:
|
|
1250
|
+
${batchFiles.join(`
|
|
1251
|
+
`)}
|
|
1252
|
+
|
|
1253
|
+
Diffs:
|
|
1254
|
+
${batchDiffContent}
|
|
1255
|
+
|
|
1256
|
+
NOTE: Processing batch ${batchNum}/${totalBatches} of a large changeset. Group only the files listed above.`;
|
|
1257
|
+
try {
|
|
1258
|
+
const result = await callCopilot(getGroupingSystemPrompt(convention), userMessage, model, COPILOT_LONG_TIMEOUT_MS);
|
|
1259
|
+
if (!result)
|
|
1260
|
+
continue;
|
|
1261
|
+
const cleaned = extractJson(result);
|
|
1262
|
+
const parsed = JSON.parse(cleaned);
|
|
1263
|
+
if (Array.isArray(parsed)) {
|
|
1264
|
+
for (const group of parsed) {
|
|
1265
|
+
if (Array.isArray(group.files) && typeof group.message === "string") {
|
|
1266
|
+
allGroups.push(group);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
} catch {}
|
|
1271
|
+
}
|
|
1272
|
+
if (allGroups.length === 0) {
|
|
1273
|
+
throw new Error("AI could not group any files even with batch processing");
|
|
1274
|
+
}
|
|
1275
|
+
return allGroups;
|
|
1276
|
+
}
|
|
1144
1277
|
async function regenerateAllGroupMessages(groups, diffs, model, convention = "clean-commit") {
|
|
1278
|
+
const totalFiles = groups.reduce((sum, g) => sum + g.files.length, 0);
|
|
1279
|
+
const isLarge = totalFiles >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD;
|
|
1280
|
+
const diffContent = isLarge ? createCompactDiff(groups.flatMap((g) => g.files), diffs) : diffs.slice(0, 6000);
|
|
1145
1281
|
const groupSummary = groups.map((g, i) => `Group ${i + 1}: [${g.files.join(", ")}]`).join(`
|
|
1146
1282
|
`);
|
|
1147
1283
|
const userMessage = `Regenerate ONLY the commit messages for these pre-defined file groups. Do NOT change the file groupings.
|
|
@@ -1149,8 +1285,8 @@ async function regenerateAllGroupMessages(groups, diffs, model, convention = "cl
|
|
|
1149
1285
|
Groups:
|
|
1150
1286
|
${groupSummary}
|
|
1151
1287
|
|
|
1152
|
-
Diffs
|
|
1153
|
-
${
|
|
1288
|
+
Diffs:
|
|
1289
|
+
${diffContent}`;
|
|
1154
1290
|
const result = await callCopilot(getGroupingSystemPrompt(convention), userMessage, model, COPILOT_LONG_TIMEOUT_MS);
|
|
1155
1291
|
if (!result)
|
|
1156
1292
|
return groups;
|
|
@@ -1169,12 +1305,14 @@ ${diffs.slice(0, 6000)}`;
|
|
|
1169
1305
|
}
|
|
1170
1306
|
async function regenerateGroupMessage(files, diffs, model, convention = "clean-commit") {
|
|
1171
1307
|
try {
|
|
1308
|
+
const isLarge = files.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD;
|
|
1309
|
+
const diffContent = isLarge ? createCompactDiff(files, diffs) : diffs.slice(0, 4000);
|
|
1172
1310
|
const userMessage = `Generate a single commit message for these files:
|
|
1173
1311
|
|
|
1174
1312
|
Files: ${files.join(", ")}
|
|
1175
1313
|
|
|
1176
1314
|
Diff:
|
|
1177
|
-
${
|
|
1315
|
+
${diffContent}`;
|
|
1178
1316
|
const result = await callCopilot(getCommitSystemPrompt(convention), userMessage, model);
|
|
1179
1317
|
return result?.trim() ?? null;
|
|
1180
1318
|
} catch {
|
|
@@ -1732,7 +1870,8 @@ ${pc6.bold("Changed files:")}`);
|
|
|
1732
1870
|
warn(`AI unavailable: ${copilotError}`);
|
|
1733
1871
|
warn("Falling back to manual commit message entry.");
|
|
1734
1872
|
} else {
|
|
1735
|
-
const
|
|
1873
|
+
const spinnerMsg = stagedFiles.length >= 15 ? `Generating commit message with AI (${stagedFiles.length} files — using optimized batching)...` : "Generating commit message with AI...";
|
|
1874
|
+
const spinner = createSpinner(spinnerMsg);
|
|
1736
1875
|
commitMessage = await generateCommitMessage(diff, stagedFiles, args.model, config.commitConvention);
|
|
1737
1876
|
if (commitMessage) {
|
|
1738
1877
|
spinner.success("AI commit message generated.");
|
|
@@ -1823,7 +1962,7 @@ ${pc6.bold("Changed files:")}`);
|
|
|
1823
1962
|
for (const f of changedFiles) {
|
|
1824
1963
|
console.log(` ${pc6.dim("•")} ${f}`);
|
|
1825
1964
|
}
|
|
1826
|
-
const spinner = createSpinner(`Asking AI to group ${changedFiles.length} file(s) into logical commits...`);
|
|
1965
|
+
const spinner = createSpinner(changedFiles.length >= 15 ? `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...`);
|
|
1827
1966
|
const diffs = await getFullDiffForFiles(changedFiles);
|
|
1828
1967
|
if (!diffs.trim()) {
|
|
1829
1968
|
spinner.stop();
|
|
@@ -2001,7 +2140,7 @@ import pc7 from "picocolors";
|
|
|
2001
2140
|
// package.json
|
|
2002
2141
|
var package_default = {
|
|
2003
2142
|
name: "contribute-now",
|
|
2004
|
-
version: "0.4.0-dev.
|
|
2143
|
+
version: "0.4.0-dev.d48d9e6",
|
|
2005
2144
|
description: "Git workflow CLI for squash-merge two-branch models. Keeps dev in sync with main after squash merges.",
|
|
2006
2145
|
type: "module",
|
|
2007
2146
|
bin: {
|
|
@@ -2660,6 +2799,40 @@ function colorizeSubject(subject) {
|
|
|
2660
2799
|
// src/commands/setup.ts
|
|
2661
2800
|
import { defineCommand as defineCommand7 } from "citty";
|
|
2662
2801
|
import pc10 from "picocolors";
|
|
2802
|
+
async function shouldContinueSetupWithExistingConfig(options) {
|
|
2803
|
+
const {
|
|
2804
|
+
existingConfig,
|
|
2805
|
+
hasConfigFile,
|
|
2806
|
+
confirm: confirm2,
|
|
2807
|
+
ensureIgnored,
|
|
2808
|
+
onInfo,
|
|
2809
|
+
onWarn,
|
|
2810
|
+
onSuccess,
|
|
2811
|
+
summary
|
|
2812
|
+
} = options;
|
|
2813
|
+
if (existingConfig) {
|
|
2814
|
+
onInfo("Existing .contributerc.json detected:");
|
|
2815
|
+
summary(existingConfig);
|
|
2816
|
+
const shouldContinue = await confirm2("Continue setup and overwrite existing config?");
|
|
2817
|
+
if (!shouldContinue) {
|
|
2818
|
+
if (ensureIgnored()) {
|
|
2819
|
+
onInfo("Added .contributerc.json to .gitignore to avoid committing personal config.");
|
|
2820
|
+
}
|
|
2821
|
+
onSuccess("Keeping existing setup.");
|
|
2822
|
+
return false;
|
|
2823
|
+
}
|
|
2824
|
+
return true;
|
|
2825
|
+
}
|
|
2826
|
+
if (hasConfigFile) {
|
|
2827
|
+
onWarn("Found .contributerc.json but it appears invalid.");
|
|
2828
|
+
const shouldContinue = await confirm2("Continue setup and overwrite invalid config?");
|
|
2829
|
+
if (!shouldContinue) {
|
|
2830
|
+
onInfo("Keeping existing file. Run setup again when ready to repair it.");
|
|
2831
|
+
return false;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
return true;
|
|
2835
|
+
}
|
|
2663
2836
|
var setup_default = defineCommand7({
|
|
2664
2837
|
meta: {
|
|
2665
2838
|
name: "setup",
|
|
@@ -2671,6 +2844,20 @@ var setup_default = defineCommand7({
|
|
|
2671
2844
|
process.exit(1);
|
|
2672
2845
|
}
|
|
2673
2846
|
heading("\uD83D\uDD27 contribute-now setup");
|
|
2847
|
+
const existingConfig = readConfig();
|
|
2848
|
+
const shouldContinue = await shouldContinueSetupWithExistingConfig({
|
|
2849
|
+
existingConfig,
|
|
2850
|
+
hasConfigFile: configExists(),
|
|
2851
|
+
confirm: confirmPrompt,
|
|
2852
|
+
ensureIgnored: ensureGitignored,
|
|
2853
|
+
onInfo: info,
|
|
2854
|
+
onWarn: warn,
|
|
2855
|
+
onSuccess: success,
|
|
2856
|
+
summary: logConfigSummary
|
|
2857
|
+
});
|
|
2858
|
+
if (!shouldContinue) {
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2674
2861
|
const workflowChoice = await selectPrompt("Which git workflow does this project use?", [
|
|
2675
2862
|
"Clean Flow — main + dev, squash features into dev, merge dev into main (recommended)",
|
|
2676
2863
|
"GitHub Flow — main + feature branches, squash/merge into main",
|
|
@@ -2700,31 +2887,42 @@ var setup_default = defineCommand7({
|
|
|
2700
2887
|
info(`Found remotes: ${remotes.join(", ")}`);
|
|
2701
2888
|
let detectedRole = null;
|
|
2702
2889
|
let detectionSource = "";
|
|
2703
|
-
const
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2890
|
+
const roleSpinner = createSpinner("Detecting your role...");
|
|
2891
|
+
try {
|
|
2892
|
+
roleSpinner.update("Checking GitHub CLI and auth...");
|
|
2893
|
+
const ghInstalled = await checkGhInstalled();
|
|
2894
|
+
if (ghInstalled && await checkGhAuth()) {
|
|
2895
|
+
roleSpinner.update("Inspecting repository relationship (fork/permissions)...");
|
|
2896
|
+
const isFork = await isRepoFork();
|
|
2897
|
+
if (isFork === true) {
|
|
2898
|
+
detectedRole = "contributor";
|
|
2899
|
+
detectionSource = "gh CLI (fork detected)";
|
|
2900
|
+
} else if (isFork === false) {
|
|
2901
|
+
const repoInfo = await getCurrentRepoInfo();
|
|
2902
|
+
if (repoInfo) {
|
|
2903
|
+
const perms = await checkRepoPermissions(repoInfo.owner, repoInfo.repo);
|
|
2904
|
+
if (perms?.admin || perms?.push) {
|
|
2905
|
+
detectedRole = "maintainer";
|
|
2906
|
+
detectionSource = "gh CLI (admin/push permissions)";
|
|
2907
|
+
}
|
|
2716
2908
|
}
|
|
2717
2909
|
}
|
|
2718
2910
|
}
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2911
|
+
if (detectedRole === null) {
|
|
2912
|
+
roleSpinner.update("Analyzing git remotes...");
|
|
2913
|
+
if (remotes.includes("upstream")) {
|
|
2914
|
+
detectedRole = "contributor";
|
|
2915
|
+
detectionSource = "heuristic (upstream remote exists)";
|
|
2916
|
+
} else if (remotes.includes("origin") && remotes.length === 1) {
|
|
2917
|
+
detectedRole = "maintainer";
|
|
2918
|
+
detectionSource = "heuristic (only origin remote)";
|
|
2919
|
+
}
|
|
2727
2920
|
}
|
|
2921
|
+
roleSpinner.success("Role detection complete.");
|
|
2922
|
+
} catch {
|
|
2923
|
+
roleSpinner.fail("Role detection failed; falling back to manual selection.");
|
|
2924
|
+
detectedRole = null;
|
|
2925
|
+
detectionSource = "";
|
|
2728
2926
|
}
|
|
2729
2927
|
if (detectedRole === null) {
|
|
2730
2928
|
const roleChoice = await selectPrompt("What is your role in this project?", [
|
|
@@ -2742,16 +2940,20 @@ var setup_default = defineCommand7({
|
|
|
2742
2940
|
}
|
|
2743
2941
|
}
|
|
2744
2942
|
const defaultConfig = getDefaultConfig();
|
|
2745
|
-
|
|
2943
|
+
info(pc10.dim("Tip: press Enter to keep the default branch name shown in each prompt."));
|
|
2944
|
+
const mainBranchDefault = defaultConfig.mainBranch;
|
|
2945
|
+
const mainBranch = await inputPrompt(`Main branch name (default: ${mainBranchDefault} — press Enter to keep)`, mainBranchDefault);
|
|
2746
2946
|
let devBranch;
|
|
2747
2947
|
if (hasDevBranch(workflow)) {
|
|
2748
2948
|
const defaultDev = workflow === "git-flow" ? "develop" : "dev";
|
|
2749
|
-
devBranch = await inputPrompt(
|
|
2949
|
+
devBranch = await inputPrompt(`Dev/develop branch name (default: ${defaultDev} — press Enter to keep)`, defaultDev);
|
|
2750
2950
|
}
|
|
2751
|
-
const
|
|
2951
|
+
const originRemoteDefault = defaultConfig.origin;
|
|
2952
|
+
const originRemote = await inputPrompt(`Origin remote name (default: ${originRemoteDefault} — press Enter to keep)`, originRemoteDefault);
|
|
2752
2953
|
let upstreamRemote = defaultConfig.upstream;
|
|
2753
2954
|
if (detectedRole === "contributor") {
|
|
2754
|
-
|
|
2955
|
+
const upstreamRemoteDefault = defaultConfig.upstream;
|
|
2956
|
+
upstreamRemote = await inputPrompt(`Upstream remote name (default: ${upstreamRemoteDefault} — press Enter to keep)`, upstreamRemoteDefault);
|
|
2755
2957
|
if (!remotes.includes(upstreamRemote)) {
|
|
2756
2958
|
warn(`Remote "${upstreamRemote}" not found.`);
|
|
2757
2959
|
const originUrl = await getRemoteUrl(originRemote);
|
|
@@ -2799,9 +3001,8 @@ var setup_default = defineCommand7({
|
|
|
2799
3001
|
warn("Config was saved — verify the branch name and re-run setup if needed.");
|
|
2800
3002
|
}
|
|
2801
3003
|
}
|
|
2802
|
-
if (
|
|
2803
|
-
|
|
2804
|
-
warn(' echo ".contributerc.json" >> .gitignore');
|
|
3004
|
+
if (ensureGitignored()) {
|
|
3005
|
+
info("Added .contributerc.json to .gitignore to avoid committing personal config.");
|
|
2805
3006
|
}
|
|
2806
3007
|
console.log();
|
|
2807
3008
|
info(`Workflow: ${pc10.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
|
|
@@ -2815,6 +3016,17 @@ var setup_default = defineCommand7({
|
|
|
2815
3016
|
info(`Origin: ${pc10.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${pc10.bold(config.upstream)}` : ""}`);
|
|
2816
3017
|
}
|
|
2817
3018
|
});
|
|
3019
|
+
function logConfigSummary(config) {
|
|
3020
|
+
info(`Workflow: ${pc10.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
|
|
3021
|
+
info(`Convention: ${pc10.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
|
|
3022
|
+
info(`Role: ${pc10.bold(config.role)}`);
|
|
3023
|
+
if (config.devBranch) {
|
|
3024
|
+
info(`Main: ${pc10.bold(config.mainBranch)} | Dev: ${pc10.bold(config.devBranch)}`);
|
|
3025
|
+
} else {
|
|
3026
|
+
info(`Main: ${pc10.bold(config.mainBranch)}`);
|
|
3027
|
+
}
|
|
3028
|
+
info(`Origin: ${pc10.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${pc10.bold(config.upstream)}` : ""}`);
|
|
3029
|
+
}
|
|
2818
3030
|
|
|
2819
3031
|
// src/commands/start.ts
|
|
2820
3032
|
import { defineCommand as defineCommand8 } from "citty";
|
package/package.json
CHANGED