qkpr 1.0.5 → 1.0.7
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 +39 -1
- package/dist/index.mjs +111 -26
- package/dist/pr-Cv5LeZ_A.mjs +3 -0
- package/package.json +1 -1
- package/dist/pr-C2AR97YR.mjs +0 -3
package/README.md
CHANGED
|
@@ -24,6 +24,38 @@ pnpm add -g qkpr
|
|
|
24
24
|
yarn global add qkpr
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
## Claude Code Integration
|
|
28
|
+
|
|
29
|
+
qkpr provides a Claude Code skill for better AI assistant integration. To install:
|
|
30
|
+
|
|
31
|
+
### Global Installation (Recommended)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# From npm (if installed globally)
|
|
35
|
+
cp -r $(npm root -g)/qkpr/skills/qkpr ~/.claude/skills/
|
|
36
|
+
|
|
37
|
+
# From pnpm
|
|
38
|
+
cp -r $(pnpm root -g)/qkpr/skills/qkpr ~/.claude/skills/
|
|
39
|
+
|
|
40
|
+
# Or download directly
|
|
41
|
+
mkdir -p ~/.claude/skills/qkpr
|
|
42
|
+
curl -o ~/.claude/skills/qkpr/SKILL.md \
|
|
43
|
+
https://raw.githubusercontent.com/KazooTTT/qkpr/main/skills/qkpr/SKILL.md
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Project-Specific Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
mkdir -p .claude/skills/qkpr
|
|
50
|
+
curl -o .claude/skills/qkpr/SKILL.md \
|
|
51
|
+
https://raw.githubusercontent.com/KazooTTT/qkpr/main/skills/qkpr/SKILL.md
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Once installed, you can ask Claude Code to:
|
|
55
|
+
- "Create a PR for this branch using qkpr"
|
|
56
|
+
- "Generate a commit message with qkpr"
|
|
57
|
+
- "Suggest a branch name for my changes"
|
|
58
|
+
|
|
27
59
|
## Usage
|
|
28
60
|
|
|
29
61
|
### Interactive Menu
|
|
@@ -48,6 +80,8 @@ You can directly access the PR creation feature:
|
|
|
48
80
|
|
|
49
81
|
```bash
|
|
50
82
|
qkpr pr
|
|
83
|
+
# or specify the target branch directly
|
|
84
|
+
qkpr pr main
|
|
51
85
|
```
|
|
52
86
|
|
|
53
87
|
The CLI will interactively guide you through creating a pull request:
|
|
@@ -189,7 +223,11 @@ Shows an interactive menu to choose from all available features
|
|
|
189
223
|
qkpr pr
|
|
190
224
|
```
|
|
191
225
|
|
|
192
|
-
Directly create a pull request with interactive branch selection
|
|
226
|
+
Directly create a pull request with interactive branch selection. You can also specify the target branch directly:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
qkpr pr [branch]
|
|
230
|
+
```
|
|
193
231
|
|
|
194
232
|
### Generate Commit Message
|
|
195
233
|
|
package/dist/index.mjs
CHANGED
|
@@ -133,9 +133,10 @@ function getBranchCategory(branchName) {
|
|
|
133
133
|
return "other";
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
|
-
*
|
|
136
|
+
* 获取分支详细信息(包含时间和分类)- 优化版本
|
|
137
137
|
*/
|
|
138
138
|
function getBranchesWithInfo(branches) {
|
|
139
|
+
if (branches.length > 50) return getBranchesWithInfoBatch(branches);
|
|
139
140
|
return branches.map((branchName) => {
|
|
140
141
|
const { timestamp, formatted } = getBranchLastCommitTime(branchName);
|
|
141
142
|
return {
|
|
@@ -147,6 +148,75 @@ function getBranchesWithInfo(branches) {
|
|
|
147
148
|
});
|
|
148
149
|
}
|
|
149
150
|
/**
|
|
151
|
+
* 批量获取分支信息,性能优化版本
|
|
152
|
+
*/
|
|
153
|
+
function getBranchesWithInfoBatch(branches) {
|
|
154
|
+
try {
|
|
155
|
+
const timestamps = execSync(branches.map((branch) => {
|
|
156
|
+
return `git log -1 --format=%ct origin/${branch} 2>/dev/null || echo "0"`;
|
|
157
|
+
}).join("; echo \"---\";"), {
|
|
158
|
+
encoding: "utf-8",
|
|
159
|
+
stdio: [
|
|
160
|
+
"pipe",
|
|
161
|
+
"pipe",
|
|
162
|
+
"ignore"
|
|
163
|
+
]
|
|
164
|
+
}).split("---").map((line) => {
|
|
165
|
+
const timestamp = Number.parseInt(line.trim(), 10);
|
|
166
|
+
return Number.isNaN(timestamp) ? 0 : timestamp;
|
|
167
|
+
});
|
|
168
|
+
return branches.map((branchName, index) => {
|
|
169
|
+
const timestamp = timestamps[index] || 0;
|
|
170
|
+
const { formatted } = formatTimestamp(timestamp);
|
|
171
|
+
return {
|
|
172
|
+
name: branchName,
|
|
173
|
+
lastCommitTime: timestamp,
|
|
174
|
+
lastCommitTimeFormatted: formatted,
|
|
175
|
+
category: getBranchCategory(branchName)
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.warn("Batch fetch failed, falling back to individual fetch:", error);
|
|
180
|
+
return branches.map((branchName) => {
|
|
181
|
+
const { timestamp, formatted } = getBranchLastCommitTime(branchName);
|
|
182
|
+
return {
|
|
183
|
+
name: branchName,
|
|
184
|
+
lastCommitTime: timestamp,
|
|
185
|
+
lastCommitTimeFormatted: formatted,
|
|
186
|
+
category: getBranchCategory(branchName)
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 格式化时间戳 - 提取为独立函数供批量版本使用
|
|
193
|
+
*/
|
|
194
|
+
function formatTimestamp(timestamp) {
|
|
195
|
+
if (timestamp === 0) return {
|
|
196
|
+
timestamp: 0,
|
|
197
|
+
formatted: "unknown"
|
|
198
|
+
};
|
|
199
|
+
const date = /* @__PURE__ */ new Date(timestamp * 1e3);
|
|
200
|
+
const diffMs = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
|
|
201
|
+
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
202
|
+
let formatted;
|
|
203
|
+
if (diffDays === 0) {
|
|
204
|
+
const diffHours = Math.floor(diffMs / (1e3 * 60 * 60));
|
|
205
|
+
if (diffHours === 0) {
|
|
206
|
+
const diffMinutes = Math.floor(diffMs / (1e3 * 60));
|
|
207
|
+
formatted = diffMinutes <= 1 ? "just now" : `${diffMinutes}m ago`;
|
|
208
|
+
} else formatted = `${diffHours}h ago`;
|
|
209
|
+
} else if (diffDays === 1) formatted = "yesterday";
|
|
210
|
+
else if (diffDays < 7) formatted = `${diffDays}d ago`;
|
|
211
|
+
else if (diffDays < 30) formatted = `${Math.floor(diffDays / 7)}w ago`;
|
|
212
|
+
else if (diffDays < 365) formatted = `${Math.floor(diffDays / 30)}mo ago`;
|
|
213
|
+
else formatted = `${Math.floor(diffDays / 365)}y ago`;
|
|
214
|
+
return {
|
|
215
|
+
timestamp,
|
|
216
|
+
formatted
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
150
220
|
* 解析 Git remote URL
|
|
151
221
|
*/
|
|
152
222
|
function parseRemoteUrl(remote) {
|
|
@@ -1506,10 +1576,7 @@ var AutocompletePinPrompt = class extends Base {
|
|
|
1506
1576
|
this.selected = this.selected > 0 ? this.selected - 1 : len - 1;
|
|
1507
1577
|
this.ensureSelectedInRange();
|
|
1508
1578
|
this.render();
|
|
1509
|
-
} else
|
|
1510
|
-
this.render();
|
|
1511
|
-
if (this.lastSearchTerm !== this.rl.line) this.search(this.rl.line);
|
|
1512
|
-
}
|
|
1579
|
+
} else if (this.lastSearchTerm !== this.rl.line) this.search(this.rl.line);
|
|
1513
1580
|
}
|
|
1514
1581
|
};
|
|
1515
1582
|
/**
|
|
@@ -1567,6 +1634,21 @@ async function promptBranchSelection(branches, options) {
|
|
|
1567
1634
|
const sortingPinnedBranches = getPinnedBranches();
|
|
1568
1635
|
const searchBranches = async (_answers, input = "") => {
|
|
1569
1636
|
const currentPinnedBranches = getPinnedBranches();
|
|
1637
|
+
const addCancelOption = (list) => {
|
|
1638
|
+
list.push(new inquirer.Separator(" "));
|
|
1639
|
+
list.push({
|
|
1640
|
+
name: dim(" [Cancel PR creation]"),
|
|
1641
|
+
value: "__CANCEL__",
|
|
1642
|
+
short: "Cancel"
|
|
1643
|
+
});
|
|
1644
|
+
};
|
|
1645
|
+
const createBranchChoice = (branch) => {
|
|
1646
|
+
return {
|
|
1647
|
+
name: `${currentPinnedBranches.includes(branch.name) ? "📌" : " "} ${branch.name.padEnd(45)} ${dim(`(${branch.lastCommitTimeFormatted})`)}`,
|
|
1648
|
+
value: branch.name,
|
|
1649
|
+
short: branch.name
|
|
1650
|
+
};
|
|
1651
|
+
};
|
|
1570
1652
|
const topGroupBranches = branchInfos.filter((b) => sortingPinnedBranches.includes(b.name));
|
|
1571
1653
|
const bottomGroupBranches = branchInfos.filter((b) => !sortingPinnedBranches.includes(b.name));
|
|
1572
1654
|
const effectiveTopGroup = filterPinned ? [] : topGroupBranches;
|
|
@@ -1577,22 +1659,15 @@ async function promptBranchSelection(branches, options) {
|
|
|
1577
1659
|
const displayBottomGroup = bottomGroupBranches.slice(0, 100);
|
|
1578
1660
|
const choices = [];
|
|
1579
1661
|
[...effectiveTopGroup, ...displayBottomGroup].forEach((branch) => {
|
|
1580
|
-
|
|
1581
|
-
choices.push({
|
|
1582
|
-
name: `${isPinnedNow ? "📌" : " "} ${branch.name.padEnd(45)} ${dim(`(${branch.lastCommitTimeFormatted})`)}`,
|
|
1583
|
-
value: branch.name,
|
|
1584
|
-
short: branch.name
|
|
1585
|
-
});
|
|
1662
|
+
choices.push(createBranchChoice(branch));
|
|
1586
1663
|
});
|
|
1587
|
-
if (!filterPinned)
|
|
1588
|
-
choices.push(new inquirer.Separator(" "));
|
|
1589
|
-
choices.push({
|
|
1590
|
-
name: dim(" [Cancel PR creation]"),
|
|
1591
|
-
value: "__CANCEL__",
|
|
1592
|
-
short: "Cancel"
|
|
1593
|
-
});
|
|
1594
|
-
}
|
|
1664
|
+
if (!filterPinned) addCancelOption(choices);
|
|
1595
1665
|
const lowerInput = input.toLowerCase();
|
|
1666
|
+
if (lowerInput.trim()) {
|
|
1667
|
+
const searchChoices = branchInfos.filter((branch) => branch.name.toLowerCase().includes(lowerInput)).map(createBranchChoice);
|
|
1668
|
+
addCancelOption(searchChoices);
|
|
1669
|
+
return searchChoices;
|
|
1670
|
+
}
|
|
1596
1671
|
return choices.filter((choice) => {
|
|
1597
1672
|
if (!choice.value || choice.value === "__CANCEL__") return true;
|
|
1598
1673
|
return choice.value.toLowerCase().includes(lowerInput);
|
|
@@ -1736,7 +1811,7 @@ async function handlePinCommand(branchName) {
|
|
|
1736
1811
|
addPinnedBranch(branchName);
|
|
1737
1812
|
console.log(green(`✅ Branch '${branchName}' has been pinned`));
|
|
1738
1813
|
} else {
|
|
1739
|
-
const { getAllBranches: getAllBranches$1 } = await import("./pr-
|
|
1814
|
+
const { getAllBranches: getAllBranches$1 } = await import("./pr-Cv5LeZ_A.mjs");
|
|
1740
1815
|
const branches = getAllBranches$1();
|
|
1741
1816
|
if (branches.length === 0) {
|
|
1742
1817
|
console.log(yellow("⚠️ No branches found"));
|
|
@@ -1895,7 +1970,7 @@ async function promptForUpdate(packageName$1, result) {
|
|
|
1895
1970
|
type: "confirm",
|
|
1896
1971
|
name: "shouldUpdate",
|
|
1897
1972
|
message: "Would you like to update now?",
|
|
1898
|
-
default:
|
|
1973
|
+
default: true
|
|
1899
1974
|
}, {
|
|
1900
1975
|
type: "list",
|
|
1901
1976
|
name: "packageManager",
|
|
@@ -2134,7 +2209,7 @@ async function promptPushBranch(branchName) {
|
|
|
2134
2209
|
/**
|
|
2135
2210
|
* 处理 PR 命令
|
|
2136
2211
|
*/
|
|
2137
|
-
async function handlePRCommand() {
|
|
2212
|
+
async function handlePRCommand(targetBranchArg) {
|
|
2138
2213
|
printPRBanner();
|
|
2139
2214
|
const gitInfo = getGitInfo();
|
|
2140
2215
|
if (!gitInfo.isGitRepo) {
|
|
@@ -2163,7 +2238,12 @@ async function handlePRCommand() {
|
|
|
2163
2238
|
console.log(yellow("⚠️ No branches found."));
|
|
2164
2239
|
return;
|
|
2165
2240
|
}
|
|
2166
|
-
|
|
2241
|
+
let targetBranch = null;
|
|
2242
|
+
if (targetBranchArg) if (branches.includes(targetBranchArg)) {
|
|
2243
|
+
targetBranch = targetBranchArg;
|
|
2244
|
+
console.log(green(`✅ Using specified target branch: ${targetBranch}\n`));
|
|
2245
|
+
} else console.log(yellow(`⚠️ Branch '${targetBranchArg}' not found. Falling back to interactive selection.`));
|
|
2246
|
+
if (!targetBranch) targetBranch = await promptTargetBranch(branches, gitInfo.currentBranch);
|
|
2167
2247
|
if (!targetBranch) return;
|
|
2168
2248
|
const prInfo = createPullRequest(gitInfo.currentBranch, targetBranch, gitInfo.remoteUrl);
|
|
2169
2249
|
if (!prInfo) {
|
|
@@ -2207,8 +2287,13 @@ async function handlePRCommand() {
|
|
|
2207
2287
|
}
|
|
2208
2288
|
yargs(hideBin(process.argv)).scriptName("qkpr").usage("Usage: $0 <command> [options]").command("$0", "Show interactive menu to choose features", () => {}, async () => {
|
|
2209
2289
|
await showMainMenu();
|
|
2210
|
-
}).command("pr", "🔧 Create a Pull Request with interactive branch selection", () => {
|
|
2211
|
-
|
|
2290
|
+
}).command("pr [branch]", "🔧 Create a Pull Request with interactive branch selection", (yargs$1) => {
|
|
2291
|
+
return yargs$1.positional("branch", {
|
|
2292
|
+
describe: "Target branch name",
|
|
2293
|
+
type: "string"
|
|
2294
|
+
});
|
|
2295
|
+
}, async (argv) => {
|
|
2296
|
+
await handlePRCommand(argv.branch);
|
|
2212
2297
|
await checkAndNotifyUpdate(packageName, version);
|
|
2213
2298
|
}).command("commit", "🤖 Generate commit message using AI", () => {}, async () => {
|
|
2214
2299
|
await handleCommitCommand();
|
|
@@ -2250,4 +2335,4 @@ yargs(hideBin(process.argv)).scriptName("qkpr").usage("Usage: $0 <command> [opti
|
|
|
2250
2335
|
}).version(version).alias("v", "version").help("h").alias("h", "help").epilog("For more information, visit https://github.com/KazooTTT/qkpr").argv;
|
|
2251
2336
|
|
|
2252
2337
|
//#endregion
|
|
2253
|
-
export {
|
|
2338
|
+
export { generateMergeBranchName as a, getAllBranches as c, getBranchesWithInfo as d, getBranchesWithInfoBatch as f, parseRemoteUrl as g, mergeSourceToMergeBranch as h, formatTimestamp as i, getBranchCategory as l, getGitInfo as m, createMergeBranch as n, generatePRMessage as o, getCommitsBetweenBranches as p, createPullRequest as r, generatePRUrl as s, copyToClipboard as t, getBranchLastCommitTime as u };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as generateMergeBranchName, c as getAllBranches, d as getBranchesWithInfo, f as getBranchesWithInfoBatch, g as parseRemoteUrl, h as mergeSourceToMergeBranch, i as formatTimestamp, l as getBranchCategory, m as getGitInfo, n as createMergeBranch, o as generatePRMessage, p as getCommitsBetweenBranches, r as createPullRequest, s as generatePRUrl, t as copyToClipboard, u as getBranchLastCommitTime } from "./index.mjs";
|
|
2
|
+
|
|
3
|
+
export { copyToClipboard, createMergeBranch, createPullRequest, formatTimestamp, generateMergeBranchName, generatePRMessage, generatePRUrl, getAllBranches, getBranchCategory, getBranchLastCommitTime, getBranchesWithInfo, getBranchesWithInfoBatch, getCommitsBetweenBranches, getGitInfo, mergeSourceToMergeBranch, parseRemoteUrl };
|
package/package.json
CHANGED
package/dist/pr-C2AR97YR.mjs
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { a as generatePRMessage, c as getBranchCategory, d as getCommitsBetweenBranches, f as getGitInfo, i as generateMergeBranchName, l as getBranchLastCommitTime, m as parseRemoteUrl, n as createMergeBranch, o as generatePRUrl, p as mergeSourceToMergeBranch, r as createPullRequest, s as getAllBranches, t as copyToClipboard, u as getBranchesWithInfo } from "./index.mjs";
|
|
2
|
-
|
|
3
|
-
export { copyToClipboard, createMergeBranch, createPullRequest, generateMergeBranchName, generatePRMessage, generatePRUrl, getAllBranches, getBranchCategory, getBranchLastCommitTime, getBranchesWithInfo, getCommitsBetweenBranches, getGitInfo, mergeSourceToMergeBranch, parseRemoteUrl };
|