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