contribute-now 0.5.0-dev.914e35d → 0.5.0-dev.9ad3d01
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 +191 -446
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
|
-
import { defineCommand as
|
|
6
|
+
import { defineCommand as defineCommand14, runMain } from "citty";
|
|
7
7
|
|
|
8
8
|
// src/commands/branch.ts
|
|
9
9
|
import { defineCommand } from "citty";
|
|
@@ -2268,7 +2268,7 @@ import pc7 from "picocolors";
|
|
|
2268
2268
|
// package.json
|
|
2269
2269
|
var package_default = {
|
|
2270
2270
|
name: "contribute-now",
|
|
2271
|
-
version: "0.5.0-dev.
|
|
2271
|
+
version: "0.5.0-dev.9ad3d01",
|
|
2272
2272
|
description: "Developer CLI that automates git workflows — branching, syncing, committing, and PRs — with multi-workflow and commit convention support.",
|
|
2273
2273
|
type: "module",
|
|
2274
2274
|
bin: {
|
|
@@ -3090,161 +3090,9 @@ function colorizeSubject(subject) {
|
|
|
3090
3090
|
return pc9.white(subject);
|
|
3091
3091
|
}
|
|
3092
3092
|
|
|
3093
|
-
// src/commands/
|
|
3093
|
+
// src/commands/setup.ts
|
|
3094
3094
|
import { defineCommand as defineCommand7 } from "citty";
|
|
3095
3095
|
import pc10 from "picocolors";
|
|
3096
|
-
import { execFile as execFileCb4 } from "node:child_process";
|
|
3097
|
-
function gitRun(args) {
|
|
3098
|
-
return new Promise((resolve) => {
|
|
3099
|
-
execFileCb4("git", args, (err, stdout, stderr) => {
|
|
3100
|
-
resolve({
|
|
3101
|
-
exitCode: err ? err.code === "ENOENT" ? 127 : err.status ?? 1 : 0,
|
|
3102
|
-
stdout: stdout ?? "",
|
|
3103
|
-
stderr: stderr ?? ""
|
|
3104
|
-
});
|
|
3105
|
-
});
|
|
3106
|
-
});
|
|
3107
|
-
}
|
|
3108
|
-
var save_default = defineCommand7({
|
|
3109
|
-
meta: {
|
|
3110
|
-
name: "save",
|
|
3111
|
-
description: "Save, restore, or manage uncommitted changes"
|
|
3112
|
-
},
|
|
3113
|
-
args: {
|
|
3114
|
-
action: {
|
|
3115
|
-
type: "positional",
|
|
3116
|
-
description: "Action: save (default), restore, list, drop",
|
|
3117
|
-
required: false
|
|
3118
|
-
},
|
|
3119
|
-
message: {
|
|
3120
|
-
type: "string",
|
|
3121
|
-
alias: "m",
|
|
3122
|
-
description: "Description for saved changes"
|
|
3123
|
-
}
|
|
3124
|
-
},
|
|
3125
|
-
async run({ args }) {
|
|
3126
|
-
if (!await isGitRepo()) {
|
|
3127
|
-
error("Not inside a git repository.");
|
|
3128
|
-
process.exit(1);
|
|
3129
|
-
}
|
|
3130
|
-
const action = args.action ?? "save";
|
|
3131
|
-
switch (action) {
|
|
3132
|
-
case "save":
|
|
3133
|
-
await handleSave(args.message);
|
|
3134
|
-
break;
|
|
3135
|
-
case "restore":
|
|
3136
|
-
await handleRestore();
|
|
3137
|
-
break;
|
|
3138
|
-
case "list":
|
|
3139
|
-
await handleList();
|
|
3140
|
-
break;
|
|
3141
|
-
case "drop":
|
|
3142
|
-
await handleDrop();
|
|
3143
|
-
break;
|
|
3144
|
-
default:
|
|
3145
|
-
error(`Unknown action: ${action}. Use save, restore, list, or drop.`);
|
|
3146
|
-
process.exit(1);
|
|
3147
|
-
}
|
|
3148
|
-
}
|
|
3149
|
-
});
|
|
3150
|
-
async function handleSave(message) {
|
|
3151
|
-
heading("\uD83D\uDCBE contrib save");
|
|
3152
|
-
const currentBranch = await getCurrentBranch();
|
|
3153
|
-
const label = message ?? `work-in-progress on ${currentBranch ?? "unknown"}`;
|
|
3154
|
-
const stashMsg = `contrib-save: ${label}`;
|
|
3155
|
-
const result = await gitRun(["stash", "push", "-m", stashMsg]);
|
|
3156
|
-
if (result.exitCode !== 0) {
|
|
3157
|
-
error(`Failed to save: ${result.stderr}`);
|
|
3158
|
-
process.exit(1);
|
|
3159
|
-
}
|
|
3160
|
-
if (result.stdout.includes("No local changes to save")) {
|
|
3161
|
-
info("No uncommitted changes to save.");
|
|
3162
|
-
return;
|
|
3163
|
-
}
|
|
3164
|
-
success(`Saved: ${pc10.dim(label)}`);
|
|
3165
|
-
info(`Use ${pc10.bold("contrib save restore")} to bring them back.`);
|
|
3166
|
-
}
|
|
3167
|
-
async function handleRestore() {
|
|
3168
|
-
heading("\uD83D\uDCBE contrib save restore");
|
|
3169
|
-
const stashes = await getStashList();
|
|
3170
|
-
if (stashes.length === 0) {
|
|
3171
|
-
info("No saved changes found.");
|
|
3172
|
-
return;
|
|
3173
|
-
}
|
|
3174
|
-
if (stashes.length === 1) {
|
|
3175
|
-
const result2 = await gitRun(["stash", "pop", "stash@{0}"]);
|
|
3176
|
-
if (result2.exitCode !== 0) {
|
|
3177
|
-
error(`Failed to restore: ${result2.stderr}`);
|
|
3178
|
-
warn("You may have conflicts. Resolve them and run `git stash drop` when done.");
|
|
3179
|
-
process.exit(1);
|
|
3180
|
-
}
|
|
3181
|
-
success(`Restored: ${pc10.dim(stashes[0].message)}`);
|
|
3182
|
-
return;
|
|
3183
|
-
}
|
|
3184
|
-
const choices = stashes.map((s) => `${s.index} ${s.message}`);
|
|
3185
|
-
const selected = await selectPrompt("Which save to restore?", choices);
|
|
3186
|
-
const idx = selected.split(/\s{2,}/)[0].trim();
|
|
3187
|
-
const result = await gitRun(["stash", "pop", `stash@{${idx}}`]);
|
|
3188
|
-
if (result.exitCode !== 0) {
|
|
3189
|
-
error(`Failed to restore: ${result.stderr}`);
|
|
3190
|
-
warn("You may have conflicts. Resolve them and run `git stash drop` when done.");
|
|
3191
|
-
process.exit(1);
|
|
3192
|
-
}
|
|
3193
|
-
const match = stashes.find((s) => String(s.index) === idx);
|
|
3194
|
-
success(`Restored: ${pc10.dim(match?.message ?? "saved changes")}`);
|
|
3195
|
-
}
|
|
3196
|
-
async function handleList() {
|
|
3197
|
-
heading("\uD83D\uDCBE contrib save list");
|
|
3198
|
-
const stashes = await getStashList();
|
|
3199
|
-
if (stashes.length === 0) {
|
|
3200
|
-
info("No saved changes.");
|
|
3201
|
-
return;
|
|
3202
|
-
}
|
|
3203
|
-
console.log();
|
|
3204
|
-
for (const s of stashes) {
|
|
3205
|
-
const idx = pc10.dim(`[${s.index}]`);
|
|
3206
|
-
const msg = s.message;
|
|
3207
|
-
console.log(` ${idx} ${msg}`);
|
|
3208
|
-
}
|
|
3209
|
-
console.log();
|
|
3210
|
-
info(`Use ${pc10.bold("contrib save restore")} to bring changes back.`);
|
|
3211
|
-
info(`Use ${pc10.bold("contrib save drop")} to discard saved changes.`);
|
|
3212
|
-
}
|
|
3213
|
-
async function handleDrop() {
|
|
3214
|
-
heading("\uD83D\uDCBE contrib save drop");
|
|
3215
|
-
const stashes = await getStashList();
|
|
3216
|
-
if (stashes.length === 0) {
|
|
3217
|
-
info("No saved changes to drop.");
|
|
3218
|
-
return;
|
|
3219
|
-
}
|
|
3220
|
-
const choices = stashes.map((s) => `${s.index} ${s.message}`);
|
|
3221
|
-
const selected = await selectPrompt("Which save to drop?", choices);
|
|
3222
|
-
const idx = selected.split(/\s{2,}/)[0].trim();
|
|
3223
|
-
const result = await gitRun(["stash", "drop", `stash@{${idx}}`]);
|
|
3224
|
-
if (result.exitCode !== 0) {
|
|
3225
|
-
error(`Failed to drop: ${result.stderr}`);
|
|
3226
|
-
process.exit(1);
|
|
3227
|
-
}
|
|
3228
|
-
const match = stashes.find((s) => String(s.index) === idx);
|
|
3229
|
-
success(`Dropped: ${pc10.dim(match?.message ?? "saved changes")}`);
|
|
3230
|
-
}
|
|
3231
|
-
async function getStashList() {
|
|
3232
|
-
const result = await gitRun(["stash", "list"]);
|
|
3233
|
-
if (result.exitCode !== 0 || !result.stdout.trim())
|
|
3234
|
-
return [];
|
|
3235
|
-
return result.stdout.trimEnd().split(`
|
|
3236
|
-
`).filter(Boolean).map((line) => {
|
|
3237
|
-
const idxMatch = line.match(/^stash@\{(\d+)\}/);
|
|
3238
|
-
const index = idxMatch ? Number.parseInt(idxMatch[1], 10) : 0;
|
|
3239
|
-
const parts = line.split(": ");
|
|
3240
|
-
const message = parts.length > 2 ? parts.slice(2).join(": ") : parts[parts.length - 1];
|
|
3241
|
-
return { index, message };
|
|
3242
|
-
});
|
|
3243
|
-
}
|
|
3244
|
-
|
|
3245
|
-
// src/commands/setup.ts
|
|
3246
|
-
import { defineCommand as defineCommand8 } from "citty";
|
|
3247
|
-
import pc11 from "picocolors";
|
|
3248
3096
|
async function shouldContinueSetupWithExistingConfig(options) {
|
|
3249
3097
|
const {
|
|
3250
3098
|
existingConfig,
|
|
@@ -3282,7 +3130,7 @@ async function shouldContinueSetupWithExistingConfig(options) {
|
|
|
3282
3130
|
}
|
|
3283
3131
|
return true;
|
|
3284
3132
|
}
|
|
3285
|
-
var setup_default =
|
|
3133
|
+
var setup_default = defineCommand7({
|
|
3286
3134
|
meta: {
|
|
3287
3135
|
name: "setup",
|
|
3288
3136
|
description: "Initialize contribute-now config for this repo (.contributerc.json)"
|
|
@@ -3317,7 +3165,7 @@ var setup_default = defineCommand8({
|
|
|
3317
3165
|
workflow = "github-flow";
|
|
3318
3166
|
else if (workflowChoice.startsWith("Git Flow"))
|
|
3319
3167
|
workflow = "git-flow";
|
|
3320
|
-
info(`Workflow: ${
|
|
3168
|
+
info(`Workflow: ${pc10.bold(WORKFLOW_DESCRIPTIONS[workflow])}`);
|
|
3321
3169
|
const conventionChoice = await selectPrompt("Which commit convention should this project use?", [
|
|
3322
3170
|
`${CONVENTION_DESCRIPTIONS["clean-commit"]} (recommended)`,
|
|
3323
3171
|
CONVENTION_DESCRIPTIONS.conventional,
|
|
@@ -3381,15 +3229,15 @@ var setup_default = defineCommand8({
|
|
|
3381
3229
|
detectedRole = roleChoice;
|
|
3382
3230
|
detectionSource = "user selection";
|
|
3383
3231
|
} else {
|
|
3384
|
-
info(`Detected role: ${
|
|
3385
|
-
const confirmed = await confirmPrompt(`Role detected as ${
|
|
3232
|
+
info(`Detected role: ${pc10.bold(detectedRole)} (via ${detectionSource})`);
|
|
3233
|
+
const confirmed = await confirmPrompt(`Role detected as ${pc10.bold(detectedRole)}. Is this correct?`);
|
|
3386
3234
|
if (!confirmed) {
|
|
3387
3235
|
const roleChoice = await selectPrompt("Select your role:", ["maintainer", "contributor"]);
|
|
3388
3236
|
detectedRole = roleChoice;
|
|
3389
3237
|
}
|
|
3390
3238
|
}
|
|
3391
3239
|
const defaultConfig = getDefaultConfig();
|
|
3392
|
-
info(
|
|
3240
|
+
info(pc10.dim("Tip: press Enter to keep the default branch name shown in each prompt."));
|
|
3393
3241
|
const mainBranchDefault = defaultConfig.mainBranch;
|
|
3394
3242
|
const mainBranch = await inputPrompt(`Main branch name (default: ${mainBranchDefault} — press Enter to keep)`, mainBranchDefault);
|
|
3395
3243
|
let devBranch;
|
|
@@ -3415,7 +3263,7 @@ var setup_default = defineCommand8({
|
|
|
3415
3263
|
error("Setup cannot continue without the upstream remote for contributors.");
|
|
3416
3264
|
process.exit(1);
|
|
3417
3265
|
}
|
|
3418
|
-
success(`Added remote ${
|
|
3266
|
+
success(`Added remote ${pc10.bold(upstreamRemote)} → ${upstreamUrl}`);
|
|
3419
3267
|
} else {
|
|
3420
3268
|
error("An upstream remote URL is required for contributors.");
|
|
3421
3269
|
info("Add it manually: git remote add upstream <url>");
|
|
@@ -3436,17 +3284,17 @@ var setup_default = defineCommand8({
|
|
|
3436
3284
|
writeConfig(config);
|
|
3437
3285
|
success(`✅ Config written to .contributerc.json`);
|
|
3438
3286
|
const syncRemote = config.role === "contributor" ? config.upstream : config.origin;
|
|
3439
|
-
info(`Fetching ${
|
|
3287
|
+
info(`Fetching ${pc10.bold(syncRemote)} to verify branch configuration...`);
|
|
3440
3288
|
await fetchRemote(syncRemote);
|
|
3441
3289
|
const mainRef = `${syncRemote}/${config.mainBranch}`;
|
|
3442
3290
|
if (!await refExists(mainRef)) {
|
|
3443
|
-
warn(`Main branch ref ${
|
|
3291
|
+
warn(`Main branch ref ${pc10.bold(mainRef)} not found on remote.`);
|
|
3444
3292
|
warn("Config was saved — verify the branch name and re-run setup if needed.");
|
|
3445
3293
|
}
|
|
3446
3294
|
if (config.devBranch) {
|
|
3447
3295
|
const devRef = `${syncRemote}/${config.devBranch}`;
|
|
3448
3296
|
if (!await refExists(devRef)) {
|
|
3449
|
-
warn(`Dev branch ref ${
|
|
3297
|
+
warn(`Dev branch ref ${pc10.bold(devRef)} not found on remote.`);
|
|
3450
3298
|
warn("Config was saved — verify the branch name and re-run setup if needed.");
|
|
3451
3299
|
}
|
|
3452
3300
|
}
|
|
@@ -3454,33 +3302,33 @@ var setup_default = defineCommand8({
|
|
|
3454
3302
|
info("Added .contributerc.json to .gitignore to avoid committing personal config.");
|
|
3455
3303
|
}
|
|
3456
3304
|
console.log();
|
|
3457
|
-
info(`Workflow: ${
|
|
3458
|
-
info(`Convention: ${
|
|
3459
|
-
info(`Role: ${
|
|
3305
|
+
info(`Workflow: ${pc10.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
|
|
3306
|
+
info(`Convention: ${pc10.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
|
|
3307
|
+
info(`Role: ${pc10.bold(config.role)}`);
|
|
3460
3308
|
if (config.devBranch) {
|
|
3461
|
-
info(`Main: ${
|
|
3309
|
+
info(`Main: ${pc10.bold(config.mainBranch)} | Dev: ${pc10.bold(config.devBranch)}`);
|
|
3462
3310
|
} else {
|
|
3463
|
-
info(`Main: ${
|
|
3311
|
+
info(`Main: ${pc10.bold(config.mainBranch)}`);
|
|
3464
3312
|
}
|
|
3465
|
-
info(`Origin: ${
|
|
3313
|
+
info(`Origin: ${pc10.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${pc10.bold(config.upstream)}` : ""}`);
|
|
3466
3314
|
}
|
|
3467
3315
|
});
|
|
3468
3316
|
function logConfigSummary(config) {
|
|
3469
|
-
info(`Workflow: ${
|
|
3470
|
-
info(`Convention: ${
|
|
3471
|
-
info(`Role: ${
|
|
3317
|
+
info(`Workflow: ${pc10.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
|
|
3318
|
+
info(`Convention: ${pc10.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
|
|
3319
|
+
info(`Role: ${pc10.bold(config.role)}`);
|
|
3472
3320
|
if (config.devBranch) {
|
|
3473
|
-
info(`Main: ${
|
|
3321
|
+
info(`Main: ${pc10.bold(config.mainBranch)} | Dev: ${pc10.bold(config.devBranch)}`);
|
|
3474
3322
|
} else {
|
|
3475
|
-
info(`Main: ${
|
|
3323
|
+
info(`Main: ${pc10.bold(config.mainBranch)}`);
|
|
3476
3324
|
}
|
|
3477
|
-
info(`Origin: ${
|
|
3325
|
+
info(`Origin: ${pc10.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${pc10.bold(config.upstream)}` : ""}`);
|
|
3478
3326
|
}
|
|
3479
3327
|
|
|
3480
3328
|
// src/commands/start.ts
|
|
3481
|
-
import { defineCommand as
|
|
3482
|
-
import
|
|
3483
|
-
var start_default =
|
|
3329
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
3330
|
+
import pc11 from "picocolors";
|
|
3331
|
+
var start_default = defineCommand8({
|
|
3484
3332
|
meta: {
|
|
3485
3333
|
name: "start",
|
|
3486
3334
|
description: "Create a new feature branch from the latest base branch"
|
|
@@ -3536,8 +3384,8 @@ var start_default = defineCommand9({
|
|
|
3536
3384
|
if (suggested) {
|
|
3537
3385
|
spinner.success("Branch name suggestion ready.");
|
|
3538
3386
|
console.log(`
|
|
3539
|
-
${
|
|
3540
|
-
const accepted = await confirmPrompt(`Use ${
|
|
3387
|
+
${pc11.dim("AI suggestion:")} ${pc11.bold(pc11.cyan(suggested))}`);
|
|
3388
|
+
const accepted = await confirmPrompt(`Use ${pc11.bold(suggested)} as your branch name?`);
|
|
3541
3389
|
if (accepted) {
|
|
3542
3390
|
branchName = suggested;
|
|
3543
3391
|
} else {
|
|
@@ -3548,28 +3396,28 @@ var start_default = defineCommand9({
|
|
|
3548
3396
|
}
|
|
3549
3397
|
}
|
|
3550
3398
|
if (!hasPrefix(branchName, branchPrefixes)) {
|
|
3551
|
-
const prefix = await selectPrompt(`Choose a branch type for ${
|
|
3399
|
+
const prefix = await selectPrompt(`Choose a branch type for ${pc11.bold(branchName)}:`, branchPrefixes);
|
|
3552
3400
|
branchName = formatBranchName(prefix, branchName);
|
|
3553
3401
|
}
|
|
3554
3402
|
if (!isValidBranchName(branchName)) {
|
|
3555
3403
|
error("Invalid branch name. Use only alphanumeric characters, dots, hyphens, underscores, and slashes.");
|
|
3556
3404
|
process.exit(1);
|
|
3557
3405
|
}
|
|
3558
|
-
info(`Creating branch: ${
|
|
3406
|
+
info(`Creating branch: ${pc11.bold(branchName)}`);
|
|
3559
3407
|
if (await branchExists(branchName)) {
|
|
3560
|
-
error(`Branch ${
|
|
3561
|
-
info(` Use ${
|
|
3408
|
+
error(`Branch ${pc11.bold(branchName)} already exists.`);
|
|
3409
|
+
info(` Use ${pc11.bold(`git checkout ${branchName}`)} to switch to it, or choose a different name.`);
|
|
3562
3410
|
process.exit(1);
|
|
3563
3411
|
}
|
|
3564
3412
|
await fetchRemote(syncSource.remote);
|
|
3565
3413
|
if (!await refExists(syncSource.ref)) {
|
|
3566
|
-
warn(`Remote ref ${
|
|
3414
|
+
warn(`Remote ref ${pc11.bold(syncSource.ref)} not found. Creating branch from local ${pc11.bold(baseBranch)}.`);
|
|
3567
3415
|
}
|
|
3568
3416
|
const currentBranch = await getCurrentBranch();
|
|
3569
3417
|
if (currentBranch === baseBranch && await refExists(syncSource.ref)) {
|
|
3570
3418
|
const ahead = await countCommitsAhead(baseBranch, syncSource.ref);
|
|
3571
3419
|
if (ahead > 0) {
|
|
3572
|
-
warn(`You are on ${
|
|
3420
|
+
warn(`You are on ${pc11.bold(baseBranch)} with ${pc11.bold(String(ahead))} local commit${ahead > 1 ? "s" : ""} not in ${pc11.bold(syncSource.ref)}.`);
|
|
3573
3421
|
info(" Syncing will discard those commits. Consider backing them up first (e.g. create a branch).");
|
|
3574
3422
|
const proceed = await confirmPrompt("Discard local commits and sync to remote?");
|
|
3575
3423
|
if (!proceed) {
|
|
@@ -3586,10 +3434,10 @@ var start_default = defineCommand9({
|
|
|
3586
3434
|
error(`Failed to create branch: ${result2.stderr}`);
|
|
3587
3435
|
process.exit(1);
|
|
3588
3436
|
}
|
|
3589
|
-
success(`✅ Created ${
|
|
3437
|
+
success(`✅ Created ${pc11.bold(branchName)} from ${pc11.bold(syncSource.ref)}`);
|
|
3590
3438
|
return;
|
|
3591
3439
|
}
|
|
3592
|
-
error(`Failed to update ${
|
|
3440
|
+
error(`Failed to update ${pc11.bold(baseBranch)}: ${updateResult.stderr}`);
|
|
3593
3441
|
info("Make sure your base branch exists locally or the remote ref is available.");
|
|
3594
3442
|
process.exit(1);
|
|
3595
3443
|
}
|
|
@@ -3598,14 +3446,14 @@ var start_default = defineCommand9({
|
|
|
3598
3446
|
error(`Failed to create branch: ${result.stderr}`);
|
|
3599
3447
|
process.exit(1);
|
|
3600
3448
|
}
|
|
3601
|
-
success(`✅ Created ${
|
|
3449
|
+
success(`✅ Created ${pc11.bold(branchName)} from latest ${pc11.bold(baseBranch)}`);
|
|
3602
3450
|
}
|
|
3603
3451
|
});
|
|
3604
3452
|
|
|
3605
3453
|
// src/commands/status.ts
|
|
3606
|
-
import { defineCommand as
|
|
3607
|
-
import
|
|
3608
|
-
var status_default =
|
|
3454
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
3455
|
+
import pc12 from "picocolors";
|
|
3456
|
+
var status_default = defineCommand9({
|
|
3609
3457
|
meta: {
|
|
3610
3458
|
name: "status",
|
|
3611
3459
|
description: "Show sync status of branches"
|
|
@@ -3621,8 +3469,8 @@ var status_default = defineCommand10({
|
|
|
3621
3469
|
process.exit(1);
|
|
3622
3470
|
}
|
|
3623
3471
|
heading("\uD83D\uDCCA contribute-now status");
|
|
3624
|
-
console.log(` ${
|
|
3625
|
-
console.log(` ${
|
|
3472
|
+
console.log(` ${pc12.dim("Workflow:")} ${pc12.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
|
|
3473
|
+
console.log(` ${pc12.dim("Role:")} ${pc12.bold(config.role)}`);
|
|
3626
3474
|
console.log();
|
|
3627
3475
|
await fetchAll();
|
|
3628
3476
|
const currentBranch = await getCurrentBranch();
|
|
@@ -3631,7 +3479,7 @@ var status_default = defineCommand10({
|
|
|
3631
3479
|
const isContributor = config.role === "contributor";
|
|
3632
3480
|
const [dirty, fileStatus] = await Promise.all([hasUncommittedChanges(), getFileStatus()]);
|
|
3633
3481
|
if (dirty) {
|
|
3634
|
-
console.log(` ${
|
|
3482
|
+
console.log(` ${pc12.yellow("⚠")} ${pc12.yellow("Uncommitted changes in working tree")}`);
|
|
3635
3483
|
console.log();
|
|
3636
3484
|
}
|
|
3637
3485
|
const mainRemote = `${origin}/${mainBranch}`;
|
|
@@ -3650,82 +3498,82 @@ var status_default = defineCommand10({
|
|
|
3650
3498
|
if (isFeatureBranch) {
|
|
3651
3499
|
const branchDiv = await getDivergence(currentBranch, baseBranch);
|
|
3652
3500
|
const branchLine = formatStatus(currentBranch, baseBranch, branchDiv.ahead, branchDiv.behind);
|
|
3653
|
-
console.log(branchLine +
|
|
3501
|
+
console.log(branchLine + pc12.dim(` (current ${pc12.green("*")})`));
|
|
3654
3502
|
branchStatus = await detectBranchStatus(currentBranch, baseBranch);
|
|
3655
3503
|
if (branchStatus.merged) {
|
|
3656
|
-
console.log(` ${
|
|
3504
|
+
console.log(` ${pc12.green("✓")} ${pc12.green("Branch merged")} — ${pc12.dim(branchStatus.mergedReason ?? "all commits reachable from base")}`);
|
|
3657
3505
|
}
|
|
3658
3506
|
if (branchStatus.stale) {
|
|
3659
|
-
console.log(` ${
|
|
3507
|
+
console.log(` ${pc12.yellow("⏳")} ${pc12.yellow("Branch is stale")} — ${pc12.dim(`last commit ${branchStatus.staleDaysAgo} days ago`)}`);
|
|
3660
3508
|
}
|
|
3661
3509
|
} else if (currentBranch) {
|
|
3662
|
-
console.log(
|
|
3510
|
+
console.log(pc12.dim(` (on ${pc12.bold(currentBranch)} branch)`));
|
|
3663
3511
|
}
|
|
3664
3512
|
const hasFiles = fileStatus.staged.length > 0 || fileStatus.modified.length > 0 || fileStatus.untracked.length > 0;
|
|
3665
3513
|
if (hasFiles) {
|
|
3666
3514
|
console.log();
|
|
3667
3515
|
if (fileStatus.staged.length > 0) {
|
|
3668
|
-
console.log(` ${
|
|
3516
|
+
console.log(` ${pc12.green("Staged for commit:")}`);
|
|
3669
3517
|
for (const { file, status } of fileStatus.staged) {
|
|
3670
|
-
console.log(` ${
|
|
3518
|
+
console.log(` ${pc12.green("+")} ${pc12.dim(`${status}:`)} ${file}`);
|
|
3671
3519
|
}
|
|
3672
3520
|
}
|
|
3673
3521
|
if (fileStatus.modified.length > 0) {
|
|
3674
|
-
console.log(` ${
|
|
3522
|
+
console.log(` ${pc12.yellow("Unstaged changes:")}`);
|
|
3675
3523
|
for (const { file, status } of fileStatus.modified) {
|
|
3676
|
-
console.log(` ${
|
|
3524
|
+
console.log(` ${pc12.yellow("~")} ${pc12.dim(`${status}:`)} ${file}`);
|
|
3677
3525
|
}
|
|
3678
3526
|
}
|
|
3679
3527
|
if (fileStatus.untracked.length > 0) {
|
|
3680
|
-
console.log(` ${
|
|
3528
|
+
console.log(` ${pc12.red("Untracked files:")}`);
|
|
3681
3529
|
for (const file of fileStatus.untracked) {
|
|
3682
|
-
console.log(` ${
|
|
3530
|
+
console.log(` ${pc12.red("?")} ${file}`);
|
|
3683
3531
|
}
|
|
3684
3532
|
}
|
|
3685
3533
|
} else if (!dirty) {
|
|
3686
|
-
console.log(` ${
|
|
3534
|
+
console.log(` ${pc12.green("✓")} ${pc12.dim("Working tree clean")}`);
|
|
3687
3535
|
}
|
|
3688
3536
|
const tips = [];
|
|
3689
3537
|
if (fileStatus.staged.length > 0) {
|
|
3690
|
-
tips.push(`Run ${
|
|
3538
|
+
tips.push(`Run ${pc12.bold("contrib commit")} to commit staged changes`);
|
|
3691
3539
|
}
|
|
3692
3540
|
if (fileStatus.modified.length > 0 || fileStatus.untracked.length > 0) {
|
|
3693
|
-
tips.push(`Run ${
|
|
3541
|
+
tips.push(`Run ${pc12.bold("contrib commit")} to stage and commit changes`);
|
|
3694
3542
|
}
|
|
3695
3543
|
if (isFeatureBranch && branchStatus) {
|
|
3696
3544
|
if (branchStatus.merged) {
|
|
3697
|
-
tips.push(`Run ${
|
|
3545
|
+
tips.push(`Run ${pc12.bold("contrib clean")} to delete this merged branch`);
|
|
3698
3546
|
} else if (branchStatus.stale) {
|
|
3699
|
-
tips.push(`Run ${
|
|
3547
|
+
tips.push(`Run ${pc12.bold("contrib sync")} to rebase on latest changes, or ${pc12.bold("contrib clean")} if no longer needed`);
|
|
3700
3548
|
} else if (fileStatus.staged.length === 0 && fileStatus.modified.length === 0 && fileStatus.untracked.length === 0) {
|
|
3701
3549
|
const branchDiv = await getDivergence(currentBranch, `${origin}/${currentBranch}`);
|
|
3702
3550
|
if (branchDiv.ahead > 0) {
|
|
3703
|
-
tips.push(`Run ${
|
|
3551
|
+
tips.push(`Run ${pc12.bold("contrib submit")} to push and create/update your PR`);
|
|
3704
3552
|
}
|
|
3705
3553
|
}
|
|
3706
3554
|
}
|
|
3707
3555
|
if (tips.length > 0) {
|
|
3708
3556
|
console.log();
|
|
3709
|
-
console.log(` ${
|
|
3557
|
+
console.log(` ${pc12.dim("\uD83D\uDCA1 Tip:")}`);
|
|
3710
3558
|
for (const tip of tips) {
|
|
3711
|
-
console.log(` ${
|
|
3559
|
+
console.log(` ${pc12.dim(tip)}`);
|
|
3712
3560
|
}
|
|
3713
3561
|
}
|
|
3714
3562
|
console.log();
|
|
3715
3563
|
}
|
|
3716
3564
|
});
|
|
3717
3565
|
function formatStatus(branch, base, ahead, behind) {
|
|
3718
|
-
const label =
|
|
3566
|
+
const label = pc12.bold(branch.padEnd(20));
|
|
3719
3567
|
if (ahead === 0 && behind === 0) {
|
|
3720
|
-
return ` ${
|
|
3568
|
+
return ` ${pc12.green("✓")} ${label} ${pc12.dim(`in sync with ${base}`)}`;
|
|
3721
3569
|
}
|
|
3722
3570
|
if (ahead > 0 && behind === 0) {
|
|
3723
|
-
return ` ${
|
|
3571
|
+
return ` ${pc12.yellow("↑")} ${label} ${pc12.yellow(`${ahead} commit${ahead !== 1 ? "s" : ""} ahead of ${base}`)}`;
|
|
3724
3572
|
}
|
|
3725
3573
|
if (behind > 0 && ahead === 0) {
|
|
3726
|
-
return ` ${
|
|
3574
|
+
return ` ${pc12.red("↓")} ${label} ${pc12.red(`${behind} commit${behind !== 1 ? "s" : ""} behind ${base}`)}`;
|
|
3727
3575
|
}
|
|
3728
|
-
return ` ${
|
|
3576
|
+
return ` ${pc12.red("⚡")} ${label} ${pc12.yellow(`${ahead} ahead`)}${pc12.dim(", ")}${pc12.red(`${behind} behind`)} ${pc12.dim(base)}`;
|
|
3729
3577
|
}
|
|
3730
3578
|
var STALE_THRESHOLD_DAYS = 14;
|
|
3731
3579
|
async function detectBranchStatus(branch, baseBranch) {
|
|
@@ -3771,16 +3619,16 @@ async function detectBranchStatus(branch, baseBranch) {
|
|
|
3771
3619
|
}
|
|
3772
3620
|
|
|
3773
3621
|
// src/commands/submit.ts
|
|
3774
|
-
import { defineCommand as
|
|
3775
|
-
import
|
|
3622
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
3623
|
+
import pc13 from "picocolors";
|
|
3776
3624
|
async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
3777
|
-
info(`Checking out ${
|
|
3625
|
+
info(`Checking out ${pc13.bold(baseBranch)}...`);
|
|
3778
3626
|
const coResult = await checkoutBranch(baseBranch);
|
|
3779
3627
|
if (coResult.exitCode !== 0) {
|
|
3780
3628
|
error(`Failed to checkout ${baseBranch}: ${coResult.stderr}`);
|
|
3781
3629
|
process.exit(1);
|
|
3782
3630
|
}
|
|
3783
|
-
info(`Squash merging ${
|
|
3631
|
+
info(`Squash merging ${pc13.bold(featureBranch)} into ${pc13.bold(baseBranch)}...`);
|
|
3784
3632
|
const mergeResult = await mergeSquash(featureBranch);
|
|
3785
3633
|
if (mergeResult.exitCode !== 0) {
|
|
3786
3634
|
error(`Squash merge failed: ${mergeResult.stderr}`);
|
|
@@ -3797,7 +3645,7 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
3797
3645
|
message = aiMsg;
|
|
3798
3646
|
spinner.success("AI commit message generated.");
|
|
3799
3647
|
console.log(`
|
|
3800
|
-
${
|
|
3648
|
+
${pc13.dim("AI suggestion:")} ${pc13.bold(pc13.cyan(message))}`);
|
|
3801
3649
|
} else {
|
|
3802
3650
|
spinner.fail("AI did not return a commit message.");
|
|
3803
3651
|
}
|
|
@@ -3826,7 +3674,7 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
3826
3674
|
message = regen;
|
|
3827
3675
|
spinner.success("Commit message regenerated.");
|
|
3828
3676
|
console.log(`
|
|
3829
|
-
${
|
|
3677
|
+
${pc13.dim("AI suggestion:")} ${pc13.bold(pc13.cyan(regen))}`);
|
|
3830
3678
|
} else {
|
|
3831
3679
|
spinner.fail("Regeneration failed.");
|
|
3832
3680
|
finalMsg = await inputPrompt("Enter commit message");
|
|
@@ -3843,13 +3691,13 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
3843
3691
|
error(`Commit failed: ${commitResult.stderr}`);
|
|
3844
3692
|
process.exit(1);
|
|
3845
3693
|
}
|
|
3846
|
-
info(`Pushing ${
|
|
3694
|
+
info(`Pushing ${pc13.bold(baseBranch)} to ${origin}...`);
|
|
3847
3695
|
const pushResult = await pushBranch(origin, baseBranch);
|
|
3848
3696
|
if (pushResult.exitCode !== 0) {
|
|
3849
3697
|
error(`Failed to push ${baseBranch}: ${pushResult.stderr}`);
|
|
3850
3698
|
process.exit(1);
|
|
3851
3699
|
}
|
|
3852
|
-
info(`Deleting local branch ${
|
|
3700
|
+
info(`Deleting local branch ${pc13.bold(featureBranch)}...`);
|
|
3853
3701
|
const delLocal = await forceDeleteBranch(featureBranch);
|
|
3854
3702
|
if (delLocal.exitCode !== 0) {
|
|
3855
3703
|
warn(`Could not delete local branch: ${delLocal.stderr.trim()}`);
|
|
@@ -3857,16 +3705,16 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
3857
3705
|
const remoteBranchRef = `${origin}/${featureBranch}`;
|
|
3858
3706
|
const remoteExists = await branchExists(remoteBranchRef);
|
|
3859
3707
|
if (remoteExists) {
|
|
3860
|
-
info(`Deleting remote branch ${
|
|
3708
|
+
info(`Deleting remote branch ${pc13.bold(featureBranch)}...`);
|
|
3861
3709
|
const delRemote = await deleteRemoteBranch(origin, featureBranch);
|
|
3862
3710
|
if (delRemote.exitCode !== 0) {
|
|
3863
3711
|
warn(`Could not delete remote branch: ${delRemote.stderr.trim()}`);
|
|
3864
3712
|
}
|
|
3865
3713
|
}
|
|
3866
|
-
success(`✅ Squash merged ${
|
|
3867
|
-
info(`Run ${
|
|
3714
|
+
success(`✅ Squash merged ${pc13.bold(featureBranch)} into ${pc13.bold(baseBranch)} and pushed.`);
|
|
3715
|
+
info(`Run ${pc13.bold("contrib start")} to begin a new feature.`);
|
|
3868
3716
|
}
|
|
3869
|
-
var submit_default =
|
|
3717
|
+
var submit_default = defineCommand10({
|
|
3870
3718
|
meta: {
|
|
3871
3719
|
name: "submit",
|
|
3872
3720
|
description: "Push current branch and create a pull request"
|
|
@@ -3908,7 +3756,7 @@ var submit_default = defineCommand11({
|
|
|
3908
3756
|
}
|
|
3909
3757
|
if (protectedBranches.includes(currentBranch)) {
|
|
3910
3758
|
heading("\uD83D\uDE80 contrib submit");
|
|
3911
|
-
warn(`You're on ${
|
|
3759
|
+
warn(`You're on ${pc13.bold(currentBranch)}, which is a protected branch. PRs should come from feature branches.`);
|
|
3912
3760
|
await fetchAll();
|
|
3913
3761
|
const remoteRef = `${origin}/${currentBranch}`;
|
|
3914
3762
|
const localWork = await hasLocalWork(origin, currentBranch);
|
|
@@ -3917,11 +3765,11 @@ var submit_default = defineCommand11({
|
|
|
3917
3765
|
const hasAnything = hasCommits || dirty;
|
|
3918
3766
|
if (!hasAnything) {
|
|
3919
3767
|
error("No local changes or commits to move. Switch to a feature branch first.");
|
|
3920
|
-
info(` Run ${
|
|
3768
|
+
info(` Run ${pc13.bold("contrib start")} to create a new feature branch.`);
|
|
3921
3769
|
process.exit(1);
|
|
3922
3770
|
}
|
|
3923
3771
|
if (hasCommits) {
|
|
3924
|
-
info(`Found ${
|
|
3772
|
+
info(`Found ${pc13.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${pc13.bold(currentBranch)}.`);
|
|
3925
3773
|
}
|
|
3926
3774
|
if (dirty) {
|
|
3927
3775
|
info("You also have uncommitted changes in the working tree.");
|
|
@@ -3937,7 +3785,7 @@ var submit_default = defineCommand11({
|
|
|
3937
3785
|
info("No changes made. You are still on your current branch.");
|
|
3938
3786
|
return;
|
|
3939
3787
|
}
|
|
3940
|
-
info(
|
|
3788
|
+
info(pc13.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
|
|
3941
3789
|
const description = await inputPrompt("What are you going to work on?");
|
|
3942
3790
|
let newBranchName = description;
|
|
3943
3791
|
if (looksLikeNaturalLanguage(description)) {
|
|
@@ -3948,8 +3796,8 @@ var submit_default = defineCommand11({
|
|
|
3948
3796
|
if (suggested) {
|
|
3949
3797
|
spinner.success("Branch name suggestion ready.");
|
|
3950
3798
|
console.log(`
|
|
3951
|
-
${
|
|
3952
|
-
const accepted = await confirmPrompt(`Use ${
|
|
3799
|
+
${pc13.dim("AI suggestion:")} ${pc13.bold(pc13.cyan(suggested))}`);
|
|
3800
|
+
const accepted = await confirmPrompt(`Use ${pc13.bold(suggested)} as your branch name?`);
|
|
3953
3801
|
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
3954
3802
|
} else {
|
|
3955
3803
|
spinner.fail("AI did not return a suggestion.");
|
|
@@ -3958,7 +3806,7 @@ var submit_default = defineCommand11({
|
|
|
3958
3806
|
}
|
|
3959
3807
|
}
|
|
3960
3808
|
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
3961
|
-
const prefix = await selectPrompt(`Choose a branch type for ${
|
|
3809
|
+
const prefix = await selectPrompt(`Choose a branch type for ${pc13.bold(newBranchName)}:`, config.branchPrefixes);
|
|
3962
3810
|
newBranchName = formatBranchName(prefix, newBranchName);
|
|
3963
3811
|
}
|
|
3964
3812
|
if (!isValidBranchName(newBranchName)) {
|
|
@@ -3966,7 +3814,7 @@ var submit_default = defineCommand11({
|
|
|
3966
3814
|
process.exit(1);
|
|
3967
3815
|
}
|
|
3968
3816
|
if (await branchExists(newBranchName)) {
|
|
3969
|
-
error(`Branch ${
|
|
3817
|
+
error(`Branch ${pc13.bold(newBranchName)} already exists. Choose a different name.`);
|
|
3970
3818
|
process.exit(1);
|
|
3971
3819
|
}
|
|
3972
3820
|
const branchResult = await createBranch(newBranchName);
|
|
@@ -3974,12 +3822,12 @@ var submit_default = defineCommand11({
|
|
|
3974
3822
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
3975
3823
|
process.exit(1);
|
|
3976
3824
|
}
|
|
3977
|
-
success(`Created ${
|
|
3825
|
+
success(`Created ${pc13.bold(newBranchName)} with your changes.`);
|
|
3978
3826
|
await updateLocalBranch(currentBranch, remoteRef);
|
|
3979
|
-
info(`Reset ${
|
|
3827
|
+
info(`Reset ${pc13.bold(currentBranch)} back to ${pc13.bold(remoteRef)} — no damage done.`);
|
|
3980
3828
|
console.log();
|
|
3981
|
-
success(`You're now on ${
|
|
3982
|
-
info(`Run ${
|
|
3829
|
+
success(`You're now on ${pc13.bold(newBranchName)} with all your work intact.`);
|
|
3830
|
+
info(`Run ${pc13.bold("contrib submit")} again to push and create your PR.`);
|
|
3983
3831
|
return;
|
|
3984
3832
|
}
|
|
3985
3833
|
heading("\uD83D\uDE80 contrib submit");
|
|
@@ -3988,7 +3836,7 @@ var submit_default = defineCommand11({
|
|
|
3988
3836
|
if (ghInstalled && ghAuthed) {
|
|
3989
3837
|
const mergedPR = await getMergedPRForBranch(currentBranch);
|
|
3990
3838
|
if (mergedPR) {
|
|
3991
|
-
warn(`PR #${mergedPR.number} (${
|
|
3839
|
+
warn(`PR #${mergedPR.number} (${pc13.bold(mergedPR.title)}) was already merged.`);
|
|
3992
3840
|
const localWork = await hasLocalWork(origin, currentBranch);
|
|
3993
3841
|
const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
|
|
3994
3842
|
if (hasWork) {
|
|
@@ -3996,7 +3844,7 @@ var submit_default = defineCommand11({
|
|
|
3996
3844
|
warn("You have uncommitted changes in your working tree.");
|
|
3997
3845
|
}
|
|
3998
3846
|
if (localWork.unpushedCommits > 0) {
|
|
3999
|
-
warn(`You have ${
|
|
3847
|
+
warn(`You have ${pc13.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not in the merged PR.`);
|
|
4000
3848
|
}
|
|
4001
3849
|
const SAVE_NEW_BRANCH = "Save changes to a new branch";
|
|
4002
3850
|
const DISCARD = "Discard all changes and clean up";
|
|
@@ -4007,7 +3855,7 @@ var submit_default = defineCommand11({
|
|
|
4007
3855
|
return;
|
|
4008
3856
|
}
|
|
4009
3857
|
if (action === SAVE_NEW_BRANCH) {
|
|
4010
|
-
info(
|
|
3858
|
+
info(pc13.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
|
|
4011
3859
|
const description = await inputPrompt("What are you going to work on?");
|
|
4012
3860
|
let newBranchName = description;
|
|
4013
3861
|
if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
|
|
@@ -4016,8 +3864,8 @@ var submit_default = defineCommand11({
|
|
|
4016
3864
|
if (suggested) {
|
|
4017
3865
|
spinner.success("Branch name suggestion ready.");
|
|
4018
3866
|
console.log(`
|
|
4019
|
-
${
|
|
4020
|
-
const accepted = await confirmPrompt(`Use ${
|
|
3867
|
+
${pc13.dim("AI suggestion:")} ${pc13.bold(pc13.cyan(suggested))}`);
|
|
3868
|
+
const accepted = await confirmPrompt(`Use ${pc13.bold(suggested)} as your branch name?`);
|
|
4021
3869
|
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
4022
3870
|
} else {
|
|
4023
3871
|
spinner.fail("AI did not return a suggestion.");
|
|
@@ -4025,7 +3873,7 @@ var submit_default = defineCommand11({
|
|
|
4025
3873
|
}
|
|
4026
3874
|
}
|
|
4027
3875
|
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
4028
|
-
const prefix = await selectPrompt(`Choose a branch type for ${
|
|
3876
|
+
const prefix = await selectPrompt(`Choose a branch type for ${pc13.bold(newBranchName)}:`, config.branchPrefixes);
|
|
4029
3877
|
newBranchName = formatBranchName(prefix, newBranchName);
|
|
4030
3878
|
}
|
|
4031
3879
|
if (!isValidBranchName(newBranchName)) {
|
|
@@ -4035,7 +3883,7 @@ var submit_default = defineCommand11({
|
|
|
4035
3883
|
const staleUpstream = await getUpstreamRef();
|
|
4036
3884
|
const staleUpstreamHash = staleUpstream ? await getCommitHash(staleUpstream) : null;
|
|
4037
3885
|
if (await branchExists(newBranchName)) {
|
|
4038
|
-
error(`Branch ${
|
|
3886
|
+
error(`Branch ${pc13.bold(newBranchName)} already exists. Choose a different name.`);
|
|
4039
3887
|
process.exit(1);
|
|
4040
3888
|
}
|
|
4041
3889
|
const renameResult = await renameBranch(currentBranch, newBranchName);
|
|
@@ -4043,10 +3891,10 @@ var submit_default = defineCommand11({
|
|
|
4043
3891
|
error(`Failed to rename branch: ${renameResult.stderr}`);
|
|
4044
3892
|
process.exit(1);
|
|
4045
3893
|
}
|
|
4046
|
-
success(`Renamed ${
|
|
3894
|
+
success(`Renamed ${pc13.bold(currentBranch)} → ${pc13.bold(newBranchName)}`);
|
|
4047
3895
|
await unsetUpstream();
|
|
4048
3896
|
const syncSource2 = getSyncSource(config);
|
|
4049
|
-
info(`Syncing ${
|
|
3897
|
+
info(`Syncing ${pc13.bold(newBranchName)} with latest ${pc13.bold(baseBranch)}...`);
|
|
4050
3898
|
await fetchRemote(syncSource2.remote);
|
|
4051
3899
|
let rebaseResult;
|
|
4052
3900
|
if (staleUpstreamHash) {
|
|
@@ -4057,17 +3905,17 @@ var submit_default = defineCommand11({
|
|
|
4057
3905
|
}
|
|
4058
3906
|
if (rebaseResult.exitCode !== 0) {
|
|
4059
3907
|
warn("Rebase encountered conflicts. Resolve them manually, then run:");
|
|
4060
|
-
info(` ${
|
|
3908
|
+
info(` ${pc13.bold("git rebase --continue")}`);
|
|
4061
3909
|
} else {
|
|
4062
|
-
success(`Rebased ${
|
|
3910
|
+
success(`Rebased ${pc13.bold(newBranchName)} onto ${pc13.bold(syncSource2.ref)}.`);
|
|
4063
3911
|
}
|
|
4064
|
-
info(`All your changes are preserved. Run ${
|
|
3912
|
+
info(`All your changes are preserved. Run ${pc13.bold("contrib submit")} when ready to create a new PR.`);
|
|
4065
3913
|
return;
|
|
4066
3914
|
}
|
|
4067
3915
|
warn("Discarding local changes...");
|
|
4068
3916
|
}
|
|
4069
3917
|
const syncSource = getSyncSource(config);
|
|
4070
|
-
info(`Switching to ${
|
|
3918
|
+
info(`Switching to ${pc13.bold(baseBranch)} and syncing...`);
|
|
4071
3919
|
await fetchRemote(syncSource.remote);
|
|
4072
3920
|
await resetHard("HEAD");
|
|
4073
3921
|
const coResult = await checkoutBranch(baseBranch);
|
|
@@ -4076,23 +3924,23 @@ var submit_default = defineCommand11({
|
|
|
4076
3924
|
process.exit(1);
|
|
4077
3925
|
}
|
|
4078
3926
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
4079
|
-
success(`Synced ${
|
|
4080
|
-
info(`Deleting stale branch ${
|
|
3927
|
+
success(`Synced ${pc13.bold(baseBranch)} with ${pc13.bold(syncSource.ref)}.`);
|
|
3928
|
+
info(`Deleting stale branch ${pc13.bold(currentBranch)}...`);
|
|
4081
3929
|
const delResult = await forceDeleteBranch(currentBranch);
|
|
4082
3930
|
if (delResult.exitCode === 0) {
|
|
4083
|
-
success(`Deleted ${
|
|
3931
|
+
success(`Deleted ${pc13.bold(currentBranch)}.`);
|
|
4084
3932
|
} else {
|
|
4085
3933
|
warn(`Could not delete branch: ${delResult.stderr.trim()}`);
|
|
4086
3934
|
}
|
|
4087
3935
|
console.log();
|
|
4088
|
-
info(`You're now on ${
|
|
3936
|
+
info(`You're now on ${pc13.bold(baseBranch)}. Run ${pc13.bold("contrib start")} to begin a new feature.`);
|
|
4089
3937
|
return;
|
|
4090
3938
|
}
|
|
4091
3939
|
}
|
|
4092
3940
|
if (ghInstalled && ghAuthed) {
|
|
4093
3941
|
const existingPR = await getPRForBranch(currentBranch);
|
|
4094
3942
|
if (existingPR) {
|
|
4095
|
-
info(`Pushing ${
|
|
3943
|
+
info(`Pushing ${pc13.bold(currentBranch)} to ${origin}...`);
|
|
4096
3944
|
const pushResult2 = await pushSetUpstream(origin, currentBranch);
|
|
4097
3945
|
if (pushResult2.exitCode !== 0) {
|
|
4098
3946
|
error(`Failed to push: ${pushResult2.stderr}`);
|
|
@@ -4103,8 +3951,8 @@ var submit_default = defineCommand11({
|
|
|
4103
3951
|
}
|
|
4104
3952
|
process.exit(1);
|
|
4105
3953
|
}
|
|
4106
|
-
success(`Pushed changes to existing PR #${existingPR.number}: ${
|
|
4107
|
-
console.log(` ${
|
|
3954
|
+
success(`Pushed changes to existing PR #${existingPR.number}: ${pc13.bold(existingPR.title)}`);
|
|
3955
|
+
console.log(` ${pc13.cyan(existingPR.url)}`);
|
|
4108
3956
|
return;
|
|
4109
3957
|
}
|
|
4110
3958
|
}
|
|
@@ -4124,10 +3972,10 @@ var submit_default = defineCommand11({
|
|
|
4124
3972
|
prBody = result.body;
|
|
4125
3973
|
spinner.success("PR description generated.");
|
|
4126
3974
|
console.log(`
|
|
4127
|
-
${
|
|
3975
|
+
${pc13.dim("AI title:")} ${pc13.bold(pc13.cyan(prTitle))}`);
|
|
4128
3976
|
console.log(`
|
|
4129
|
-
${
|
|
4130
|
-
console.log(
|
|
3977
|
+
${pc13.dim("AI body preview:")}`);
|
|
3978
|
+
console.log(pc13.dim(prBody.slice(0, 300) + (prBody.length > 300 ? "..." : "")));
|
|
4131
3979
|
} else {
|
|
4132
3980
|
spinner.fail("AI did not return a PR description.");
|
|
4133
3981
|
}
|
|
@@ -4215,7 +4063,7 @@ ${pc14.dim("AI body preview:")}`);
|
|
|
4215
4063
|
});
|
|
4216
4064
|
return;
|
|
4217
4065
|
}
|
|
4218
|
-
info(`Pushing ${
|
|
4066
|
+
info(`Pushing ${pc13.bold(currentBranch)} to ${origin}...`);
|
|
4219
4067
|
const pushResult = await pushSetUpstream(origin, currentBranch);
|
|
4220
4068
|
if (pushResult.exitCode !== 0) {
|
|
4221
4069
|
error(`Failed to push: ${pushResult.stderr}`);
|
|
@@ -4234,7 +4082,7 @@ ${pc14.dim("AI body preview:")}`);
|
|
|
4234
4082
|
const prUrl = `https://github.com/${repoInfo.owner}/${repoInfo.repo}/compare/${baseBranch}...${currentBranch}?expand=1`;
|
|
4235
4083
|
console.log();
|
|
4236
4084
|
info("Create your PR manually:");
|
|
4237
|
-
console.log(` ${
|
|
4085
|
+
console.log(` ${pc13.cyan(prUrl)}`);
|
|
4238
4086
|
} else {
|
|
4239
4087
|
info("gh CLI not available. Create your PR manually on GitHub.");
|
|
4240
4088
|
}
|
|
@@ -4267,109 +4115,10 @@ ${pc14.dim("AI body preview:")}`);
|
|
|
4267
4115
|
}
|
|
4268
4116
|
});
|
|
4269
4117
|
|
|
4270
|
-
// src/commands/switch.ts
|
|
4271
|
-
import { defineCommand as defineCommand12 } from "citty";
|
|
4272
|
-
import pc15 from "picocolors";
|
|
4273
|
-
var switch_default = defineCommand12({
|
|
4274
|
-
meta: {
|
|
4275
|
-
name: "switch",
|
|
4276
|
-
description: "Switch to another branch with stash protection for uncommitted changes"
|
|
4277
|
-
},
|
|
4278
|
-
args: {
|
|
4279
|
-
name: {
|
|
4280
|
-
type: "positional",
|
|
4281
|
-
description: "Branch name to switch to (interactive picker if omitted)",
|
|
4282
|
-
required: false
|
|
4283
|
-
}
|
|
4284
|
-
},
|
|
4285
|
-
async run({ args }) {
|
|
4286
|
-
if (!await isGitRepo()) {
|
|
4287
|
-
error("Not inside a git repository.");
|
|
4288
|
-
process.exit(1);
|
|
4289
|
-
}
|
|
4290
|
-
const config = readConfig();
|
|
4291
|
-
const protectedBranches = config ? getProtectedBranches(config) : ["main", "master"];
|
|
4292
|
-
const currentBranch = await getCurrentBranch();
|
|
4293
|
-
heading("\uD83D\uDD00 contrib switch");
|
|
4294
|
-
let targetBranch = args.name;
|
|
4295
|
-
if (!targetBranch) {
|
|
4296
|
-
const localBranches = await getLocalBranches();
|
|
4297
|
-
if (localBranches.length === 0) {
|
|
4298
|
-
error("No local branches found.");
|
|
4299
|
-
process.exit(1);
|
|
4300
|
-
}
|
|
4301
|
-
const choices = localBranches.filter((b) => b.name !== currentBranch).map((b) => {
|
|
4302
|
-
const labels = [];
|
|
4303
|
-
if (protectedBranches.includes(b.name))
|
|
4304
|
-
labels.push(pc15.red("protected"));
|
|
4305
|
-
if (b.upstream)
|
|
4306
|
-
labels.push(pc15.dim(`→ ${b.upstream}`));
|
|
4307
|
-
if (b.gone)
|
|
4308
|
-
labels.push(pc15.red("remote gone"));
|
|
4309
|
-
const suffix = labels.length > 0 ? ` ${labels.join(" · ")}` : "";
|
|
4310
|
-
return `${b.name}${suffix}`;
|
|
4311
|
-
});
|
|
4312
|
-
if (choices.length === 0) {
|
|
4313
|
-
info("You are already on the only local branch.");
|
|
4314
|
-
process.exit(0);
|
|
4315
|
-
}
|
|
4316
|
-
const selected = await selectPrompt("Switch to which branch?", choices);
|
|
4317
|
-
targetBranch = selected.split(/\s{2,}/)[0].trim();
|
|
4318
|
-
}
|
|
4319
|
-
if (targetBranch === currentBranch) {
|
|
4320
|
-
info(`Already on ${pc15.bold(targetBranch)}.`);
|
|
4321
|
-
return;
|
|
4322
|
-
}
|
|
4323
|
-
if (await hasUncommittedChanges()) {
|
|
4324
|
-
warn("You have uncommitted changes.");
|
|
4325
|
-
const action = await selectPrompt("How would you like to handle them?", [
|
|
4326
|
-
"Save changes and switch",
|
|
4327
|
-
"Cancel"
|
|
4328
|
-
]);
|
|
4329
|
-
if (action === "Cancel") {
|
|
4330
|
-
info("Switch cancelled.");
|
|
4331
|
-
return;
|
|
4332
|
-
}
|
|
4333
|
-
const { execFile } = await import("node:child_process");
|
|
4334
|
-
const { promisify } = await import("node:util");
|
|
4335
|
-
const exec = promisify(execFile);
|
|
4336
|
-
const stashMsg = `contrib-save: auto-save from ${currentBranch}`;
|
|
4337
|
-
try {
|
|
4338
|
-
await exec("git", ["stash", "push", "-m", stashMsg]);
|
|
4339
|
-
info(`Saved changes: ${pc15.dim(stashMsg)}`);
|
|
4340
|
-
} catch {
|
|
4341
|
-
error("Failed to save changes. Please commit or save manually.");
|
|
4342
|
-
process.exit(1);
|
|
4343
|
-
}
|
|
4344
|
-
const result2 = await checkoutBranch(targetBranch);
|
|
4345
|
-
if (result2.exitCode !== 0) {
|
|
4346
|
-
error(`Failed to switch to ${targetBranch}: ${result2.stderr}`);
|
|
4347
|
-
try {
|
|
4348
|
-
await exec("git", ["stash", "pop"]);
|
|
4349
|
-
info("Restored saved changes.");
|
|
4350
|
-
} catch {
|
|
4351
|
-
warn("Could not restore save automatically. Use `contrib save restore` to recover.");
|
|
4352
|
-
}
|
|
4353
|
-
process.exit(1);
|
|
4354
|
-
}
|
|
4355
|
-
success(`Switched to ${pc15.bold(targetBranch)}`);
|
|
4356
|
-
info(`Your changes from ${pc15.bold(currentBranch ?? "previous branch")} are saved.`);
|
|
4357
|
-
info(`Use ${pc15.bold("contrib save restore")} to bring them back.`);
|
|
4358
|
-
return;
|
|
4359
|
-
}
|
|
4360
|
-
const result = await checkoutBranch(targetBranch);
|
|
4361
|
-
if (result.exitCode !== 0) {
|
|
4362
|
-
error(`Failed to switch to ${targetBranch}: ${result.stderr}`);
|
|
4363
|
-
process.exit(1);
|
|
4364
|
-
}
|
|
4365
|
-
success(`Switched to ${pc15.bold(targetBranch)}`);
|
|
4366
|
-
}
|
|
4367
|
-
});
|
|
4368
|
-
|
|
4369
4118
|
// src/commands/sync.ts
|
|
4370
|
-
import { defineCommand as
|
|
4371
|
-
import
|
|
4372
|
-
var sync_default =
|
|
4119
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
4120
|
+
import pc14 from "picocolors";
|
|
4121
|
+
var sync_default = defineCommand11({
|
|
4373
4122
|
meta: {
|
|
4374
4123
|
name: "sync",
|
|
4375
4124
|
description: "Sync your local branches with the remote"
|
|
@@ -4420,24 +4169,24 @@ var sync_default = defineCommand13({
|
|
|
4420
4169
|
await fetchRemote(origin);
|
|
4421
4170
|
}
|
|
4422
4171
|
if (!await refExists(syncSource.ref)) {
|
|
4423
|
-
error(`Remote ref ${
|
|
4172
|
+
error(`Remote ref ${pc14.bold(syncSource.ref)} does not exist.`);
|
|
4424
4173
|
info("This can happen if the branch was renamed or deleted on the remote.");
|
|
4425
|
-
info(`Check your config: the base branch may need updating via ${
|
|
4174
|
+
info(`Check your config: the base branch may need updating via ${pc14.bold("contrib setup")}.`);
|
|
4426
4175
|
process.exit(1);
|
|
4427
4176
|
}
|
|
4428
4177
|
let allowMergeCommit = false;
|
|
4429
4178
|
const div = await getDivergence(baseBranch, syncSource.ref);
|
|
4430
4179
|
if (div.ahead > 0 || div.behind > 0) {
|
|
4431
|
-
info(`${
|
|
4180
|
+
info(`${pc14.bold(baseBranch)} is ${pc14.yellow(`${div.ahead} ahead`)} and ${pc14.red(`${div.behind} behind`)} ${syncSource.ref}`);
|
|
4432
4181
|
} else {
|
|
4433
|
-
info(`${
|
|
4182
|
+
info(`${pc14.bold(baseBranch)} is already in sync with ${syncSource.ref}`);
|
|
4434
4183
|
}
|
|
4435
4184
|
if (div.ahead > 0) {
|
|
4436
4185
|
const currentBranch = await getCurrentBranch();
|
|
4437
4186
|
const protectedBranches = getProtectedBranches(config);
|
|
4438
4187
|
const isOnProtected = currentBranch && protectedBranches.includes(currentBranch);
|
|
4439
4188
|
if (isOnProtected) {
|
|
4440
|
-
warn(`You have ${
|
|
4189
|
+
warn(`You have ${pc14.bold(String(div.ahead))} local commit${div.ahead !== 1 ? "s" : ""} on ${pc14.bold(baseBranch)} that aren't on the remote.`);
|
|
4441
4190
|
info("Pulling now could create a merge commit, which breaks clean history.");
|
|
4442
4191
|
console.log();
|
|
4443
4192
|
const MOVE_BRANCH = "Move my commits to a new feature branch, then sync";
|
|
@@ -4453,7 +4202,7 @@ var sync_default = defineCommand13({
|
|
|
4453
4202
|
return;
|
|
4454
4203
|
}
|
|
4455
4204
|
if (action === MOVE_BRANCH) {
|
|
4456
|
-
info(
|
|
4205
|
+
info(pc14.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
|
|
4457
4206
|
const description = await inputPrompt("What are you going to work on?");
|
|
4458
4207
|
let newBranchName = description;
|
|
4459
4208
|
if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
|
|
@@ -4464,8 +4213,8 @@ var sync_default = defineCommand13({
|
|
|
4464
4213
|
if (suggested) {
|
|
4465
4214
|
spinner.success("Branch name suggestion ready.");
|
|
4466
4215
|
console.log(`
|
|
4467
|
-
${
|
|
4468
|
-
const accepted = await confirmPrompt(`Use ${
|
|
4216
|
+
${pc14.dim("AI suggestion:")} ${pc14.bold(pc14.cyan(suggested))}`);
|
|
4217
|
+
const accepted = await confirmPrompt(`Use ${pc14.bold(suggested)} as your branch name?`);
|
|
4469
4218
|
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
4470
4219
|
} else {
|
|
4471
4220
|
spinner.fail("AI did not return a suggestion.");
|
|
@@ -4474,7 +4223,7 @@ var sync_default = defineCommand13({
|
|
|
4474
4223
|
}
|
|
4475
4224
|
}
|
|
4476
4225
|
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
4477
|
-
const prefix = await selectPrompt(`Choose a branch type for ${
|
|
4226
|
+
const prefix = await selectPrompt(`Choose a branch type for ${pc14.bold(newBranchName)}:`, config.branchPrefixes);
|
|
4478
4227
|
newBranchName = formatBranchName(prefix, newBranchName);
|
|
4479
4228
|
}
|
|
4480
4229
|
if (!isValidBranchName(newBranchName)) {
|
|
@@ -4482,7 +4231,7 @@ var sync_default = defineCommand13({
|
|
|
4482
4231
|
process.exit(1);
|
|
4483
4232
|
}
|
|
4484
4233
|
if (await branchExists(newBranchName)) {
|
|
4485
|
-
error(`Branch ${
|
|
4234
|
+
error(`Branch ${pc14.bold(newBranchName)} already exists. Choose a different name.`);
|
|
4486
4235
|
process.exit(1);
|
|
4487
4236
|
}
|
|
4488
4237
|
const branchResult = await createBranch(newBranchName);
|
|
@@ -4490,7 +4239,7 @@ var sync_default = defineCommand13({
|
|
|
4490
4239
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
4491
4240
|
process.exit(1);
|
|
4492
4241
|
}
|
|
4493
|
-
success(`Created ${
|
|
4242
|
+
success(`Created ${pc14.bold(newBranchName)} with your commits.`);
|
|
4494
4243
|
const coResult2 = await checkoutBranch(baseBranch);
|
|
4495
4244
|
if (coResult2.exitCode !== 0) {
|
|
4496
4245
|
error(`Failed to checkout ${baseBranch}: ${coResult2.stderr}`);
|
|
@@ -4498,11 +4247,11 @@ var sync_default = defineCommand13({
|
|
|
4498
4247
|
}
|
|
4499
4248
|
const remoteRef = syncSource.ref;
|
|
4500
4249
|
await updateLocalBranch(baseBranch, remoteRef);
|
|
4501
|
-
success(`Reset ${
|
|
4502
|
-
success(`✅ ${
|
|
4250
|
+
success(`Reset ${pc14.bold(baseBranch)} to ${pc14.bold(remoteRef)}.`);
|
|
4251
|
+
success(`✅ ${pc14.bold(baseBranch)} is now in sync with ${syncSource.ref}`);
|
|
4503
4252
|
console.log();
|
|
4504
|
-
info(`Your commits are safe on ${
|
|
4505
|
-
info(`Run ${
|
|
4253
|
+
info(`Your commits are safe on ${pc14.bold(newBranchName)}.`);
|
|
4254
|
+
info(`Run ${pc14.bold(`git checkout ${newBranchName}`)} then ${pc14.bold("contrib update")} to rebase onto the synced ${pc14.bold(baseBranch)}.`);
|
|
4506
4255
|
return;
|
|
4507
4256
|
}
|
|
4508
4257
|
allowMergeCommit = true;
|
|
@@ -4510,7 +4259,7 @@ var sync_default = defineCommand13({
|
|
|
4510
4259
|
}
|
|
4511
4260
|
}
|
|
4512
4261
|
if (!args.yes) {
|
|
4513
|
-
const ok = await confirmPrompt(`This will pull ${
|
|
4262
|
+
const ok = await confirmPrompt(`This will pull ${pc14.bold(syncSource.ref)} into local ${pc14.bold(baseBranch)}.`);
|
|
4514
4263
|
if (!ok)
|
|
4515
4264
|
process.exit(0);
|
|
4516
4265
|
}
|
|
@@ -4524,8 +4273,8 @@ var sync_default = defineCommand13({
|
|
|
4524
4273
|
if (allowMergeCommit) {
|
|
4525
4274
|
error(`Pull failed: ${pullResult.stderr.trim()}`);
|
|
4526
4275
|
} else {
|
|
4527
|
-
error(`Fast-forward pull failed. Your local ${
|
|
4528
|
-
info(`Use ${
|
|
4276
|
+
error(`Fast-forward pull failed. Your local ${pc14.bold(baseBranch)} may have diverged.`);
|
|
4277
|
+
info(`Use ${pc14.bold("contrib sync")} again and choose "Move my commits to a new feature branch" to fix this.`);
|
|
4529
4278
|
}
|
|
4530
4279
|
process.exit(1);
|
|
4531
4280
|
}
|
|
@@ -4533,7 +4282,7 @@ var sync_default = defineCommand13({
|
|
|
4533
4282
|
if (hasDevBranch(workflow) && role === "maintainer") {
|
|
4534
4283
|
const mainDiv = await getDivergence(config.mainBranch, `${origin}/${config.mainBranch}`);
|
|
4535
4284
|
if (mainDiv.behind > 0) {
|
|
4536
|
-
info(`Also syncing ${
|
|
4285
|
+
info(`Also syncing ${pc14.bold(config.mainBranch)}...`);
|
|
4537
4286
|
const mainCoResult = await checkoutBranch(config.mainBranch);
|
|
4538
4287
|
if (mainCoResult.exitCode === 0) {
|
|
4539
4288
|
const mainPullResult = await pullFastForwardOnly(origin, config.mainBranch);
|
|
@@ -4549,9 +4298,9 @@ var sync_default = defineCommand13({
|
|
|
4549
4298
|
|
|
4550
4299
|
// src/commands/update.ts
|
|
4551
4300
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
4552
|
-
import { defineCommand as
|
|
4553
|
-
import
|
|
4554
|
-
var update_default =
|
|
4301
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
4302
|
+
import pc15 from "picocolors";
|
|
4303
|
+
var update_default = defineCommand12({
|
|
4555
4304
|
meta: {
|
|
4556
4305
|
name: "update",
|
|
4557
4306
|
description: "Rebase current branch onto the latest base branch"
|
|
@@ -4588,7 +4337,7 @@ var update_default = defineCommand14({
|
|
|
4588
4337
|
}
|
|
4589
4338
|
if (protectedBranches.includes(currentBranch)) {
|
|
4590
4339
|
heading("\uD83D\uDD03 contrib update");
|
|
4591
|
-
warn(`You're on ${
|
|
4340
|
+
warn(`You're on ${pc15.bold(currentBranch)}, which is a protected branch. Updates (rebase) apply to feature branches.`);
|
|
4592
4341
|
await fetchAll();
|
|
4593
4342
|
const { origin } = config;
|
|
4594
4343
|
const remoteRef = `${origin}/${currentBranch}`;
|
|
@@ -4597,12 +4346,12 @@ var update_default = defineCommand14({
|
|
|
4597
4346
|
const hasCommits = localWork.unpushedCommits > 0;
|
|
4598
4347
|
const hasAnything = hasCommits || dirty;
|
|
4599
4348
|
if (!hasAnything) {
|
|
4600
|
-
info(`No local changes found on ${
|
|
4601
|
-
info(`Use ${
|
|
4349
|
+
info(`No local changes found on ${pc15.bold(currentBranch)}.`);
|
|
4350
|
+
info(`Use ${pc15.bold("contrib sync")} to sync protected branches, or ${pc15.bold("contrib start")} to create a feature branch.`);
|
|
4602
4351
|
process.exit(1);
|
|
4603
4352
|
}
|
|
4604
4353
|
if (hasCommits) {
|
|
4605
|
-
info(`Found ${
|
|
4354
|
+
info(`Found ${pc15.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${pc15.bold(currentBranch)}.`);
|
|
4606
4355
|
}
|
|
4607
4356
|
if (dirty) {
|
|
4608
4357
|
info("You also have uncommitted changes in the working tree.");
|
|
@@ -4618,7 +4367,7 @@ var update_default = defineCommand14({
|
|
|
4618
4367
|
info("No changes made. You are still on your current branch.");
|
|
4619
4368
|
return;
|
|
4620
4369
|
}
|
|
4621
|
-
info(
|
|
4370
|
+
info(pc15.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
|
|
4622
4371
|
const description = await inputPrompt("What are you going to work on?");
|
|
4623
4372
|
let newBranchName = description;
|
|
4624
4373
|
if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
|
|
@@ -4629,8 +4378,8 @@ var update_default = defineCommand14({
|
|
|
4629
4378
|
if (suggested) {
|
|
4630
4379
|
spinner.success("Branch name suggestion ready.");
|
|
4631
4380
|
console.log(`
|
|
4632
|
-
${
|
|
4633
|
-
const accepted = await confirmPrompt(`Use ${
|
|
4381
|
+
${pc15.dim("AI suggestion:")} ${pc15.bold(pc15.cyan(suggested))}`);
|
|
4382
|
+
const accepted = await confirmPrompt(`Use ${pc15.bold(suggested)} as your branch name?`);
|
|
4634
4383
|
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
4635
4384
|
} else {
|
|
4636
4385
|
spinner.fail("AI did not return a suggestion.");
|
|
@@ -4639,7 +4388,7 @@ var update_default = defineCommand14({
|
|
|
4639
4388
|
}
|
|
4640
4389
|
}
|
|
4641
4390
|
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
4642
|
-
const prefix = await selectPrompt(`Choose a branch type for ${
|
|
4391
|
+
const prefix = await selectPrompt(`Choose a branch type for ${pc15.bold(newBranchName)}:`, config.branchPrefixes);
|
|
4643
4392
|
newBranchName = formatBranchName(prefix, newBranchName);
|
|
4644
4393
|
}
|
|
4645
4394
|
if (!isValidBranchName(newBranchName)) {
|
|
@@ -4651,12 +4400,12 @@ var update_default = defineCommand14({
|
|
|
4651
4400
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
4652
4401
|
process.exit(1);
|
|
4653
4402
|
}
|
|
4654
|
-
success(`Created ${
|
|
4403
|
+
success(`Created ${pc15.bold(newBranchName)} with your changes.`);
|
|
4655
4404
|
await updateLocalBranch(currentBranch, remoteRef);
|
|
4656
|
-
info(`Reset ${
|
|
4405
|
+
info(`Reset ${pc15.bold(currentBranch)} back to ${pc15.bold(remoteRef)} — no damage done.`);
|
|
4657
4406
|
console.log();
|
|
4658
|
-
success(`You're now on ${
|
|
4659
|
-
info(`Run ${
|
|
4407
|
+
success(`You're now on ${pc15.bold(newBranchName)} with all your work intact.`);
|
|
4408
|
+
info(`Run ${pc15.bold("contrib update")} again to rebase onto latest ${pc15.bold(baseBranch)}.`);
|
|
4660
4409
|
return;
|
|
4661
4410
|
}
|
|
4662
4411
|
if (await hasUncommittedChanges()) {
|
|
@@ -4666,8 +4415,8 @@ var update_default = defineCommand14({
|
|
|
4666
4415
|
heading("\uD83D\uDD03 contrib update");
|
|
4667
4416
|
const mergedPR = await getMergedPRForBranch(currentBranch);
|
|
4668
4417
|
if (mergedPR) {
|
|
4669
|
-
warn(`PR #${mergedPR.number} (${
|
|
4670
|
-
info(`Link: ${
|
|
4418
|
+
warn(`PR #${mergedPR.number} (${pc15.bold(mergedPR.title)}) has already been merged.`);
|
|
4419
|
+
info(`Link: ${pc15.underline(mergedPR.url)}`);
|
|
4671
4420
|
const localWork = await hasLocalWork(syncSource.remote, currentBranch);
|
|
4672
4421
|
const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
|
|
4673
4422
|
if (hasWork) {
|
|
@@ -4680,13 +4429,13 @@ var update_default = defineCommand14({
|
|
|
4680
4429
|
const SAVE_NEW_BRANCH = "Save changes to a new branch";
|
|
4681
4430
|
const DISCARD = "Discard all changes and clean up";
|
|
4682
4431
|
const CANCEL = "Cancel";
|
|
4683
|
-
const action = await selectPrompt(`${
|
|
4432
|
+
const action = await selectPrompt(`${pc15.bold(currentBranch)} is stale but has local work. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
|
|
4684
4433
|
if (action === CANCEL) {
|
|
4685
4434
|
info("No changes made. You are still on your current branch.");
|
|
4686
4435
|
return;
|
|
4687
4436
|
}
|
|
4688
4437
|
if (action === SAVE_NEW_BRANCH) {
|
|
4689
|
-
info(
|
|
4438
|
+
info(pc15.dim("Tip: Describe what you're going to work on in plain English and we'll generate a branch name."));
|
|
4690
4439
|
const description = await inputPrompt("What are you going to work on?");
|
|
4691
4440
|
let newBranchName = description;
|
|
4692
4441
|
if (!args["no-ai"] && looksLikeNaturalLanguage(description)) {
|
|
@@ -4695,8 +4444,8 @@ var update_default = defineCommand14({
|
|
|
4695
4444
|
if (suggested) {
|
|
4696
4445
|
spinner.success("Branch name suggestion ready.");
|
|
4697
4446
|
console.log(`
|
|
4698
|
-
${
|
|
4699
|
-
const accepted = await confirmPrompt(`Use ${
|
|
4447
|
+
${pc15.dim("AI suggestion:")} ${pc15.bold(pc15.cyan(suggested))}`);
|
|
4448
|
+
const accepted = await confirmPrompt(`Use ${pc15.bold(suggested)} as your branch name?`);
|
|
4700
4449
|
newBranchName = accepted ? suggested : await inputPrompt("Enter branch name", description);
|
|
4701
4450
|
} else {
|
|
4702
4451
|
spinner.fail("AI did not return a suggestion.");
|
|
@@ -4704,7 +4453,7 @@ var update_default = defineCommand14({
|
|
|
4704
4453
|
}
|
|
4705
4454
|
}
|
|
4706
4455
|
if (!hasPrefix(newBranchName, config.branchPrefixes)) {
|
|
4707
|
-
const prefix = await selectPrompt(`Choose a branch type for ${
|
|
4456
|
+
const prefix = await selectPrompt(`Choose a branch type for ${pc15.bold(newBranchName)}:`, config.branchPrefixes);
|
|
4708
4457
|
newBranchName = formatBranchName(prefix, newBranchName);
|
|
4709
4458
|
}
|
|
4710
4459
|
if (!isValidBranchName(newBranchName)) {
|
|
@@ -4714,7 +4463,7 @@ var update_default = defineCommand14({
|
|
|
4714
4463
|
const staleUpstream = await getUpstreamRef();
|
|
4715
4464
|
const staleUpstreamHash = staleUpstream ? await getCommitHash(staleUpstream) : null;
|
|
4716
4465
|
if (await branchExists(newBranchName)) {
|
|
4717
|
-
error(`Branch ${
|
|
4466
|
+
error(`Branch ${pc15.bold(newBranchName)} already exists. Choose a different name.`);
|
|
4718
4467
|
process.exit(1);
|
|
4719
4468
|
}
|
|
4720
4469
|
const renameResult = await renameBranch(currentBranch, newBranchName);
|
|
@@ -4722,7 +4471,7 @@ var update_default = defineCommand14({
|
|
|
4722
4471
|
error(`Failed to rename branch: ${renameResult.stderr}`);
|
|
4723
4472
|
process.exit(1);
|
|
4724
4473
|
}
|
|
4725
|
-
success(`Renamed ${
|
|
4474
|
+
success(`Renamed ${pc15.bold(currentBranch)} → ${pc15.bold(newBranchName)}`);
|
|
4726
4475
|
await unsetUpstream();
|
|
4727
4476
|
await fetchRemote(syncSource.remote);
|
|
4728
4477
|
let rebaseResult2;
|
|
@@ -4734,11 +4483,11 @@ var update_default = defineCommand14({
|
|
|
4734
4483
|
}
|
|
4735
4484
|
if (rebaseResult2.exitCode !== 0) {
|
|
4736
4485
|
warn("Rebase encountered conflicts. Resolve them manually, then run:");
|
|
4737
|
-
info(` ${
|
|
4486
|
+
info(` ${pc15.bold("git rebase --continue")}`);
|
|
4738
4487
|
} else {
|
|
4739
|
-
success(`Rebased ${
|
|
4488
|
+
success(`Rebased ${pc15.bold(newBranchName)} onto ${pc15.bold(syncSource.ref)}.`);
|
|
4740
4489
|
}
|
|
4741
|
-
info(`All your changes are preserved. Run ${
|
|
4490
|
+
info(`All your changes are preserved. Run ${pc15.bold("contrib submit")} when ready to create a new PR.`);
|
|
4742
4491
|
return;
|
|
4743
4492
|
}
|
|
4744
4493
|
warn("Discarding local changes...");
|
|
@@ -4751,24 +4500,24 @@ var update_default = defineCommand14({
|
|
|
4751
4500
|
process.exit(1);
|
|
4752
4501
|
}
|
|
4753
4502
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
4754
|
-
success(`Synced ${
|
|
4755
|
-
info(`Deleting stale branch ${
|
|
4503
|
+
success(`Synced ${pc15.bold(baseBranch)} with ${pc15.bold(syncSource.ref)}.`);
|
|
4504
|
+
info(`Deleting stale branch ${pc15.bold(currentBranch)}...`);
|
|
4756
4505
|
await forceDeleteBranch(currentBranch);
|
|
4757
|
-
success(`Deleted ${
|
|
4758
|
-
info(`Run ${
|
|
4506
|
+
success(`Deleted ${pc15.bold(currentBranch)}.`);
|
|
4507
|
+
info(`Run ${pc15.bold("contrib start")} to begin a new feature branch.`);
|
|
4759
4508
|
return;
|
|
4760
4509
|
}
|
|
4761
|
-
info(`Updating ${
|
|
4510
|
+
info(`Updating ${pc15.bold(currentBranch)} with latest ${pc15.bold(baseBranch)}...`);
|
|
4762
4511
|
await fetchRemote(syncSource.remote);
|
|
4763
4512
|
if (!await refExists(syncSource.ref)) {
|
|
4764
|
-
error(`Remote ref ${
|
|
4513
|
+
error(`Remote ref ${pc15.bold(syncSource.ref)} does not exist.`);
|
|
4765
4514
|
error("Run `git fetch --all` and verify your remote configuration.");
|
|
4766
4515
|
process.exit(1);
|
|
4767
4516
|
}
|
|
4768
4517
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
4769
4518
|
const rebaseStrategy = await determineRebaseStrategy(currentBranch, syncSource.ref);
|
|
4770
4519
|
if (rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase) {
|
|
4771
|
-
info(
|
|
4520
|
+
info(pc15.dim(`Using --onto rebase (branch was based on a different ref)`));
|
|
4772
4521
|
}
|
|
4773
4522
|
const rebaseResult = rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase ? await rebaseOnto(syncSource.ref, rebaseStrategy.ontoOldBase) : await rebase(syncSource.ref);
|
|
4774
4523
|
if (rebaseResult.exitCode !== 0) {
|
|
@@ -4797,10 +4546,10 @@ ${content.slice(0, 2000)}
|
|
|
4797
4546
|
if (suggestion) {
|
|
4798
4547
|
spinner.success("AI conflict guidance ready.");
|
|
4799
4548
|
console.log(`
|
|
4800
|
-
${
|
|
4801
|
-
console.log(
|
|
4549
|
+
${pc15.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
|
|
4550
|
+
console.log(pc15.dim("─".repeat(60)));
|
|
4802
4551
|
console.log(suggestion);
|
|
4803
|
-
console.log(
|
|
4552
|
+
console.log(pc15.dim("─".repeat(60)));
|
|
4804
4553
|
console.log();
|
|
4805
4554
|
} else {
|
|
4806
4555
|
spinner.fail("AI could not analyze the conflicts.");
|
|
@@ -4808,22 +4557,22 @@ ${pc17.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
|
|
|
4808
4557
|
}
|
|
4809
4558
|
}
|
|
4810
4559
|
}
|
|
4811
|
-
console.log(
|
|
4560
|
+
console.log(pc15.bold("To resolve:"));
|
|
4812
4561
|
console.log(` 1. Fix conflicts in the affected files`);
|
|
4813
|
-
console.log(` 2. ${
|
|
4814
|
-
console.log(` 3. ${
|
|
4562
|
+
console.log(` 2. ${pc15.cyan("git add <resolved-files>")}`);
|
|
4563
|
+
console.log(` 3. ${pc15.cyan("git rebase --continue")}`);
|
|
4815
4564
|
console.log();
|
|
4816
|
-
console.log(` Or abort: ${
|
|
4565
|
+
console.log(` Or abort: ${pc15.cyan("git rebase --abort")}`);
|
|
4817
4566
|
process.exit(1);
|
|
4818
4567
|
}
|
|
4819
|
-
success(`✅ ${
|
|
4568
|
+
success(`✅ ${pc15.bold(currentBranch)} has been rebased onto latest ${pc15.bold(baseBranch)}`);
|
|
4820
4569
|
}
|
|
4821
4570
|
});
|
|
4822
4571
|
|
|
4823
4572
|
// src/commands/validate.ts
|
|
4824
|
-
import { defineCommand as
|
|
4825
|
-
import
|
|
4826
|
-
var validate_default =
|
|
4573
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
4574
|
+
import pc16 from "picocolors";
|
|
4575
|
+
var validate_default = defineCommand13({
|
|
4827
4576
|
meta: {
|
|
4828
4577
|
name: "validate",
|
|
4829
4578
|
description: "Validate a commit message against the configured convention"
|
|
@@ -4853,7 +4602,7 @@ var validate_default = defineCommand15({
|
|
|
4853
4602
|
}
|
|
4854
4603
|
const errors = getValidationError(convention);
|
|
4855
4604
|
for (const line of errors) {
|
|
4856
|
-
console.error(
|
|
4605
|
+
console.error(pc16.red(` ✗ ${line}`));
|
|
4857
4606
|
}
|
|
4858
4607
|
process.exit(1);
|
|
4859
4608
|
}
|
|
@@ -4861,7 +4610,7 @@ var validate_default = defineCommand15({
|
|
|
4861
4610
|
|
|
4862
4611
|
// src/ui/banner.ts
|
|
4863
4612
|
import figlet from "figlet";
|
|
4864
|
-
import
|
|
4613
|
+
import pc17 from "picocolors";
|
|
4865
4614
|
var LOGO_BIG;
|
|
4866
4615
|
try {
|
|
4867
4616
|
LOGO_BIG = figlet.textSync(`Contribute
|
|
@@ -4883,14 +4632,14 @@ function getAuthor() {
|
|
|
4883
4632
|
}
|
|
4884
4633
|
function showBanner(variant = "small") {
|
|
4885
4634
|
const logo = variant === "big" ? LOGO_BIG : LOGO_SMALL;
|
|
4886
|
-
console.log(
|
|
4635
|
+
console.log(pc17.cyan(`
|
|
4887
4636
|
${logo}`));
|
|
4888
|
-
console.log(` ${
|
|
4637
|
+
console.log(` ${pc17.dim(`v${getVersion()}`)} ${pc17.dim("—")} ${pc17.dim(`Built by ${getAuthor()}`)}`);
|
|
4889
4638
|
if (variant === "big") {
|
|
4890
4639
|
console.log();
|
|
4891
|
-
console.log(` ${
|
|
4892
|
-
console.log(` ${
|
|
4893
|
-
console.log(` ${
|
|
4640
|
+
console.log(` ${pc17.yellow("Star")} ${pc17.cyan("https://github.com/warengonzaga/contribute-now")}`);
|
|
4641
|
+
console.log(` ${pc17.green("Contribute")} ${pc17.cyan("https://github.com/warengonzaga/contribute-now/blob/main/CONTRIBUTING.md")}`);
|
|
4642
|
+
console.log(` ${pc17.magenta("Sponsor")} ${pc17.cyan("https://warengonzaga.com/sponsor")}`);
|
|
4894
4643
|
}
|
|
4895
4644
|
console.log();
|
|
4896
4645
|
}
|
|
@@ -4905,8 +4654,6 @@ if (!isVersion) {
|
|
|
4905
4654
|
"commit",
|
|
4906
4655
|
"update",
|
|
4907
4656
|
"submit",
|
|
4908
|
-
"switch",
|
|
4909
|
-
"save",
|
|
4910
4657
|
"clean",
|
|
4911
4658
|
"status",
|
|
4912
4659
|
"log",
|
|
@@ -4920,7 +4667,7 @@ if (!isVersion) {
|
|
|
4920
4667
|
const useBigBanner = isHelp || !hasSubCommand;
|
|
4921
4668
|
showBanner(useBigBanner ? "big" : "small");
|
|
4922
4669
|
}
|
|
4923
|
-
var main =
|
|
4670
|
+
var main = defineCommand14({
|
|
4924
4671
|
meta: {
|
|
4925
4672
|
name: "contrib",
|
|
4926
4673
|
version: getVersion(),
|
|
@@ -4940,8 +4687,6 @@ var main = defineCommand16({
|
|
|
4940
4687
|
commit: commit_default,
|
|
4941
4688
|
update: update_default,
|
|
4942
4689
|
submit: submit_default,
|
|
4943
|
-
switch: switch_default,
|
|
4944
|
-
save: save_default,
|
|
4945
4690
|
branch: branch_default,
|
|
4946
4691
|
clean: clean_default,
|
|
4947
4692
|
status: status_default,
|