qkpr 1.0.0 โ†’ 1.0.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/README.md CHANGED
@@ -9,7 +9,7 @@ Create a Pull Request with interactive branch selection
9
9
 
10
10
  quick create pr with interactive branch selection
11
11
 
12
- ![](docs/images/demo.gif)
12
+ ![demo](https://github.com/user-attachments/assets/ec06d376-d7dd-4470-ae61-a7e66605f7b8)
13
13
 
14
14
  ## Installation
15
15
 
@@ -74,6 +74,7 @@ Features:
74
74
  - ๐ŸŒฟ **Branch Name Suggestion**: Suggests appropriate branch names based on changes
75
75
  - ๐ŸŽฏ **Smart Analysis**: Analyzes staged changes (git diff --cached)
76
76
  - โœ… **Interactive**: Choose to commit, copy, or regenerate
77
+ - ๐Ÿš€ **Auto Push**: Option to automatically push the commit to the remote repository
77
78
 
78
79
  #### First Time Setup
79
80
 
package/README.zh-CN.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  ๅฟซ้€Ÿๅˆ›ๅปบๅธฆๆœ‰ไบคไบ’ๅผๅˆ†ๆ”ฏ้€‰ๆ‹ฉ็š„ PR
11
11
 
12
- ![](docs/images/demo.gif)
12
+ ![demo](https://github.com/user-attachments/assets/ec06d376-d7dd-4470-ae61-a7e66605f7b8)
13
13
 
14
14
  ## ๅฎ‰่ฃ…
15
15
 
@@ -74,6 +74,7 @@ qkpr commit
74
74
  - ๐ŸŒฟ **ๅˆ†ๆ”ฏๅ็งฐๅปบ่ฎฎ**๏ผšๅŸบไบŽๅ˜ๆ›ดๅปบ่ฎฎ้€‚ๅฝ“็š„ๅˆ†ๆ”ฏๅ็งฐ
75
75
  - ๐ŸŽฏ **ๆ™บ่ƒฝๅˆ†ๆž**๏ผšๅˆ†ๆžๆš‚ๅญ˜็š„ๅ˜ๆ›ด๏ผˆ`git diff --cached`๏ผ‰
76
76
  - โœ… **ไบคไบ’ๅผ**๏ผš้€‰ๆ‹ฉๆไบคใ€ๅคๅˆถๆˆ–้‡ๆ–ฐ็”Ÿๆˆ
77
+ - ๐Ÿš€ **่‡ชๅŠจๆŽจ้€**๏ผšๅฏ้€‰ๆ‹ฉๅœจๆไบคๅŽ่‡ชๅŠจๅฐ†ไปฃ็ ๆŽจ้€ๅˆฐ่ฟœ็จ‹ไป“ๅบ“
77
78
 
78
79
  #### ้ฆ–ๆฌก่ฎพ็ฝฎ
79
80
 
package/dist/index.mjs CHANGED
@@ -110,7 +110,17 @@ function getBranchLastCommitTime(branchName) {
110
110
  */
111
111
  function getBranchCategory(branchName) {
112
112
  const match = branchName.match(/^([^/]+)\//);
113
- return match ? match[1] : "other";
113
+ if (match) return match[1];
114
+ const lowerBranch = branchName.toLowerCase();
115
+ if ([
116
+ "main",
117
+ "master",
118
+ "develop",
119
+ "dev"
120
+ ].includes(lowerBranch)) return "main";
121
+ if (lowerBranch.startsWith("release")) return "release";
122
+ if (lowerBranch.startsWith("hotfix") || lowerBranch.startsWith("fix")) return "hotfix";
123
+ return "other";
114
124
  }
115
125
  /**
116
126
  * ่Žทๅ–ๅˆ†ๆ”ฏ่ฏฆ็ป†ไฟกๆฏ๏ผˆๅŒ…ๅซๆ—ถ้—ดๅ’Œๅˆ†็ฑป๏ผ‰
@@ -302,8 +312,204 @@ function createPullRequest(sourceBranch, targetBranch, remoteUrl) {
302
312
  }
303
313
 
304
314
  //#endregion
305
- //#region src/config/prompts.ts
306
- const COMMIT_MESSAGE_PROMPT = `้ตๅพช Angular Commit Message ่ง„่Œƒ๏ผŒ็”Ÿๆˆgit commit message,
315
+ //#region src/utils/config.ts
316
+ const CONFIG_DIR = join(homedir(), ".qkpr");
317
+ const CONFIG_FILE = join(CONFIG_DIR, "config.json");
318
+ /**
319
+ * ็กฎไฟ้…็ฝฎ็›ฎๅฝ•ๅญ˜ๅœจ
320
+ */
321
+ function ensureConfigDir() {
322
+ if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
323
+ }
324
+ /**
325
+ * ่ฏปๅ–้…็ฝฎ
326
+ */
327
+ function readConfig() {
328
+ ensureConfigDir();
329
+ if (!existsSync(CONFIG_FILE)) return {};
330
+ try {
331
+ const content = readFileSync(CONFIG_FILE, "utf-8");
332
+ return JSON.parse(content);
333
+ } catch {
334
+ return {};
335
+ }
336
+ }
337
+ /**
338
+ * ๅ†™ๅ…ฅ้…็ฝฎ
339
+ */
340
+ function writeConfig(config) {
341
+ ensureConfigDir();
342
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
343
+ }
344
+ /**
345
+ * ่Žทๅ– Gemini API Key
346
+ * ไผ˜ๅ…ˆ็บง๏ผš้…็ฝฎๆ–‡ไปถ > ็Žฏๅขƒๅ˜้‡ QUICK_PR_GEMINI_API_KEY > GEMINI_API_KEY
347
+ *
348
+ * You can set the API key in either:
349
+ * 1. Config file (~/.qkpr/config.json) via `qkpr config` command
350
+ * 2. Environment variable: export QKPR_GEMINI_API_KEY=your_api_key
351
+ * 3. Environment variable (legacy): export GEMINI_API_KEY=your_api_key
352
+ */
353
+ function getGeminiApiKey() {
354
+ return readConfig().geminiApiKey || process.env.QUICK_PR_GEMINI_API_KEY || process.env.GEMINI_API_KEY;
355
+ }
356
+ /**
357
+ * ่ฎพ็ฝฎ Gemini API Key
358
+ */
359
+ function setGeminiApiKey(apiKey) {
360
+ const config = readConfig();
361
+ config.geminiApiKey = apiKey;
362
+ writeConfig(config);
363
+ }
364
+ function getGeminiModel() {
365
+ return readConfig().geminiModel || process.env.QUICK_PR_GEMINI_MODEL || process.env.GEMINI_MODEL || "gemini-2.0-flash";
366
+ }
367
+ function setGeminiModel(model) {
368
+ const config = readConfig();
369
+ config.geminiModel = model;
370
+ writeConfig(config);
371
+ }
372
+ /**
373
+ * ่Žทๅ–ๅทฒๅ›บๅฎš็š„ๅˆ†ๆ”ฏๅˆ—่กจ
374
+ */
375
+ function getPinnedBranches() {
376
+ return readConfig().pinnedBranches || [];
377
+ }
378
+ /**
379
+ * ๆทปๅŠ ๅ›บๅฎšๅˆ†ๆ”ฏ
380
+ */
381
+ function addPinnedBranch(branch) {
382
+ const config = readConfig();
383
+ const pinnedBranches = config.pinnedBranches || [];
384
+ if (!pinnedBranches.includes(branch)) {
385
+ pinnedBranches.push(branch);
386
+ config.pinnedBranches = pinnedBranches;
387
+ writeConfig(config);
388
+ }
389
+ }
390
+ /**
391
+ * ็งป้™คๅ›บๅฎšๅˆ†ๆ”ฏ
392
+ */
393
+ function removePinnedBranch(branch) {
394
+ const config = readConfig();
395
+ const pinnedBranches = config.pinnedBranches || [];
396
+ const index = pinnedBranches.indexOf(branch);
397
+ if (index > -1) {
398
+ pinnedBranches.splice(index, 1);
399
+ config.pinnedBranches = pinnedBranches;
400
+ writeConfig(config);
401
+ }
402
+ }
403
+ /**
404
+ * ่Žทๅ–่ฏญ่จ€
405
+ */
406
+ function getPromptLanguage() {
407
+ return readConfig().promptLanguage || "zh";
408
+ }
409
+ /**
410
+ * ่ฎพ็ฝฎ่ฏญ่จ€
411
+ */
412
+ function setPromptLanguage(language) {
413
+ const config = readConfig();
414
+ config.promptLanguage = language;
415
+ writeConfig(config);
416
+ }
417
+ /**
418
+ * ่Žทๅ–่‡ชๅฎšไน‰ Commit Message Prompt
419
+ */
420
+ function getCustomCommitMessagePrompt() {
421
+ return readConfig().customCommitMessagePrompt;
422
+ }
423
+ /**
424
+ * ่ฎพ็ฝฎ่‡ชๅฎšไน‰ Commit Message Prompt
425
+ */
426
+ function setCustomCommitMessagePrompt(prompt) {
427
+ const config = readConfig();
428
+ config.customCommitMessagePrompt = prompt;
429
+ writeConfig(config);
430
+ }
431
+ /**
432
+ * ่Žทๅ–่‡ชๅฎšไน‰ Branch Name Prompt
433
+ */
434
+ function getCustomBranchNamePrompt() {
435
+ return readConfig().customBranchNamePrompt;
436
+ }
437
+ /**
438
+ * ่ฎพ็ฝฎ่‡ชๅฎšไน‰ Branch Name Prompt
439
+ */
440
+ function setCustomBranchNamePrompt(prompt) {
441
+ const config = readConfig();
442
+ config.customBranchNamePrompt = prompt;
443
+ writeConfig(config);
444
+ }
445
+
446
+ //#endregion
447
+ //#region src/config/locales.ts
448
+ const locales = {
449
+ en: {
450
+ COMMIT_MESSAGE_PROMPT: `Follow the Angular Commit Message Specification to generate a git commit message.
451
+
452
+ Default to Chinese if the user does not specify a language.
453
+ Use plaintext syntax as much as possible, do not use markdown syntax.
454
+ The generated content should not contain emojis.
455
+ Format:
456
+ <type>(<scope>): <subject>
457
+
458
+ - Detailed description 1
459
+ - Detailed description 2
460
+ - Detailed description 3
461
+
462
+ Where subject is required, and detailed descriptions are optional supplementary explanations.
463
+
464
+ type:
465
+ [
466
+ 'feat', // New feature
467
+ 'fix', // Bug fix
468
+ 'docs', // Documentation changes
469
+ 'style', // Code style
470
+ 'refactor', // Refactoring
471
+ 'perf', // Performance optimization
472
+ 'test', // Adding tests
473
+ 'chore', // Changes to the build process or auxiliary tools
474
+ 'revert', // Revert
475
+ 'build', // Build
476
+ ],
477
+
478
+ scope: Optional, indicates the scope of impact (e.g., module name)
479
+ subject: A concise description of the commit.
480
+ Detailed description: Use a list to briefly describe the main changes. Each list item should be short and clear, limited to 3-5 items.
481
+
482
+ Important rules:
483
+ 1. Do not generate body and footer sections.
484
+ 2. Only generate the subject and a list-style detailed description.
485
+ 3. List items should be concise, each on a single line.
486
+ 4. Do not add extra explanations or descriptive text.
487
+
488
+ Example:
489
+ feat(auth): Add WeChat login functionality
490
+
491
+ - Support WeChat QR code login
492
+ - Support binding WeChat accounts
493
+ - Add WeChat user information synchronization
494
+
495
+ Do not return any content other than the commit message.
496
+
497
+ Please generate a commit message based on the git diff.`,
498
+ BRANCH_NAME_PROMPT: `Please generate a branch name based on the git diff, following these conventions:
499
+
500
+ feat/ New feature development (e.g., feat/user-authentication)
501
+ fix/ Bug fix (e.g., fix/login-error)
502
+ hotfix/ Urgent production issue fix (e.g., hotfix/payment-failure)
503
+ refactor/ Code refactoring (e.g., refactor/user-service)
504
+ docs/ Documentation update (e.g., docs/api-reference)
505
+ perf/ Performance optimization (e.g., perf/image-loading)
506
+ test/ Test related (e.g., test/user-profile)
507
+ chore/ Build/configuration change (e.g., chore/webpack-update)
508
+
509
+ Output format: Directly output the branch name, with no other content.`
510
+ },
511
+ zh: {
512
+ COMMIT_MESSAGE_PROMPT: `้ตๅพช Angular Commit Message ่ง„่Œƒ๏ผŒ็”Ÿๆˆgit commit message,
307
513
 
308
514
  ๅฆ‚ๆžœ็”จๆˆทๆฒกๆœ‰ๆŒ‡็คบ๏ผŒ้ป˜่ฎคไธบไธญๆ–‡
309
515
  ๅฐฝ้‡ไฝฟ็”จplaintext็š„่ฏญๆณ•๏ผŒไธ่ฆไฝฟ็”จmd็š„่ฏญๆณ•
@@ -350,8 +556,8 @@ feat(auth): ๆทปๅŠ ๅพฎไฟก็™ปๅฝ•ๅŠŸ่ƒฝ
350
556
 
351
557
  ้™คไบ†commit msg๏ผŒๅ…ถไป–ไธ้œ€่ฆ่ฟ”ๅ›žไปปไฝ•ๅ†…ๅฎนใ€‚
352
558
 
353
- ่ฏทไฝ ๆ นๆฎ git diff ็”Ÿๆˆ commit messageใ€‚`;
354
- const BRANCH_NAME_PROMPT = `่ฏทๆ นๆฎ git diff ็”Ÿๆˆๅˆ†ๆ”ฏๅ๏ผŒ้ตๅพชไปฅไธ‹่ง„่Œƒ๏ผš
559
+ ่ฏทไฝ ๆ นๆฎ git diff ็”Ÿๆˆ commit messageใ€‚`,
560
+ BRANCH_NAME_PROMPT: `่ฏทๆ นๆฎ git diff ็”Ÿๆˆๅˆ†ๆ”ฏๅ๏ผŒ้ตๅพชไปฅไธ‹่ง„่Œƒ๏ผš
355
561
 
356
562
  feat/ ๆ–ฐๅŠŸ่ƒฝๅผ€ๅ‘ feat/user-authentication
357
563
  fix/ Bugไฟฎๅค fix/login-error
@@ -362,7 +568,16 @@ perf/ ๆ€ง่ƒฝไผ˜ๅŒ– perf/image-loading
362
568
  test/ ๆต‹่ฏ•็›ธๅ…ณ test/user-profile
363
569
  chore/ ๆž„ๅปบ/้…็ฝฎๅ˜ๆ›ด chore/webpack-update
364
570
 
365
- ่พ“ๅ‡บๆ ผๅผ๏ผš็›ดๆŽฅ่พ“ๅ‡บๅˆ†ๆ”ฏๅ๏ผŒๆ— ้œ€ๅ…ถไป–ๅ†…ๅฎน`;
571
+ ่พ“ๅ‡บๆ ผๅผ๏ผš็›ดๆŽฅ่พ“ๅ‡บๅˆ†ๆ”ฏๅ๏ผŒๆ— ้œ€ๅ…ถไป–ๅ†…ๅฎน
572
+ `
573
+ }
574
+ };
575
+
576
+ //#endregion
577
+ //#region src/config/prompts.ts
578
+ const lang = getPromptLanguage();
579
+ const COMMIT_MESSAGE_PROMPT = getCustomCommitMessagePrompt() || locales[lang].COMMIT_MESSAGE_PROMPT;
580
+ const BRANCH_NAME_PROMPT = getCustomBranchNamePrompt() || locales[lang].BRANCH_NAME_PROMPT;
366
581
 
367
582
  //#endregion
368
583
  //#region src/services/commit.ts
@@ -512,96 +727,6 @@ function displayBranchName(branchName) {
512
727
  console.log(green(` ${branchName}\n`));
513
728
  }
514
729
 
515
- //#endregion
516
- //#region src/utils/config.ts
517
- const CONFIG_DIR = join(homedir(), ".qkpr");
518
- const CONFIG_FILE = join(CONFIG_DIR, "config.json");
519
- /**
520
- * ็กฎไฟ้…็ฝฎ็›ฎๅฝ•ๅญ˜ๅœจ
521
- */
522
- function ensureConfigDir() {
523
- if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
524
- }
525
- /**
526
- * ่ฏปๅ–้…็ฝฎ
527
- */
528
- function readConfig() {
529
- ensureConfigDir();
530
- if (!existsSync(CONFIG_FILE)) return {};
531
- try {
532
- const content = readFileSync(CONFIG_FILE, "utf-8");
533
- return JSON.parse(content);
534
- } catch {
535
- return {};
536
- }
537
- }
538
- /**
539
- * ๅ†™ๅ…ฅ้…็ฝฎ
540
- */
541
- function writeConfig(config) {
542
- ensureConfigDir();
543
- writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
544
- }
545
- /**
546
- * ่Žทๅ– Gemini API Key
547
- * ไผ˜ๅ…ˆ็บง๏ผš้…็ฝฎๆ–‡ไปถ > ็Žฏๅขƒๅ˜้‡ QUICK_PR_GEMINI_API_KEY > GEMINI_API_KEY
548
- *
549
- * You can set the API key in either:
550
- * 1. Config file (~/.qkpr/config.json) via `qkpr config` command
551
- * 2. Environment variable: export QKPR_GEMINI_API_KEY=your_api_key
552
- * 3. Environment variable (legacy): export GEMINI_API_KEY=your_api_key
553
- */
554
- function getGeminiApiKey() {
555
- return readConfig().geminiApiKey || process.env.QUICK_PR_GEMINI_API_KEY || process.env.GEMINI_API_KEY;
556
- }
557
- /**
558
- * ่ฎพ็ฝฎ Gemini API Key
559
- */
560
- function setGeminiApiKey(apiKey) {
561
- const config = readConfig();
562
- config.geminiApiKey = apiKey;
563
- writeConfig(config);
564
- }
565
- function getGeminiModel() {
566
- return readConfig().geminiModel || process.env.QUICK_PR_GEMINI_MODEL || process.env.GEMINI_MODEL || "gemini-2.0-flash";
567
- }
568
- function setGeminiModel(model) {
569
- const config = readConfig();
570
- config.geminiModel = model;
571
- writeConfig(config);
572
- }
573
- /**
574
- * ่Žทๅ–ๅทฒๅ›บๅฎš็š„ๅˆ†ๆ”ฏๅˆ—่กจ
575
- */
576
- function getPinnedBranches() {
577
- return readConfig().pinnedBranches || [];
578
- }
579
- /**
580
- * ๆทปๅŠ ๅ›บๅฎšๅˆ†ๆ”ฏ
581
- */
582
- function addPinnedBranch(branch) {
583
- const config = readConfig();
584
- const pinnedBranches = config.pinnedBranches || [];
585
- if (!pinnedBranches.includes(branch)) {
586
- pinnedBranches.push(branch);
587
- config.pinnedBranches = pinnedBranches;
588
- writeConfig(config);
589
- }
590
- }
591
- /**
592
- * ็งป้™คๅ›บๅฎšๅˆ†ๆ”ฏ
593
- */
594
- function removePinnedBranch(branch) {
595
- const config = readConfig();
596
- const pinnedBranches = config.pinnedBranches || [];
597
- const index = pinnedBranches.indexOf(branch);
598
- if (index > -1) {
599
- pinnedBranches.splice(index, 1);
600
- config.pinnedBranches = pinnedBranches;
601
- writeConfig(config);
602
- }
603
- }
604
-
605
730
  //#endregion
606
731
  //#region src/utils/commit-cli.ts
607
732
  inquirer.registerPrompt("autocomplete", inquirerAutoComplete);
@@ -653,6 +778,18 @@ async function promptSaveApiKey() {
653
778
  return shouldSave;
654
779
  }
655
780
  /**
781
+ * ่ฏข้—ฎๆ˜ฏๅฆ push
782
+ */
783
+ async function promptPush() {
784
+ const { shouldPush } = await inquirer.prompt([{
785
+ type: "confirm",
786
+ name: "shouldPush",
787
+ message: "Push the changes to the remote repository?",
788
+ default: true
789
+ }]);
790
+ return shouldPush;
791
+ }
792
+ /**
656
793
  * ่ฏข้—ฎ็”จๆˆท้€‰ๆ‹ฉๆจกๅž‹
657
794
  */
658
795
  async function promptModelSelection(apiKey) {
@@ -749,7 +886,13 @@ async function handleCommitCommand() {
749
886
  console.log(cyan("โ•‘ ๐Ÿค– AI Commit Message Generator โ•‘"));
750
887
  console.log(cyan("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n"));
751
888
  const model = getGeminiModel();
752
- console.log(dim(`Using model: ${model}\n`));
889
+ console.log(dim(`Using model: ${model}`));
890
+ if (getCustomCommitMessagePrompt()) console.log(dim("Using custom commit message prompt"));
891
+ else {
892
+ const promptLanguage = getPromptLanguage();
893
+ console.log(dim(`Using ${promptLanguage} commit message prompt`));
894
+ }
895
+ console.log("");
753
896
  if (!hasStagedChanges()) {
754
897
  console.log(yellow("โš ๏ธ No staged changes found."));
755
898
  console.log(dim("Please stage your changes using: git add <files>\n"));
@@ -787,8 +930,15 @@ async function handleCommitCommand() {
787
930
  }
788
931
  switch (action) {
789
932
  case "commit":
790
- if (performCommit(commitMessage)) console.log(green("\nโœ… Commit successful!\n"));
791
- else console.log(red("\nโŒ Commit failed\n"));
933
+ if (performCommit(commitMessage)) {
934
+ console.log(green("\nโœ… Commit successful!\n"));
935
+ if (await promptPush()) {
936
+ const branchName = execSync("git branch --show-current").toString().trim();
937
+ if (branchName) {
938
+ if (!pushBranchToRemote(branchName)) console.log(red("โŒ Failed to push changes"));
939
+ } else console.log(red("โŒ Could not determine the current branch name."));
940
+ }
941
+ } else console.log(red("\nโŒ Commit failed\n"));
792
942
  break;
793
943
  case "copy":
794
944
  if (copyToClipboard(commitMessage)) console.log(green("\nโœ… Commit message copied to clipboard\n"));
@@ -907,7 +1057,13 @@ async function handleBranchCommand() {
907
1057
  console.log(cyan("โ•‘ ๐ŸŒฟ AI Branch Name Generator โ•‘"));
908
1058
  console.log(cyan("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n"));
909
1059
  const model = getGeminiModel();
910
- console.log(dim(`Using model: ${model}\n`));
1060
+ console.log(dim(`Using model: ${model}`));
1061
+ if (getCustomBranchNamePrompt()) console.log(dim("Using custom branch name prompt"));
1062
+ else {
1063
+ const promptLanguage = getPromptLanguage();
1064
+ console.log(dim(`Using ${promptLanguage} branch name prompt`));
1065
+ }
1066
+ console.log("");
911
1067
  if (!hasStagedChanges()) {
912
1068
  console.log(yellow("โš ๏ธ No staged changes found."));
913
1069
  console.log(dim("Please stage your changes using: git add <files>\n"));
@@ -953,6 +1109,125 @@ async function handleBranchCommand() {
953
1109
  console.log(red(`\nโŒ Error: ${error.message}\n`));
954
1110
  }
955
1111
  }
1112
+ /**
1113
+ * ้…็ฝฎ่ฏญ่จ€
1114
+ */
1115
+ async function handleConfigPromptLangCommand() {
1116
+ console.log(cyan("\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—"));
1117
+ console.log(cyan("โ•‘ ๐ŸŒ Prompt Language Configuration โ•‘"));
1118
+ console.log(cyan("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n"));
1119
+ const currentLanguage = getPromptLanguage();
1120
+ console.log(dim(`Current prompt language: ${currentLanguage}\n`));
1121
+ const { language } = await inquirer.prompt([{
1122
+ type: "list",
1123
+ name: "language",
1124
+ message: "Select a language for the prompts:",
1125
+ choices: [
1126
+ {
1127
+ name: "๐Ÿ‡จ๐Ÿ‡ณ Chinese",
1128
+ value: "zh"
1129
+ },
1130
+ {
1131
+ name: "๐Ÿ‡บ๐Ÿ‡ธ English",
1132
+ value: "en"
1133
+ },
1134
+ new inquirer.Separator(),
1135
+ {
1136
+ name: "โ†ฉ๏ธ Go back",
1137
+ value: "back"
1138
+ }
1139
+ ],
1140
+ default: currentLanguage
1141
+ }]);
1142
+ if (language === "back") {
1143
+ console.log(yellow("\nโš ๏ธ Cancelled\n"));
1144
+ return;
1145
+ }
1146
+ setPromptLanguage(language);
1147
+ console.log(green(`\nโœ… Prompt language configured successfully: ${language}\n`));
1148
+ }
1149
+ /**
1150
+ * ้…็ฝฎ่‡ชๅฎšไน‰ Prompts
1151
+ */
1152
+ async function handleConfigPromptsCommand() {
1153
+ console.log(cyan("\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—"));
1154
+ console.log(cyan("โ•‘ ๐Ÿ“ Custom Prompts Configuration โ•‘"));
1155
+ console.log(cyan("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n"));
1156
+ const currentCommitPrompt = getCustomCommitMessagePrompt();
1157
+ const currentBranchPrompt = getCustomBranchNamePrompt();
1158
+ console.log(dim("Current custom commit message prompt:"));
1159
+ console.log(currentCommitPrompt ? yellow(currentCommitPrompt) : dim("Not set"));
1160
+ console.log(dim("\nCurrent custom branch name prompt:"));
1161
+ console.log(currentBranchPrompt ? yellow(currentBranchPrompt) : dim("Not set"));
1162
+ const { action } = await inquirer.prompt([{
1163
+ type: "list",
1164
+ name: "action",
1165
+ message: "What would you like to do?",
1166
+ choices: [
1167
+ {
1168
+ name: "โœ๏ธ Set custom commit message prompt",
1169
+ value: "commit"
1170
+ },
1171
+ {
1172
+ name: "โœ๏ธ Set custom branch name prompt",
1173
+ value: "branch"
1174
+ },
1175
+ new inquirer.Separator(),
1176
+ {
1177
+ name: "๐Ÿ—‘๏ธ Clear custom commit message prompt",
1178
+ value: "clear-commit"
1179
+ },
1180
+ {
1181
+ name: "๐Ÿ—‘๏ธ Clear custom branch name prompt",
1182
+ value: "clear-branch"
1183
+ },
1184
+ new inquirer.Separator(),
1185
+ {
1186
+ name: "โ†ฉ๏ธ Go back",
1187
+ value: "back"
1188
+ }
1189
+ ]
1190
+ }]);
1191
+ switch (action) {
1192
+ case "commit": {
1193
+ const { prompt } = await inquirer.prompt([{
1194
+ type: "editor",
1195
+ name: "prompt",
1196
+ message: "Enter your custom commit message prompt:",
1197
+ default: currentCommitPrompt
1198
+ }]);
1199
+ if (prompt) {
1200
+ setCustomCommitMessagePrompt(prompt);
1201
+ console.log(green("\nโœ… Custom commit message prompt saved!\n"));
1202
+ } else console.log(yellow("\nโš ๏ธ Cancelled\n"));
1203
+ break;
1204
+ }
1205
+ case "branch": {
1206
+ const { prompt } = await inquirer.prompt([{
1207
+ type: "editor",
1208
+ name: "prompt",
1209
+ message: "Enter your custom branch name prompt:",
1210
+ default: currentBranchPrompt
1211
+ }]);
1212
+ if (prompt) {
1213
+ setCustomBranchNamePrompt(prompt);
1214
+ console.log(green("\nโœ… Custom branch name prompt saved!\n"));
1215
+ } else console.log(yellow("\nโš ๏ธ Cancelled\n"));
1216
+ break;
1217
+ }
1218
+ case "clear-commit":
1219
+ setCustomCommitMessagePrompt("");
1220
+ console.log(green("\nโœ… Custom commit message prompt cleared!\n"));
1221
+ break;
1222
+ case "clear-branch":
1223
+ setCustomBranchNamePrompt("");
1224
+ console.log(green("\nโœ… Custom branch name prompt cleared!\n"));
1225
+ break;
1226
+ case "back":
1227
+ console.log(yellow("\nโš ๏ธ Cancelled\n"));
1228
+ break;
1229
+ }
1230
+ }
956
1231
 
957
1232
  //#endregion
958
1233
  //#region src/utils/pr-cli.ts
@@ -986,6 +1261,8 @@ async function promptBranchSelection(branches, options) {
986
1261
  branches$1.sort((a, b) => b.lastCommitTime - a.lastCommitTime);
987
1262
  });
988
1263
  const categoryOrder = [
1264
+ "main",
1265
+ "release",
989
1266
  "feat",
990
1267
  "fix",
991
1268
  "merge",
@@ -1021,7 +1298,11 @@ async function promptBranchSelection(branches, options) {
1021
1298
  sortedCategories.forEach((category) => {
1022
1299
  const branches$1 = categorizedBranches.get(category);
1023
1300
  if (branches$1.length > 0) {
1024
- const categoryLabel = category === "other" ? "Other Branches" : `${category}/*`;
1301
+ let categoryLabel;
1302
+ if (category === "other") categoryLabel = "Other Branches";
1303
+ else if (category === "main") categoryLabel = "Main Branches";
1304
+ else if (category === "release") categoryLabel = "Release Branches";
1305
+ else categoryLabel = `${category}/*`;
1025
1306
  choices.push(new inquirer.Separator(cyan(`โ”โ”โ”โ”โ”โ”โ”โ” ${categoryLabel} โ”โ”โ”โ”โ”โ”โ”โ”`)));
1026
1307
  branches$1.forEach((branch) => {
1027
1308
  choices.push({
@@ -1233,14 +1514,22 @@ async function checkForUpdates(packageName$1, currentVersion) {
1233
1514
  "pipe",
1234
1515
  "ignore"
1235
1516
  ],
1236
- timeout: 3e3
1517
+ timeout: 5e3
1237
1518
  }).trim();
1519
+ const hasUpdate = compareVersions(currentVersion, latestVersion) < 0;
1520
+ if (process.env.DEBUG_QKPR_UPDATE) {
1521
+ console.log(`[DEBUG] Package: ${packageName$1}`);
1522
+ console.log(`[DEBUG] Current: ${currentVersion}`);
1523
+ console.log(`[DEBUG] Latest: ${latestVersion}`);
1524
+ console.log(`[DEBUG] Has update: ${hasUpdate}`);
1525
+ }
1238
1526
  return {
1239
- hasUpdate: compareVersions(currentVersion, latestVersion) < 0,
1527
+ hasUpdate,
1240
1528
  currentVersion,
1241
1529
  latestVersion
1242
1530
  };
1243
- } catch {
1531
+ } catch (error) {
1532
+ if (process.env.DEBUG_QKPR_UPDATE) console.log(`[DEBUG] Update check failed: ${error instanceof Error ? error.message : "Unknown error"}`);
1244
1533
  return {
1245
1534
  hasUpdate: false,
1246
1535
  currentVersion,
@@ -1366,19 +1655,69 @@ async function showPinnedBranchesMenu() {
1366
1655
  }
1367
1656
  }
1368
1657
  }
1369
- /**
1370
- * Show main menu for feature selection
1371
- */
1658
+ async function showSettingsMenu() {
1659
+ const inquirer$1 = (await import("inquirer")).default;
1660
+ while (true) {
1661
+ const { setting } = await inquirer$1.prompt([{
1662
+ type: "list",
1663
+ name: "setting",
1664
+ message: "Settings",
1665
+ choices: [
1666
+ {
1667
+ name: "1. โš™๏ธ Configure API Key",
1668
+ value: "config",
1669
+ key: "1"
1670
+ },
1671
+ {
1672
+ name: "2. ๐Ÿ”ง Configure Model",
1673
+ value: "config:model",
1674
+ key: "2"
1675
+ },
1676
+ {
1677
+ name: "3. ๐ŸŒ Configure Prompt Language",
1678
+ value: "config:prompt-lang",
1679
+ key: "3"
1680
+ },
1681
+ {
1682
+ name: "4. ๐Ÿ“ Configure Custom Prompts",
1683
+ value: "config:prompts",
1684
+ key: "4"
1685
+ },
1686
+ new inquirer$1.Separator(),
1687
+ {
1688
+ name: "โ†ฉ๏ธ Back to main menu",
1689
+ value: "back"
1690
+ }
1691
+ ]
1692
+ }]);
1693
+ switch (setting) {
1694
+ case "config":
1695
+ await handleConfigCommand();
1696
+ break;
1697
+ case "config:model":
1698
+ await handleConfigModelCommand();
1699
+ break;
1700
+ case "config:prompt-lang":
1701
+ await handleConfigPromptLangCommand();
1702
+ break;
1703
+ case "config:prompts":
1704
+ await handleConfigPromptsCommand();
1705
+ break;
1706
+ case "back": return;
1707
+ }
1708
+ }
1709
+ }
1372
1710
  async function showMainMenu() {
1711
+ await checkAndNotifyUpdate(packageName, version);
1373
1712
  console.log(bold(cyan("\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—")));
1374
- console.log(bold(cyan("โ•‘ ๐Ÿš€ Quick PR Tool โ•‘")));
1713
+ console.log(bold(cyan("โ•‘ ๐Ÿš€ Quick PR Tool โ•‘")));
1375
1714
  console.log(bold(cyan("โ•‘ โ•‘")));
1376
- console.log(bold(cyan("โ•‘ Your All-in-One Git Workflow Assistant โ•‘")));
1715
+ console.log(bold(cyan("โ•‘ Your All-in-One Git Workflow Assistant โ•‘")));
1377
1716
  console.log(bold(cyan("โ•‘ โ•‘")));
1378
- console.log(bold(cyan("โ•‘ Author: KazooTTT โ•‘")));
1379
- console.log(bold(cyan("โ•‘ GitHub: https://github.com/KazooTTT/qkpr โ•‘")));
1717
+ console.log(bold(cyan("โ•‘ Author: KazooTTT โ•‘")));
1718
+ console.log(bold(cyan("โ•‘ GitHub: https://github.com/KazooTTT/qkpr โ•‘")));
1380
1719
  console.log(bold(cyan("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")));
1381
- console.log(` Version: ${version}\n`);
1720
+ console.log(` Version: ${version}\n`);
1382
1721
  const inquirer$1 = (await import("inquirer")).default;
1383
1722
  const { feature } = await inquirer$1.prompt([{
1384
1723
  type: "list",
@@ -1401,20 +1740,15 @@ async function showMainMenu() {
1401
1740
  key: "3"
1402
1741
  },
1403
1742
  {
1404
- name: "4. โš™๏ธ Configure API Key",
1405
- value: "config",
1743
+ name: "4. ๐Ÿ“Œ Manage Pinned Branches",
1744
+ value: "pinned",
1406
1745
  key: "4"
1407
1746
  },
1408
1747
  {
1409
- name: "5. ๐Ÿ”ง Configure Model",
1410
- value: "config:model",
1748
+ name: "5. โš™๏ธ Settings",
1749
+ value: "settings",
1411
1750
  key: "5"
1412
1751
  },
1413
- {
1414
- name: "6. ๐Ÿ“Œ Manage Pinned Branches",
1415
- value: "pinned",
1416
- key: "6"
1417
- },
1418
1752
  new inquirer$1.Separator(),
1419
1753
  {
1420
1754
  name: "โŒ Exit",
@@ -1425,33 +1759,24 @@ async function showMainMenu() {
1425
1759
  switch (feature) {
1426
1760
  case "pr":
1427
1761
  await handlePRCommand();
1428
- await checkAndNotifyUpdate(packageName, version);
1429
1762
  await showMainMenu();
1430
1763
  break;
1431
1764
  case "commit":
1432
1765
  await handleCommitCommand();
1433
- await checkAndNotifyUpdate(packageName, version);
1434
1766
  await showMainMenu();
1435
1767
  break;
1436
1768
  case "branch":
1437
1769
  await handleBranchCommand();
1438
- await checkAndNotifyUpdate(packageName, version);
1439
- await showMainMenu();
1440
- break;
1441
- case "config":
1442
- await handleConfigCommand();
1443
- await checkAndNotifyUpdate(packageName, version);
1444
- await showMainMenu();
1445
- break;
1446
- case "config:model":
1447
- await handleConfigModelCommand();
1448
- await checkAndNotifyUpdate(packageName, version);
1449
1770
  await showMainMenu();
1450
1771
  break;
1451
1772
  case "pinned":
1452
1773
  await showPinnedBranchesMenu();
1453
1774
  await showMainMenu();
1454
1775
  break;
1776
+ case "settings":
1777
+ await showSettingsMenu();
1778
+ await showMainMenu();
1779
+ break;
1455
1780
  case "exit":
1456
1781
  console.log(dim("\n๐Ÿ‘‹ Goodbye!\n"));
1457
1782
  process.exit(0);
@@ -1459,11 +1784,11 @@ async function showMainMenu() {
1459
1784
  }
1460
1785
  function printPRBanner() {
1461
1786
  console.log(bold(cyan("\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—")));
1462
- console.log(bold(cyan("โ•‘ ๐Ÿ”ง Quick PR Creator โ•‘")));
1787
+ console.log(bold(cyan("โ•‘ ๐Ÿ”ง Quick PR Creator โ•‘")));
1463
1788
  console.log(bold(cyan("โ•‘ โ•‘")));
1464
- console.log(bold(cyan("โ•‘ Interactive PR Creation Tool โ•‘")));
1789
+ console.log(bold(cyan("โ•‘ Interactive PR Creation Tool โ•‘")));
1465
1790
  console.log(bold(cyan("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")));
1466
- console.log(` Version: ${version}\n`);
1791
+ console.log(` Version: ${version}\n`);
1467
1792
  }
1468
1793
  /**
1469
1794
  * ่ฏข้—ฎๆ˜ฏๅฆๆŽจ้€ๅˆ†ๆ”ฏๅˆฐ่ฟœ็จ‹
@@ -1548,6 +1873,12 @@ yargs(hideBin(process.argv)).scriptName("qkpr").usage("Usage: $0 <command> [opti
1548
1873
  }).command("config:model", "๐Ÿ”ง Configure Gemini Model", () => {}, async () => {
1549
1874
  await handleConfigModelCommand();
1550
1875
  await checkAndNotifyUpdate(packageName, version);
1876
+ }).command("config:prompt-lang", "๐ŸŒ Configure Prompt Language", () => {}, async () => {
1877
+ await handleConfigPromptLangCommand();
1878
+ await checkAndNotifyUpdate(packageName, version);
1879
+ }).command("config:prompts", "๐Ÿ“ Configure Custom Prompts", () => {}, async () => {
1880
+ await handleConfigPromptsCommand();
1881
+ await checkAndNotifyUpdate(packageName, version);
1551
1882
  }).command("pin [branch]", "๐Ÿ“Œ Pin a branch for quick access", (yargs$1) => {
1552
1883
  return yargs$1.positional("branch", {
1553
1884
  describe: "Branch name to pin",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qkpr",
3
3
  "type": "module",
4
- "version": "1.0.0",
4
+ "version": "1.0.2",
5
5
  "description": "Create a Pull Request with interactive branch selection",
6
6
  "author": "KazooTTT <work@kazoottt.top>",
7
7
  "license": "MIT",
@@ -75,6 +75,10 @@
75
75
  "dev": "tsdown --watch",
76
76
  "lint": "eslint",
77
77
  "release": "bumpp",
78
+ "release:next": "bumpp next --yes",
79
+ "release:patch": "bumpp patch --yes",
80
+ "release:minor": "bumpp minor --yes",
81
+ "release:major": "bumpp major --yes",
78
82
  "start": "tsx src/index.ts",
79
83
  "test": "vitest",
80
84
  "typecheck": "tsc"