preguito 0.2.1 → 0.2.2

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/cli-sea.cjs CHANGED
@@ -36,7 +36,7 @@ let node_os = require("node:os");
36
36
  let node_util = require("node:util");
37
37
  let node_readline_promises = require("node:readline/promises");
38
38
 
39
- //#region node_modules/commander/lib/error.js
39
+ //#region ../../node_modules/commander/lib/error.js
40
40
  var require_error = /* @__PURE__ */ __commonJSMin(((exports) => {
41
41
  /**
42
42
  * CommanderError class
@@ -76,7 +76,7 @@ var require_error = /* @__PURE__ */ __commonJSMin(((exports) => {
76
76
  }));
77
77
 
78
78
  //#endregion
79
- //#region node_modules/commander/lib/argument.js
79
+ //#region ../../node_modules/commander/lib/argument.js
80
80
  var require_argument = /* @__PURE__ */ __commonJSMin(((exports) => {
81
81
  const { InvalidArgumentError } = require_error();
82
82
  var Argument = class {
@@ -201,7 +201,7 @@ var require_argument = /* @__PURE__ */ __commonJSMin(((exports) => {
201
201
  }));
202
202
 
203
203
  //#endregion
204
- //#region node_modules/commander/lib/help.js
204
+ //#region ../../node_modules/commander/lib/help.js
205
205
  var require_help = /* @__PURE__ */ __commonJSMin(((exports) => {
206
206
  const { humanReadableArgName } = require_argument();
207
207
  /**
@@ -663,7 +663,7 @@ var require_help = /* @__PURE__ */ __commonJSMin(((exports) => {
663
663
  }));
664
664
 
665
665
  //#endregion
666
- //#region node_modules/commander/lib/option.js
666
+ //#region ../../node_modules/commander/lib/option.js
667
667
  var require_option = /* @__PURE__ */ __commonJSMin(((exports) => {
668
668
  const { InvalidArgumentError } = require_error();
669
669
  var Option = class {
@@ -955,7 +955,7 @@ var require_option = /* @__PURE__ */ __commonJSMin(((exports) => {
955
955
  }));
956
956
 
957
957
  //#endregion
958
- //#region node_modules/commander/lib/suggestSimilar.js
958
+ //#region ../../node_modules/commander/lib/suggestSimilar.js
959
959
  var require_suggestSimilar = /* @__PURE__ */ __commonJSMin(((exports) => {
960
960
  const maxDistance = 3;
961
961
  function editDistance(a, b) {
@@ -1011,7 +1011,7 @@ var require_suggestSimilar = /* @__PURE__ */ __commonJSMin(((exports) => {
1011
1011
  }));
1012
1012
 
1013
1013
  //#endregion
1014
- //#region node_modules/commander/lib/command.js
1014
+ //#region ../../node_modules/commander/lib/command.js
1015
1015
  var require_command = /* @__PURE__ */ __commonJSMin(((exports) => {
1016
1016
  const EventEmitter = require("node:events").EventEmitter;
1017
1017
  const childProcess = require("node:child_process");
@@ -2912,7 +2912,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2912
2912
  }));
2913
2913
 
2914
2914
  //#endregion
2915
- //#region node_modules/commander/index.js
2915
+ //#region ../../node_modules/commander/index.js
2916
2916
  var require_commander = /* @__PURE__ */ __commonJSMin(((exports) => {
2917
2917
  const { Argument } = require_argument();
2918
2918
  const { Command } = require_command();
@@ -2936,7 +2936,7 @@ var require_commander = /* @__PURE__ */ __commonJSMin(((exports) => {
2936
2936
  }));
2937
2937
 
2938
2938
  //#endregion
2939
- //#region node_modules/commander/esm.mjs
2939
+ //#region ../../node_modules/commander/esm.mjs
2940
2940
  var import_commander = /* @__PURE__ */ __toESM(require_commander(), 1);
2941
2941
  const { program: program$1, createCommand, createArgument, createOption, CommanderError, InvalidArgumentError, InvalidOptionArgumentError, Command, Argument, Option, Help } = import_commander.default;
2942
2942
 
@@ -3259,6 +3259,29 @@ function resolveShortcodes(shortcodesStr, config) {
3259
3259
  return result;
3260
3260
  }
3261
3261
 
3262
+ //#endregion
3263
+ //#region src/utils/validation.ts
3264
+ const GIT_HASH_PATTERN = /^[a-f0-9]{4,40}$/i;
3265
+ const GIT_REF_FORBIDDEN_CHARS = /[\x00-\x1f\x7f ~^:?*[\\]/;
3266
+ const GIT_REF_FORBIDDEN_SEQUENCES = /\.\.|\.lock(\/|$)|@\{|\/\//;
3267
+ function validateHash(hash) {
3268
+ if (!GIT_HASH_PATTERN.test(hash)) throw new PrequitoError(`Invalid git hash: "${hash}". Expected 4-40 hexadecimal characters.`);
3269
+ }
3270
+ function validateRefName(name, type) {
3271
+ if (!name || name.trim() === "") throw new PrequitoError(`${type} name cannot be empty.`);
3272
+ if (name.startsWith("-")) throw new PrequitoError(`Invalid ${type} name: "${name}". Cannot start with '-'.`);
3273
+ if (name.endsWith(".")) throw new PrequitoError(`Invalid ${type} name: "${name}". Cannot end with '.'.`);
3274
+ if (name.endsWith("/")) throw new PrequitoError(`Invalid ${type} name: "${name}". Cannot end with '/'.`);
3275
+ if (GIT_REF_FORBIDDEN_CHARS.test(name)) throw new PrequitoError(`Invalid ${type} name: "${name}". Contains forbidden characters.`);
3276
+ if (GIT_REF_FORBIDDEN_SEQUENCES.test(name)) throw new PrequitoError(`Invalid ${type} name: "${name}". Contains forbidden pattern.`);
3277
+ }
3278
+ function validateBranchName(branch) {
3279
+ validateRefName(branch, "Branch");
3280
+ }
3281
+ function validateTagName(tag) {
3282
+ validateRefName(tag, "Tag");
3283
+ }
3284
+
3262
3285
  //#endregion
3263
3286
  //#region src/git/operations.ts
3264
3287
  const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
@@ -3338,6 +3361,7 @@ async function forcePushLease() {
3338
3361
  return result.stdout + result.stderr;
3339
3362
  }
3340
3363
  async function checkout(branch) {
3364
+ validateBranchName(branch);
3341
3365
  await git(["checkout", branch]);
3342
3366
  }
3343
3367
  async function pull() {
@@ -3345,10 +3369,12 @@ async function pull() {
3345
3369
  return result.stdout + result.stderr;
3346
3370
  }
3347
3371
  async function rebase(branch) {
3372
+ validateBranchName(branch);
3348
3373
  const result = await git(["rebase", branch]);
3349
3374
  return result.stdout + result.stderr;
3350
3375
  }
3351
3376
  async function rebaseInteractiveEdit(hash) {
3377
+ validateHash(hash);
3352
3378
  const sedCmd = `sed -i 's/^pick ${hash.slice(0, 7)}/edit ${hash.slice(0, 7)}/'`;
3353
3379
  const result = await git([
3354
3380
  "rebase",
@@ -3375,7 +3401,8 @@ async function rebaseInteractive(count) {
3375
3401
  `HEAD~${count}`
3376
3402
  ]);
3377
3403
  }
3378
- async function pushUpstream$1(branch) {
3404
+ async function pushUpstream(branch) {
3405
+ if (branch !== void 0) validateBranchName(branch);
3379
3406
  const result = await git([
3380
3407
  "push",
3381
3408
  "--set-upstream",
@@ -3385,6 +3412,7 @@ async function pushUpstream$1(branch) {
3385
3412
  return result.stdout + result.stderr;
3386
3413
  }
3387
3414
  async function commitFixup(hash) {
3415
+ validateHash(hash);
3388
3416
  return (await git([
3389
3417
  "commit",
3390
3418
  "--fixup",
@@ -3399,14 +3427,17 @@ async function resetSoft(count = 1) {
3399
3427
  ])).stdout;
3400
3428
  }
3401
3429
  async function createBranch(branch) {
3430
+ validateBranchName(branch);
3402
3431
  await git([
3403
3432
  "checkout",
3404
3433
  "-b",
3405
3434
  branch
3406
3435
  ]);
3407
3436
  }
3408
- async function stash() {
3409
- return (await git(["stash"])).stdout;
3437
+ async function stash(message) {
3438
+ const args = ["stash"];
3439
+ if (message) args.push("-m", message);
3440
+ return (await git(args)).stdout;
3410
3441
  }
3411
3442
  async function stashPop() {
3412
3443
  return (await git(["stash", "pop"])).stdout;
@@ -3430,6 +3461,7 @@ async function logGrep(keyword, count) {
3430
3461
  return (await git(args)).stdout;
3431
3462
  }
3432
3463
  async function logTag(tag) {
3464
+ validateTagName(tag);
3433
3465
  return (await git([
3434
3466
  "log",
3435
3467
  "--oneline",
@@ -3437,12 +3469,44 @@ async function logTag(tag) {
3437
3469
  ])).stdout;
3438
3470
  }
3439
3471
  async function logTagAll(tag) {
3472
+ validateTagName(tag);
3440
3473
  return (await git([
3441
3474
  "log",
3442
3475
  "--oneline",
3443
3476
  tag
3444
3477
  ])).stdout;
3445
3478
  }
3479
+ async function diff(options = []) {
3480
+ return (await git(["diff", ...options])).stdout;
3481
+ }
3482
+ async function stashList() {
3483
+ return (await git(["stash", "list"])).stdout;
3484
+ }
3485
+
3486
+ //#endregion
3487
+ //#region src/utils/command.ts
3488
+ async function requireGitRepo() {
3489
+ if (!await isGitRepo()) throw new PrequitoError("Not inside a git repository.");
3490
+ }
3491
+ function withErrorHandling(fn) {
3492
+ return async (...args) => {
3493
+ try {
3494
+ await fn(...args);
3495
+ } catch (error) {
3496
+ if (error instanceof PrequitoError) {
3497
+ console.error(`✖ ${error.message}`);
3498
+ process.exit(1);
3499
+ }
3500
+ throw error;
3501
+ }
3502
+ };
3503
+ }
3504
+ function parseCount(value, defaultValue = 1) {
3505
+ if (value === void 0) return defaultValue;
3506
+ const num = parseInt(value, 10);
3507
+ if (isNaN(num) || num <= 0) throw new PrequitoError("Count must be a positive integer.");
3508
+ return num;
3509
+ }
3446
3510
 
3447
3511
  //#endregion
3448
3512
  //#region src/utils/spinner.ts
@@ -3489,23 +3553,12 @@ function spinner(message, options) {
3489
3553
  //#endregion
3490
3554
  //#region src/commands/commit.ts
3491
3555
  function registerCommitCommand(program) {
3492
- program.command("c").alias("commit").description("Templated commit (e.g. guito c 42 f \"msg\" -p)").argument("[args...]", "Card ID, shortcodes, and message").option("-p, --push", "Push after committing").option("-f, --force", "Push with --force-with-lease after committing").option("-d, --dry-run", "Show the generated message without executing").option("-S, --no-stage", "Skip auto-staging (git add -A)").option("-b, --body <text>", "Commit body (optional, multi-line description)").action(async (args, opts) => {
3493
- try {
3494
- await executeCommit(args, opts);
3495
- } catch (error) {
3496
- if (error instanceof PrequitoError) {
3497
- console.error(`\u2716 ${error.message}`);
3498
- process.exit(1);
3499
- }
3500
- throw error;
3501
- }
3502
- });
3556
+ program.command("c").alias("commit").description("Templated commit (e.g. guito c 42 f \"msg\" -p)").argument("[args...]", "Card ID, shortcodes, and message").option("-p, --push", "Push after committing").option("-f, --force", "Push with --force-with-lease after committing").option("-d, --dry-run", "Show the generated message without executing").option("-S, --no-stage", "Skip auto-staging (git add -A)").action(withErrorHandling(async (args, opts) => {
3557
+ await executeCommit(args, opts);
3558
+ }));
3503
3559
  }
3504
3560
  async function executeCommit(args, opts) {
3505
- if (!await isGitRepo()) {
3506
- console.error("✖ Not inside a git repository.");
3507
- process.exit(1);
3508
- }
3561
+ await requireGitRepo();
3509
3562
  const config = await loadConfigOrDefault();
3510
3563
  const { context, message, body } = parsePositionalArgs(args, config, typeof opts.body === "string" ? opts.body : void 0);
3511
3564
  const commitTitle = renderTemplate(config.template, context, message);
@@ -3515,10 +3568,7 @@ async function executeCommit(args, opts) {
3515
3568
  return;
3516
3569
  }
3517
3570
  if (opts.stage !== false) await stageAll();
3518
- if (!await hasStagedChanges()) {
3519
- console.error("✖ No staged changes to commit.");
3520
- process.exit(1);
3521
- }
3571
+ if (!await hasStagedChanges()) throw new PrequitoError("No staged changes to commit.");
3522
3572
  const stopCommit = spinner(`Committing: ${commitTitle}`);
3523
3573
  await commit(commitMessage);
3524
3574
  stopCommit("✔ Committed.");
@@ -3536,34 +3586,11 @@ async function executeCommit(args, opts) {
3536
3586
  //#endregion
3537
3587
  //#region src/commands/amend-push.ts
3538
3588
  function registerAmendPushCommands(program) {
3539
- program.command("ap").description("Amend last commit + force push (git push --force)").action(async () => {
3540
- try {
3541
- await amendAndPush(false);
3542
- } catch (error) {
3543
- if (error instanceof PrequitoError) {
3544
- console.error(`✖ ${error.message}`);
3545
- process.exit(1);
3546
- }
3547
- throw error;
3548
- }
3549
- });
3550
- program.command("apl").description("Amend last commit + safe force push (--force-with-lease)").action(async () => {
3551
- try {
3552
- await amendAndPush(true);
3553
- } catch (error) {
3554
- if (error instanceof PrequitoError) {
3555
- console.error(`✖ ${error.message}`);
3556
- process.exit(1);
3557
- }
3558
- throw error;
3559
- }
3560
- });
3589
+ program.command("ap").description("Amend last commit + force push (git push --force)").action(withErrorHandling(() => amendAndPush(false)));
3590
+ program.command("apl").description("Amend last commit + safe force push (--force-with-lease)").action(withErrorHandling(() => amendAndPush(true)));
3561
3591
  }
3562
3592
  async function amendAndPush(useLease) {
3563
- if (!await isGitRepo()) {
3564
- console.error("✖ Not inside a git repository.");
3565
- process.exit(1);
3566
- }
3593
+ await requireGitRepo();
3567
3594
  const stopStage = spinner("Staging all changes...");
3568
3595
  await stageAll();
3569
3596
  stopStage("✔ Staged.");
@@ -3584,45 +3611,12 @@ async function amendAndPush(useLease) {
3584
3611
  //#endregion
3585
3612
  //#region src/commands/rebase.ts
3586
3613
  function registerRebaseCommands(program) {
3587
- program.command("r <branch>").alias("rebase").description("Rebase on <branch> (checkout, pull, rebase) (e.g. guito r main)").action(async (branch) => {
3588
- try {
3589
- await quickRebase(branch);
3590
- } catch (error) {
3591
- if (error instanceof PrequitoError) {
3592
- console.error(`✖ ${error.message}`);
3593
- process.exit(1);
3594
- }
3595
- throw error;
3596
- }
3597
- });
3598
- program.command("ri <count>").description("Interactive rebase last <count> commits (e.g. guito ri 3)").action(async (count) => {
3599
- try {
3600
- await interactiveRebase(count);
3601
- } catch (error) {
3602
- if (error instanceof PrequitoError) {
3603
- console.error(`✖ ${error.message}`);
3604
- process.exit(1);
3605
- }
3606
- throw error;
3607
- }
3608
- });
3609
- program.command("re <hash>").description("Edit rebase at <hash> (e.g. guito re abc123)").action(async (hash) => {
3610
- try {
3611
- await editRebase(hash);
3612
- } catch (error) {
3613
- if (error instanceof PrequitoError) {
3614
- console.error(`✖ ${error.message}`);
3615
- process.exit(1);
3616
- }
3617
- throw error;
3618
- }
3619
- });
3614
+ program.command("r <branch>").alias("rebase").description("Rebase on <branch> (checkout, pull, rebase) (e.g. guito r main)").action(withErrorHandling(quickRebase));
3615
+ program.command("ri <count>").description("Interactive rebase last <count> commits (e.g. guito ri 3)").action(withErrorHandling(interactiveRebase));
3616
+ program.command("re <hash>").description("Edit rebase at <hash> (e.g. guito re abc123)").action(withErrorHandling(editRebase));
3620
3617
  }
3621
3618
  async function quickRebase(branch) {
3622
- if (!await isGitRepo()) {
3623
- console.error("✖ Not inside a git repository.");
3624
- process.exit(1);
3625
- }
3619
+ await requireGitRepo();
3626
3620
  const currentBranch = await getCurrentBranch();
3627
3621
  console.log(`→ Current branch: ${currentBranch}`);
3628
3622
  let stop = spinner(`Checking out ${branch}...`);
@@ -3639,23 +3633,13 @@ async function quickRebase(branch) {
3639
3633
  stop("✔ Rebase complete.");
3640
3634
  }
3641
3635
  async function interactiveRebase(count) {
3642
- if (!await isGitRepo()) {
3643
- console.error("✖ Not inside a git repository.");
3644
- process.exit(1);
3645
- }
3646
- const num = parseInt(count, 10);
3647
- if (isNaN(num) || num <= 0) {
3648
- console.error("✖ Count must be a positive integer.");
3649
- process.exit(1);
3650
- }
3636
+ await requireGitRepo();
3637
+ const num = parseCount(count);
3651
3638
  console.log(`→ Starting interactive rebase for the last ${num} commit(s)...`);
3652
3639
  await rebaseInteractive(num);
3653
3640
  }
3654
3641
  async function editRebase(hash) {
3655
- if (!await isGitRepo()) {
3656
- console.error("✖ Not inside a git repository.");
3657
- process.exit(1);
3658
- }
3642
+ await requireGitRepo();
3659
3643
  console.log(`→ Starting edit rebase on commit ${hash}...`);
3660
3644
  await rebaseInteractiveEdit(hash);
3661
3645
  console.log("✔ Rebase paused at the target commit. Make your changes, then run:");
@@ -3976,76 +3960,32 @@ function registerConfigCommand(program) {
3976
3960
  //#endregion
3977
3961
  //#region src/commands/push.ts
3978
3962
  function registerPushCommands(program) {
3979
- program.command("p").alias("push").description("Push current branch (git push)").action(async () => {
3980
- try {
3981
- await executePush();
3982
- } catch (error) {
3983
- if (error instanceof PrequitoError) {
3984
- console.error(`✖ ${error.message}`);
3985
- process.exit(1);
3986
- }
3987
- throw error;
3988
- }
3989
- });
3990
- program.command("pu").description("Push + set upstream (git push --set-upstream origin <branch>)").action(async () => {
3991
- try {
3992
- await pushUpstream();
3993
- } catch (error) {
3994
- if (error instanceof PrequitoError) {
3995
- console.error(`✖ ${error.message}`);
3996
- process.exit(1);
3997
- }
3998
- throw error;
3999
- }
4000
- });
4001
- }
4002
- async function executePush() {
4003
- if (!await isGitRepo()) {
4004
- console.error("✖ Not inside a git repository.");
4005
- process.exit(1);
4006
- }
4007
- const stop = spinner("Pushing...");
4008
- await push();
4009
- stop("✔ Pushed.");
4010
- }
4011
- async function pushUpstream() {
4012
- if (!await isGitRepo()) {
4013
- console.error("✖ Not inside a git repository.");
4014
- process.exit(1);
4015
- }
4016
- const branch = await getCurrentBranch();
4017
- const stop = spinner(`Pushing with --set-upstream origin ${branch}...`);
4018
- await pushUpstream$1(branch);
4019
- stop("✔ Pushed.");
3963
+ program.command("p").alias("push").description("Push current branch (git push)").action(withErrorHandling(async () => {
3964
+ await requireGitRepo();
3965
+ const stop = spinner("Pushing...");
3966
+ await push();
3967
+ stop("✔ Pushed.");
3968
+ }));
3969
+ program.command("pu").description("Push + set upstream (git push --set-upstream origin <branch>)").action(withErrorHandling(async () => {
3970
+ await requireGitRepo();
3971
+ const branch = await getCurrentBranch();
3972
+ const stop = spinner(`Pushing with --set-upstream origin ${branch}...`);
3973
+ await pushUpstream(branch);
3974
+ stop("✔ Pushed.");
3975
+ }));
4020
3976
  }
4021
3977
 
4022
3978
  //#endregion
4023
3979
  //#region src/commands/fixup.ts
4024
3980
  function registerFixupCommand(program) {
4025
- program.command("cf <hash>").description("Fixup commit for <hash> (e.g. guito cf abc123 -f)").option("-p, --push", "Push after creating the fixup commit").option("-f, --force", "Push with --force-with-lease after creating").action(async (hash, opts) => {
4026
- try {
4027
- await executeFixup(hash, opts);
4028
- } catch (error) {
4029
- if (error instanceof PrequitoError) {
4030
- console.error(`✖ ${error.message}`);
4031
- process.exit(1);
4032
- }
4033
- throw error;
4034
- }
4035
- });
3981
+ program.command("cf <hash>").description("Fixup commit for <hash> (e.g. guito cf abc123 -f)").option("-p, --push", "Push after creating the fixup commit").option("-f, --force", "Push with --force-with-lease after creating").action(withErrorHandling(executeFixup));
4036
3982
  }
4037
3983
  async function executeFixup(hash, opts) {
4038
- if (!await isGitRepo()) {
4039
- console.error("✖ Not inside a git repository.");
4040
- process.exit(1);
4041
- }
3984
+ await requireGitRepo();
4042
3985
  const stopStage = spinner("Staging all changes...");
4043
3986
  await stageAll();
4044
3987
  stopStage("✔ Staged.");
4045
- if (!await hasStagedChanges()) {
4046
- console.error("✖ No staged changes to commit.");
4047
- process.exit(1);
4048
- }
3988
+ if (!await hasStagedChanges()) throw new PrequitoError("No staged changes to commit.");
4049
3989
  const stopCommit = spinner(`Creating fixup commit for ${hash}...`);
4050
3990
  await commitFixup(hash);
4051
3991
  stopCommit("✔ Fixup commit created.");
@@ -4063,221 +4003,125 @@ async function executeFixup(hash, opts) {
4063
4003
  //#endregion
4064
4004
  //#region src/commands/undo.ts
4065
4005
  function registerUndoCommand(program) {
4066
- program.command("u [count]").alias("undo").description("Undo last N commits, keep changes staged (e.g. guito u 3)").action(async (count) => {
4067
- try {
4068
- await executeUndo(count);
4069
- } catch (error) {
4070
- if (error instanceof PrequitoError) {
4071
- console.error(`✖ ${error.message}`);
4072
- process.exit(1);
4073
- }
4074
- throw error;
4075
- }
4076
- });
4077
- }
4078
- async function executeUndo(count) {
4079
- if (!await isGitRepo()) {
4080
- console.error("✖ Not inside a git repository.");
4081
- process.exit(1);
4082
- }
4083
- const num = count ? parseInt(count, 10) : 1;
4084
- if (isNaN(num) || num <= 0) {
4085
- console.error("✖ Count must be a positive integer.");
4086
- process.exit(1);
4087
- }
4088
- const stop = spinner(`Undoing last ${num} commit(s)...`);
4089
- await resetSoft(num);
4090
- stop(`✔ Undid last ${num} commit(s). Changes are staged.`);
4091
- const st = await status();
4092
- if (st) console.log(st);
4006
+ program.command("u [count]").alias("undo").description("Undo last N commits, keep changes staged (e.g. guito u 3)").action(withErrorHandling(async (count) => {
4007
+ await requireGitRepo();
4008
+ const num = parseCount(count);
4009
+ console.log(`→ Undoing last ${num} commit(s)...`);
4010
+ await resetSoft(num);
4011
+ console.log(`✔ Undid last ${num} commit(s). Changes are staged.`);
4012
+ const st = await status();
4013
+ if (st) console.log(st);
4014
+ }));
4093
4015
  }
4094
4016
 
4095
4017
  //#endregion
4096
4018
  //#region src/commands/status.ts
4097
4019
  function registerStatusCommand(program) {
4098
- program.command("s").alias("status").description("Short status (git status --short)").action(async () => {
4099
- try {
4100
- await executeStatus();
4101
- } catch (error) {
4102
- if (error instanceof PrequitoError) {
4103
- console.error(`✖ ${error.message}`);
4104
- process.exit(1);
4105
- }
4106
- throw error;
4107
- }
4108
- });
4109
- }
4110
- async function executeStatus() {
4111
- if (!await isGitRepo()) {
4112
- console.error("✖ Not inside a git repository.");
4113
- process.exit(1);
4114
- }
4115
- const output = await status();
4116
- if (output) console.log(output.trimEnd());
4117
- else console.log("✨ Nothing to commit, working tree clean.");
4020
+ program.command("s").alias("status").description("Short status (git status --short)").action(withErrorHandling(async () => {
4021
+ await requireGitRepo();
4022
+ const output = await status();
4023
+ if (output) console.log(output.trimEnd());
4024
+ else console.log("✨ Nothing to commit, working tree clean.");
4025
+ }));
4118
4026
  }
4119
4027
 
4120
4028
  //#endregion
4121
4029
  //#region src/commands/switch.ts
4122
4030
  function registerSwitchCommand(program) {
4123
- program.command("sw <branch>").alias("switch").description("Switch branch, -n to create (e.g. guito sw -n feature/x)").option("-n, --new", "Create a new branch").action(async (branch, opts) => {
4124
- try {
4125
- await executeSwitch(branch, opts);
4126
- } catch (error) {
4127
- if (error instanceof PrequitoError) {
4128
- console.error(`✖ ${error.message}`);
4129
- process.exit(1);
4130
- }
4131
- throw error;
4132
- }
4133
- });
4134
- }
4135
- async function executeSwitch(branch, opts) {
4136
- if (!await isGitRepo()) {
4137
- console.error("✖ Not inside a git repository.");
4138
- process.exit(1);
4139
- }
4140
- const stop = spinner(opts.new ? `Creating and switching to ${branch}...` : `Switching to ${branch}...`);
4141
- if (opts.new) await createBranch(branch);
4142
- else await checkout(branch);
4143
- stop(`✔ On branch ${branch}.`);
4031
+ program.command("sw <branch>").alias("switch").description("Switch branch, -n to create (e.g. guito sw -n feature/x)").option("-n, --new", "Create a new branch").action(withErrorHandling(async (branch, opts) => {
4032
+ await requireGitRepo();
4033
+ if (opts.new) {
4034
+ console.log(`→ Creating and switching to ${branch}...`);
4035
+ await createBranch(branch);
4036
+ } else {
4037
+ console.log(`→ Switching to ${branch}...`);
4038
+ await checkout(branch);
4039
+ }
4040
+ console.log(`✔ On branch ${branch}.`);
4041
+ }));
4144
4042
  }
4145
4043
 
4146
4044
  //#endregion
4147
4045
  //#region src/commands/stash.ts
4148
4046
  function registerStashCommands(program) {
4149
- program.command("st").description("Stash all changes (git stash)").action(async () => {
4150
- try {
4151
- await executeStash();
4152
- } catch (error) {
4153
- if (error instanceof PrequitoError) {
4154
- console.error(`✖ ${error.message}`);
4155
- process.exit(1);
4156
- }
4157
- throw error;
4158
- }
4159
- });
4160
- program.command("stp").description("Pop the latest stash (git stash pop)").action(async () => {
4161
- try {
4162
- await executeStashPop();
4163
- } catch (error) {
4164
- if (error instanceof PrequitoError) {
4165
- console.error(`✖ ${error.message}`);
4166
- process.exit(1);
4167
- }
4168
- throw error;
4169
- }
4170
- });
4171
- }
4172
- async function executeStash() {
4173
- if (!await isGitRepo()) {
4174
- console.error("✖ Not inside a git repository.");
4175
- process.exit(1);
4176
- }
4177
- const stop = spinner("Stashing changes...");
4178
- await stash();
4179
- stop("✔ Stashed.");
4180
- }
4181
- async function executeStashPop() {
4182
- if (!await isGitRepo()) {
4183
- console.error("✖ Not inside a git repository.");
4184
- process.exit(1);
4185
- }
4186
- const stop = spinner("Restoring stashed changes...");
4187
- await stashPop();
4188
- stop("✔ Restored.");
4047
+ program.command("st").description("Stash all changes (git stash)").option("-m, --message <message>", "Stash with a descriptive message").action(withErrorHandling(async (opts) => {
4048
+ await requireGitRepo();
4049
+ if (opts.message) console.log(`→ Stashing changes: ${opts.message}...`);
4050
+ else console.log("→ Stashing changes...");
4051
+ await stash(opts.message);
4052
+ console.log("✔ Stashed.");
4053
+ }));
4054
+ program.command("stp").description("Pop the latest stash (git stash pop)").action(withErrorHandling(async () => {
4055
+ await requireGitRepo();
4056
+ console.log("→ Restoring stashed changes...");
4057
+ await stashPop();
4058
+ console.log("✔ Restored.");
4059
+ }));
4060
+ program.command("stl").description("List all stashes (git stash list)").action(withErrorHandling(async () => {
4061
+ await requireGitRepo();
4062
+ const output = await stashList();
4063
+ if (output) console.log(output.trimEnd());
4064
+ else console.log("✨ No stashes found.");
4065
+ }));
4189
4066
  }
4190
4067
 
4191
4068
  //#endregion
4192
4069
  //#region src/commands/log.ts
4193
4070
  function registerLogCommand(program) {
4194
- program.command("l [count]").alias("log").description("Compact log, default last 10 (e.g. guito l 20)").action(async (count) => {
4195
- try {
4196
- await executeLog(count);
4197
- } catch (error) {
4198
- if (error instanceof PrequitoError) {
4199
- console.error(`✖ ${error.message}`);
4200
- process.exit(1);
4201
- }
4202
- throw error;
4203
- }
4204
- });
4205
- }
4206
- async function executeLog(count) {
4207
- if (!await isGitRepo()) {
4208
- console.error("✖ Not inside a git repository.");
4209
- process.exit(1);
4210
- }
4211
- const num = count ? parseInt(count, 10) : 10;
4212
- if (isNaN(num) || num <= 0) {
4213
- console.error("✖ Count must be a positive integer.");
4214
- process.exit(1);
4215
- }
4216
- const output = await logOneline(num);
4217
- if (output) console.log(output.trimEnd());
4218
- else console.log("✨ No commits found.");
4071
+ program.command("l [count]").alias("log").description("Compact log, default last 10 (e.g. guito l 20)").action(withErrorHandling(async (count) => {
4072
+ await requireGitRepo();
4073
+ const num = parseCount(count, 10);
4074
+ const output = await logOneline(num);
4075
+ if (output) console.log(output.trimEnd());
4076
+ else console.log("✨ No commits found.");
4077
+ }));
4219
4078
  }
4220
4079
 
4221
4080
  //#endregion
4222
4081
  //#region src/commands/find.ts
4223
4082
  function registerFindCommands(program) {
4224
- program.command("f <keyword>").alias("find").description("Search commits by message (e.g. guito f \"login\")").option("-n, --number <count>", "Limit number of results").action(async (keyword, opts) => {
4225
- try {
4226
- await executeFind(keyword, opts);
4227
- } catch (error) {
4228
- if (error instanceof PrequitoError) {
4229
- console.error(`✖ ${error.message}`);
4230
- process.exit(1);
4231
- }
4232
- throw error;
4233
- }
4234
- });
4235
- program.command("t <tag>").alias("tag").description("Commits since <tag> (e.g. guito t v1.0.0)").option("-a, --all", "Show all commits reachable from the tag").action(async (tag, opts) => {
4236
- try {
4237
- await executeTag(tag, opts);
4238
- } catch (error) {
4239
- if (error instanceof PrequitoError) {
4240
- console.error(`✖ ${error.message}`);
4241
- process.exit(1);
4242
- }
4243
- throw error;
4244
- }
4245
- });
4246
- }
4247
- async function executeFind(keyword, opts) {
4248
- if (!await isGitRepo()) {
4249
- console.error("✖ Not inside a git repository.");
4250
- process.exit(1);
4251
- }
4252
- const count = opts.number ? parseInt(opts.number, 10) : void 0;
4253
- const stop = spinner(`Searching for "${keyword}"...`);
4254
- const output = await logGrep(keyword, count);
4255
- stop("");
4256
- if (output) console.log(output.trimEnd());
4257
- else console.log(`✨ No commits found matching "${keyword}".`);
4258
- }
4259
- async function executeTag(tag, opts) {
4260
- if (!await isGitRepo()) {
4261
- console.error("✖ Not inside a git repository.");
4262
- process.exit(1);
4263
- }
4264
- if (opts.all) {
4265
- console.log(`🏷️ Commits reachable from ${tag}:`);
4266
- const output = await logTagAll(tag);
4083
+ program.command("f <keyword>").alias("find").description("Search commits by message (e.g. guito f \"login\")").option("-n, --number <count>", "Limit number of results").action(withErrorHandling(async (keyword, opts) => {
4084
+ await requireGitRepo();
4085
+ const count = opts.number ? parseInt(opts.number, 10) : void 0;
4086
+ const output = await logGrep(keyword, count);
4267
4087
  if (output) console.log(output.trimEnd());
4268
- else console.log("✨ No commits found.");
4269
- } else {
4270
- console.log(`🏷️ Commits since ${tag}:`);
4271
- const output = await logTag(tag);
4088
+ else console.log(`✨ No commits found matching "${keyword}".`);
4089
+ }));
4090
+ program.command("t <tag>").alias("tag").description("Commits since <tag> (e.g. guito t v1.0.0)").option("-a, --all", "Show all commits reachable from the tag").action(withErrorHandling(async (tag, opts) => {
4091
+ await requireGitRepo();
4092
+ if (opts.all) {
4093
+ console.log(`🏷️ Commits reachable from ${tag}:`);
4094
+ const output = await logTagAll(tag);
4095
+ if (output) console.log(output.trimEnd());
4096
+ else console.log("✨ No commits found.");
4097
+ } else {
4098
+ console.log(`🏷️ Commits since ${tag}:`);
4099
+ const output = await logTag(tag);
4100
+ if (output) console.log(output.trimEnd());
4101
+ else console.log("✨ No commits since this tag.");
4102
+ }
4103
+ }));
4104
+ }
4105
+
4106
+ //#endregion
4107
+ //#region src/commands/diff.ts
4108
+ function registerDiffCommand(program) {
4109
+ program.command("d").alias("diff").description("Show changes (git diff)").option("-s, --staged", "Show staged changes only").option("--stat", "Show diffstat summary").option("-n, --name-only", "Show only names of changed files").action(withErrorHandling(async (opts) => {
4110
+ await requireGitRepo();
4111
+ const options = [];
4112
+ if (opts.staged) options.push("--staged");
4113
+ if (opts.stat) options.push("--stat");
4114
+ if (opts.nameOnly) options.push("--name-only");
4115
+ const output = await diff(options);
4272
4116
  if (output) console.log(output.trimEnd());
4273
- else console.log("✨ No commits since this tag.");
4274
- }
4117
+ else console.log("✨ No changes.");
4118
+ }));
4275
4119
  }
4276
4120
 
4277
4121
  //#endregion
4278
4122
  //#region src/cli.ts
4279
4123
  const program = new Command();
4280
- program.name("guito").description("preguito - a lazy git CLI with commit templates and shortcuts").version("0.1.0").addHelpText("before", "\n🦥 preguito v0.1.0\n A lazy git CLI with commit templates and shortcuts.\n");
4124
+ program.name("guito").description("preguito - a lazy git CLI with commit templates and shortcuts").version("0.2.2").addHelpText("before", "\n🦥 preguito v0.1.0\n A lazy git CLI with commit templates and shortcuts.\n");
4281
4125
  registerCommitCommand(program);
4282
4126
  registerAmendPushCommands(program);
4283
4127
  registerRebaseCommands(program);
@@ -4291,6 +4135,7 @@ registerSwitchCommand(program);
4291
4135
  registerStashCommands(program);
4292
4136
  registerLogCommand(program);
4293
4137
  registerFindCommands(program);
4138
+ registerDiffCommand(program);
4294
4139
  program.parse();
4295
4140
 
4296
4141
  //#endregion