prq-cli 0.4.0 → 0.6.0
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 +42 -8
- package/dist/bin/prq.js +319 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,11 +17,11 @@ Requires [GitHub CLI](https://cli.github.com/) (`gh`) to be authenticated.
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
# Set up config for the current project
|
|
21
|
-
prq init
|
|
22
|
-
|
|
23
20
|
# See your review queue
|
|
24
21
|
prq
|
|
22
|
+
|
|
23
|
+
# Interactive mode — navigate with arrow keys, act with shortcuts
|
|
24
|
+
prq -i
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Commands
|
|
@@ -40,11 +40,23 @@ prq # all repos you have access to
|
|
|
40
40
|
prq status --repos org/repo1 org/repo2 # specific repos
|
|
41
41
|
prq status --stale-days 7 # custom stale threshold
|
|
42
42
|
prq status --json # machine-readable output
|
|
43
|
+
prq status -i # interactive mode
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
### Interactive Mode
|
|
47
|
+
|
|
48
|
+
Run `prq -i` to navigate your queue with keyboard shortcuts:
|
|
49
|
+
|
|
50
|
+
- **↑↓** navigate between PRs
|
|
51
|
+
- **r** review — open files changed tab
|
|
52
|
+
- **o** open — open PR in browser
|
|
53
|
+
- **n** nudge — post a comment
|
|
54
|
+
- **c** copy URL to clipboard
|
|
55
|
+
- **q** quit
|
|
56
|
+
|
|
45
57
|
### `prq open <identifier>`
|
|
46
58
|
|
|
47
|
-
Open a PR in the browser.
|
|
59
|
+
Open a PR in the browser.
|
|
48
60
|
|
|
49
61
|
```bash
|
|
50
62
|
prq open 482 # searches your queue for PR #482
|
|
@@ -71,14 +83,18 @@ prq nudge 482 --yes # skip confirmation
|
|
|
71
83
|
prq nudge 482 --message "Any updates?" # custom message
|
|
72
84
|
```
|
|
73
85
|
|
|
74
|
-
### `prq
|
|
86
|
+
### `prq run <action> <identifier>`
|
|
75
87
|
|
|
76
|
-
|
|
88
|
+
Run a custom action defined in your config.
|
|
77
89
|
|
|
78
90
|
```bash
|
|
79
|
-
prq
|
|
91
|
+
prq run checkout 482
|
|
80
92
|
```
|
|
81
93
|
|
|
94
|
+
### `prq init`
|
|
95
|
+
|
|
96
|
+
Creates a `.prqrc.json` config file in the current directory.
|
|
97
|
+
|
|
82
98
|
## Configuration
|
|
83
99
|
|
|
84
100
|
Config is loaded in this order (later overrides earlier):
|
|
@@ -92,10 +108,28 @@ Example `.prqrc.json`:
|
|
|
92
108
|
```json
|
|
93
109
|
{
|
|
94
110
|
"repos": ["org/repo1", "org/repo2"],
|
|
95
|
-
"staleDays": 5
|
|
111
|
+
"staleDays": 5,
|
|
112
|
+
"actions": {
|
|
113
|
+
"review": "claude -p '/review {url}'",
|
|
114
|
+
"checkout": "gh pr checkout {number} --repo {owner}/{repo}"
|
|
115
|
+
}
|
|
96
116
|
}
|
|
97
117
|
```
|
|
98
118
|
|
|
119
|
+
### Custom Actions
|
|
120
|
+
|
|
121
|
+
Actions are shell command templates with variables:
|
|
122
|
+
|
|
123
|
+
- `{url}` — full PR URL
|
|
124
|
+
- `{number}` — PR number
|
|
125
|
+
- `{owner}` — repo owner
|
|
126
|
+
- `{repo}` — repo name
|
|
127
|
+
- `{title}` — PR title
|
|
128
|
+
- `{author}` — PR author
|
|
129
|
+
- `{days}` — days since last activity
|
|
130
|
+
|
|
131
|
+
Default actions (`open`, `review`, `nudge`) can be overridden. Custom actions are available via `prq run <action>` and in interactive mode.
|
|
132
|
+
|
|
99
133
|
## License
|
|
100
134
|
|
|
101
135
|
MIT
|
package/dist/bin/prq.js
CHANGED
|
@@ -6478,6 +6478,122 @@ Available actions: ${available}`);
|
|
|
6478
6478
|
await executeCommand(command);
|
|
6479
6479
|
}
|
|
6480
6480
|
|
|
6481
|
+
// src/commands/skill.ts
|
|
6482
|
+
var SKILL_CONTENT = `---
|
|
6483
|
+
name: prq
|
|
6484
|
+
description: PR review queue manager. Use when the user wants to check their PR review queue, review PRs, nudge stale PRs, or manage code review workflow. Triggers on mentions of "review queue", "PRs waiting", "stale PRs", "what needs review", or "prq".
|
|
6485
|
+
---
|
|
6486
|
+
|
|
6487
|
+
# PRQ — PR Review Queue
|
|
6488
|
+
|
|
6489
|
+
You have access to the \`prq\` CLI tool installed on this machine. Use it to help the user manage their code review queue.
|
|
6490
|
+
|
|
6491
|
+
## Step 1: Get the queue
|
|
6492
|
+
|
|
6493
|
+
Run this command to get the user's current review queue:
|
|
6494
|
+
|
|
6495
|
+
\`\`\`bash
|
|
6496
|
+
prq status --json
|
|
6497
|
+
\`\`\`
|
|
6498
|
+
|
|
6499
|
+
This returns a JSON object with categorized PRs:
|
|
6500
|
+
- **needs-re-review** — PRs where the user left a review but new commits were pushed after
|
|
6501
|
+
- **requested** — PRs where the user is a requested reviewer
|
|
6502
|
+
- **stale** — PRs with no activity for N days
|
|
6503
|
+
- **waiting-on-others** — PRs the user authored that are waiting on someone else
|
|
6504
|
+
|
|
6505
|
+
## Step 2: Present the queue
|
|
6506
|
+
|
|
6507
|
+
Show the results in a clear, scannable format grouped by category. For each PR, show:
|
|
6508
|
+
- The category symbol (◆ needs re-review, ● requested, ○ stale, ◇ waiting)
|
|
6509
|
+
- The repo and PR number
|
|
6510
|
+
- The title
|
|
6511
|
+
- The detail (e.g., "new commits since your review 2d ago")
|
|
6512
|
+
|
|
6513
|
+
Then ask what the user wants to do.
|
|
6514
|
+
|
|
6515
|
+
## Step 3: Act on PRs
|
|
6516
|
+
|
|
6517
|
+
When the user asks to act on a PR, check the \`.prqrc.json\` file in the current directory (or \`~/.config/prq/config.json\`) for custom actions:
|
|
6518
|
+
|
|
6519
|
+
\`\`\`json
|
|
6520
|
+
{
|
|
6521
|
+
"actions": {
|
|
6522
|
+
"review": "/review {url}",
|
|
6523
|
+
"nudge": "shell:prq nudge {number} --yes"
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
\`\`\`
|
|
6527
|
+
|
|
6528
|
+
### Action resolution
|
|
6529
|
+
|
|
6530
|
+
For each action template:
|
|
6531
|
+
- **Starts with \`/\`** — it's a Claude Code skill. Invoke it by running the skill with the interpolated value. For example, \`/review https://github.com/org/repo/pull/123\`
|
|
6532
|
+
- **Starts with \`shell:\`** — it's a shell command. Run it with the Bash tool. For example, \`prq nudge 123 --yes\`
|
|
6533
|
+
- **Otherwise** — treat it as a prompt. Send it as a message.
|
|
6534
|
+
|
|
6535
|
+
### Template variables
|
|
6536
|
+
|
|
6537
|
+
Replace these in the action template:
|
|
6538
|
+
- \`{url}\` — full PR URL
|
|
6539
|
+
- \`{number}\` — PR number
|
|
6540
|
+
- \`{owner}\` — repo owner
|
|
6541
|
+
- \`{repo}\` — repo name
|
|
6542
|
+
- \`{title}\` — PR title
|
|
6543
|
+
- \`{author}\` — PR author
|
|
6544
|
+
|
|
6545
|
+
### Default actions (if no config found)
|
|
6546
|
+
|
|
6547
|
+
If no actions are configured, use these defaults:
|
|
6548
|
+
- **review** — invoke \`/review {url}\` if the /review skill exists, otherwise run \`prq review {number}\` to open files changed in browser
|
|
6549
|
+
- **nudge** — run \`prq nudge {number} --yes\`
|
|
6550
|
+
- **open** — run \`prq open {number}\`
|
|
6551
|
+
|
|
6552
|
+
## Step 4: Batch operations
|
|
6553
|
+
|
|
6554
|
+
If the user says things like "review all needs-re-review PRs" or "nudge all stale PRs":
|
|
6555
|
+
|
|
6556
|
+
1. Filter the queue JSON by the requested category
|
|
6557
|
+
2. Confirm the list with the user: "I'll review these 3 PRs: #2439, #2380, #2352. Proceed?"
|
|
6558
|
+
3. Execute the action on each PR sequentially
|
|
6559
|
+
|
|
6560
|
+
## Examples
|
|
6561
|
+
|
|
6562
|
+
**User:** "check my review queue"
|
|
6563
|
+
→ Run \`prq status --json\`, present results, ask what to do
|
|
6564
|
+
|
|
6565
|
+
**User:** "review 2439"
|
|
6566
|
+
→ Look up action for "review", interpolate with PR data, execute
|
|
6567
|
+
|
|
6568
|
+
**User:** "nudge all stale PRs"
|
|
6569
|
+
→ Filter stale PRs from queue, confirm, run nudge on each
|
|
6570
|
+
|
|
6571
|
+
**User:** "what PRs are waiting on me?"
|
|
6572
|
+
→ Run \`prq status --json\`, show only needs-re-review and requested categories
|
|
6573
|
+
|
|
6574
|
+
**User:** "/prq" with no context
|
|
6575
|
+
→ Run \`prq status --json\`, present full queue, ask what to do
|
|
6576
|
+
`;
|
|
6577
|
+
function skillCommand(global) {
|
|
6578
|
+
if (global) {
|
|
6579
|
+
const fs2 = __require("node:fs");
|
|
6580
|
+
const path2 = __require("node:path");
|
|
6581
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
6582
|
+
const skillDir = path2.join(home, ".claude", "skills", "prq");
|
|
6583
|
+
const skillPath = path2.join(skillDir, "SKILL.md");
|
|
6584
|
+
fs2.mkdirSync(skillDir, { recursive: true });
|
|
6585
|
+
fs2.writeFileSync(skillPath, SKILL_CONTENT);
|
|
6586
|
+
console.log(`Installed to ${skillPath}`);
|
|
6587
|
+
} else {
|
|
6588
|
+
const fs2 = __require("node:fs");
|
|
6589
|
+
const skillDir = ".claude/skills/prq";
|
|
6590
|
+
const skillPath = `${skillDir}/SKILL.md`;
|
|
6591
|
+
fs2.mkdirSync(skillDir, { recursive: true });
|
|
6592
|
+
fs2.writeFileSync(skillPath, SKILL_CONTENT);
|
|
6593
|
+
console.log(`Installed to ${skillPath}`);
|
|
6594
|
+
}
|
|
6595
|
+
}
|
|
6596
|
+
|
|
6481
6597
|
// src/categorize.ts
|
|
6482
6598
|
function timeAgo(dateStr) {
|
|
6483
6599
|
const now = Date.now();
|
|
@@ -6583,7 +6699,7 @@ function categorize(reviewedPRs, requestedPRs, authoredPRs, staleDays) {
|
|
|
6583
6699
|
return results;
|
|
6584
6700
|
}
|
|
6585
6701
|
|
|
6586
|
-
// src/
|
|
6702
|
+
// src/interactive.ts
|
|
6587
6703
|
var CATEGORY_CONFIG = {
|
|
6588
6704
|
"needs-re-review": {
|
|
6589
6705
|
icon: "◆",
|
|
@@ -6604,6 +6720,198 @@ var CATEGORY_ORDER = [
|
|
|
6604
6720
|
"stale",
|
|
6605
6721
|
"waiting-on-others"
|
|
6606
6722
|
];
|
|
6723
|
+
function toResolvedPR(pr) {
|
|
6724
|
+
const [owner, repo] = pr.repo.split("/");
|
|
6725
|
+
return {
|
|
6726
|
+
owner,
|
|
6727
|
+
repo,
|
|
6728
|
+
number: pr.number,
|
|
6729
|
+
url: pr.url,
|
|
6730
|
+
title: pr.title,
|
|
6731
|
+
author: pr.author,
|
|
6732
|
+
updatedAt: pr.updatedAt
|
|
6733
|
+
};
|
|
6734
|
+
}
|
|
6735
|
+
function render(result, selectedIndex, message) {
|
|
6736
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
6737
|
+
const lines = [];
|
|
6738
|
+
lines.push(source_default.bold(` PRQ Status for ${result.user}`));
|
|
6739
|
+
lines.push(source_default.dim(` ${"─".repeat(50)}`));
|
|
6740
|
+
const grouped = new Map;
|
|
6741
|
+
for (const pr of result.prs) {
|
|
6742
|
+
const list = grouped.get(pr.category) ?? [];
|
|
6743
|
+
list.push(pr);
|
|
6744
|
+
grouped.set(pr.category, list);
|
|
6745
|
+
}
|
|
6746
|
+
let flatIndex = 0;
|
|
6747
|
+
for (const category of CATEGORY_ORDER) {
|
|
6748
|
+
const prs = grouped.get(category);
|
|
6749
|
+
if (!prs || prs.length === 0)
|
|
6750
|
+
continue;
|
|
6751
|
+
const config = CATEGORY_CONFIG[category];
|
|
6752
|
+
lines.push("");
|
|
6753
|
+
lines.push(` ${config.color(`${config.icon} ${config.label}`)} ${source_default.dim(`(${prs.length})`)}`);
|
|
6754
|
+
for (const pr of prs) {
|
|
6755
|
+
const isSelected = flatIndex === selectedIndex;
|
|
6756
|
+
const arrow = isSelected ? source_default.yellow("›") : " ";
|
|
6757
|
+
const draft = pr.isDraft ? source_default.dim(" [draft]") : "";
|
|
6758
|
+
const maxTitle = 50;
|
|
6759
|
+
const title = pr.title.length > maxTitle ? `${pr.title.slice(0, maxTitle - 3)}...` : pr.title;
|
|
6760
|
+
if (isSelected) {
|
|
6761
|
+
const ref = source_default.white(`#${pr.number}`);
|
|
6762
|
+
lines.push(` ${arrow} ${ref} ${source_default.white(title)}${draft}`);
|
|
6763
|
+
lines.push(` ${source_default.dim("↳")} ${source_default.dim(pr.detail)}`);
|
|
6764
|
+
} else {
|
|
6765
|
+
const ref = source_default.dim(`#${pr.number}`);
|
|
6766
|
+
lines.push(` ${arrow} ${ref} ${source_default.dim(title)}${draft}`);
|
|
6767
|
+
lines.push(` ${source_default.dim("↳")} ${source_default.dim(pr.detail)}`);
|
|
6768
|
+
}
|
|
6769
|
+
flatIndex++;
|
|
6770
|
+
}
|
|
6771
|
+
}
|
|
6772
|
+
if (result.prs.length === 0) {
|
|
6773
|
+
lines.push("");
|
|
6774
|
+
lines.push(source_default.green(" All clear! No PRs need your attention."));
|
|
6775
|
+
}
|
|
6776
|
+
lines.push("");
|
|
6777
|
+
lines.push(source_default.dim(` ${"─".repeat(50)}`));
|
|
6778
|
+
lines.push("");
|
|
6779
|
+
lines.push(` ${source_default.dim("↑↓")} navigate ${source_default.white("r")} review ${source_default.white("o")} open ${source_default.white("n")} nudge ${source_default.white("c")} copy url ${source_default.white("q")} quit`);
|
|
6780
|
+
if (message) {
|
|
6781
|
+
lines.push("");
|
|
6782
|
+
lines.push(` ${message}`);
|
|
6783
|
+
}
|
|
6784
|
+
process.stdout.write(lines.join(`
|
|
6785
|
+
`));
|
|
6786
|
+
}
|
|
6787
|
+
async function interactiveMode(result, config) {
|
|
6788
|
+
if (result.prs.length === 0) {
|
|
6789
|
+
console.log(source_default.green(`
|
|
6790
|
+
All clear! No PRs need your attention.
|
|
6791
|
+
`));
|
|
6792
|
+
return;
|
|
6793
|
+
}
|
|
6794
|
+
let selectedIndex = 0;
|
|
6795
|
+
let message = "";
|
|
6796
|
+
const total = result.prs.length;
|
|
6797
|
+
if (!process.stdin.isTTY) {
|
|
6798
|
+
process.stdout.write(`Interactive mode requires a terminal. Use prq status instead.
|
|
6799
|
+
`);
|
|
6800
|
+
return;
|
|
6801
|
+
}
|
|
6802
|
+
process.stdin.setRawMode(true);
|
|
6803
|
+
process.stdin.resume();
|
|
6804
|
+
process.stdin.setEncoding("utf8");
|
|
6805
|
+
process.stdout.write("\x1B[?25l");
|
|
6806
|
+
render(result, selectedIndex, message);
|
|
6807
|
+
return new Promise((resolve) => {
|
|
6808
|
+
const cleanup = () => {
|
|
6809
|
+
process.stdin.setRawMode(false);
|
|
6810
|
+
process.stdin.pause();
|
|
6811
|
+
process.stdin.removeAllListeners("data");
|
|
6812
|
+
process.stdout.write("\x1B[?25h\x1B[2J\x1B[H");
|
|
6813
|
+
};
|
|
6814
|
+
process.stdin.on("data", async (key) => {
|
|
6815
|
+
const pr = result.prs[selectedIndex];
|
|
6816
|
+
switch (key) {
|
|
6817
|
+
case "q":
|
|
6818
|
+
case "\x03":
|
|
6819
|
+
cleanup();
|
|
6820
|
+
resolve();
|
|
6821
|
+
return;
|
|
6822
|
+
case "\x1B[A":
|
|
6823
|
+
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
6824
|
+
message = "";
|
|
6825
|
+
break;
|
|
6826
|
+
case "\x1B[B":
|
|
6827
|
+
selectedIndex = Math.min(total - 1, selectedIndex + 1);
|
|
6828
|
+
message = "";
|
|
6829
|
+
break;
|
|
6830
|
+
case "o": {
|
|
6831
|
+
const template = getAction("open", config);
|
|
6832
|
+
if (template) {
|
|
6833
|
+
const cmd = interpolate(template, buildContext(toResolvedPR(pr)));
|
|
6834
|
+
message = source_default.dim(`opening ${pr.repo}#${pr.number}...`);
|
|
6835
|
+
render(result, selectedIndex, message);
|
|
6836
|
+
try {
|
|
6837
|
+
await executeCommand(cmd);
|
|
6838
|
+
message = source_default.green(`opened ${pr.repo}#${pr.number}`);
|
|
6839
|
+
} catch {
|
|
6840
|
+
message = source_default.red("failed to open");
|
|
6841
|
+
}
|
|
6842
|
+
}
|
|
6843
|
+
break;
|
|
6844
|
+
}
|
|
6845
|
+
case "r": {
|
|
6846
|
+
const template = getAction("review", config);
|
|
6847
|
+
if (template) {
|
|
6848
|
+
const cmd = interpolate(template, buildContext(toResolvedPR(pr)));
|
|
6849
|
+
message = source_default.dim(`opening review for ${pr.repo}#${pr.number}...`);
|
|
6850
|
+
render(result, selectedIndex, message);
|
|
6851
|
+
try {
|
|
6852
|
+
await executeCommand(cmd);
|
|
6853
|
+
message = source_default.green(`opened review for ${pr.repo}#${pr.number}`);
|
|
6854
|
+
} catch {
|
|
6855
|
+
message = source_default.red("failed to open review");
|
|
6856
|
+
}
|
|
6857
|
+
}
|
|
6858
|
+
break;
|
|
6859
|
+
}
|
|
6860
|
+
case "n": {
|
|
6861
|
+
const template = getAction("nudge", config);
|
|
6862
|
+
if (template) {
|
|
6863
|
+
const cmd = interpolate(template, buildContext(toResolvedPR(pr)));
|
|
6864
|
+
message = source_default.dim(`nudging ${pr.repo}#${pr.number}...`);
|
|
6865
|
+
render(result, selectedIndex, message);
|
|
6866
|
+
try {
|
|
6867
|
+
await executeCommand(cmd);
|
|
6868
|
+
message = source_default.green(`nudged ${pr.repo}#${pr.number}`);
|
|
6869
|
+
} catch {
|
|
6870
|
+
message = source_default.red("failed to nudge");
|
|
6871
|
+
}
|
|
6872
|
+
}
|
|
6873
|
+
break;
|
|
6874
|
+
}
|
|
6875
|
+
case "c": {
|
|
6876
|
+
const url = pr.url;
|
|
6877
|
+
try {
|
|
6878
|
+
const proc = process.platform === "darwin" ? "pbcopy" : process.platform === "linux" ? "xclip -selection clipboard" : "clip";
|
|
6879
|
+
await executeCommand(`echo "${url}" | ${proc}`);
|
|
6880
|
+
message = source_default.green("url copied");
|
|
6881
|
+
} catch {
|
|
6882
|
+
message = source_default.dim(url);
|
|
6883
|
+
}
|
|
6884
|
+
break;
|
|
6885
|
+
}
|
|
6886
|
+
default:
|
|
6887
|
+
break;
|
|
6888
|
+
}
|
|
6889
|
+
render(result, selectedIndex, message);
|
|
6890
|
+
});
|
|
6891
|
+
});
|
|
6892
|
+
}
|
|
6893
|
+
|
|
6894
|
+
// src/output.ts
|
|
6895
|
+
var CATEGORY_CONFIG2 = {
|
|
6896
|
+
"needs-re-review": {
|
|
6897
|
+
icon: "◆",
|
|
6898
|
+
label: "Needs Re-review",
|
|
6899
|
+
color: source_default.yellow
|
|
6900
|
+
},
|
|
6901
|
+
requested: { icon: "●", label: "Requested Reviews", color: source_default.green },
|
|
6902
|
+
stale: { icon: "○", label: "Stale", color: source_default.red },
|
|
6903
|
+
"waiting-on-others": {
|
|
6904
|
+
icon: "◇",
|
|
6905
|
+
label: "Your PRs Waiting",
|
|
6906
|
+
color: source_default.dim
|
|
6907
|
+
}
|
|
6908
|
+
};
|
|
6909
|
+
var CATEGORY_ORDER2 = [
|
|
6910
|
+
"needs-re-review",
|
|
6911
|
+
"requested",
|
|
6912
|
+
"stale",
|
|
6913
|
+
"waiting-on-others"
|
|
6914
|
+
];
|
|
6607
6915
|
function formatPR(pr) {
|
|
6608
6916
|
const draft = pr.isDraft ? source_default.dim(" [draft]") : "";
|
|
6609
6917
|
const prRef = source_default.cyan(`${pr.repo}#${pr.number}`);
|
|
@@ -6627,12 +6935,12 @@ function formatStatus(result) {
|
|
|
6627
6935
|
grouped.set(pr.category, list);
|
|
6628
6936
|
}
|
|
6629
6937
|
let hasContent = false;
|
|
6630
|
-
for (const category of
|
|
6938
|
+
for (const category of CATEGORY_ORDER2) {
|
|
6631
6939
|
const prs = grouped.get(category);
|
|
6632
6940
|
if (!prs || prs.length === 0)
|
|
6633
6941
|
continue;
|
|
6634
6942
|
hasContent = true;
|
|
6635
|
-
const config =
|
|
6943
|
+
const config = CATEGORY_CONFIG2[category];
|
|
6636
6944
|
lines.push("");
|
|
6637
6945
|
lines.push(` ${config.color(`${config.icon} ${config.label}`)} ${source_default.dim(`(${prs.length})`)}`);
|
|
6638
6946
|
for (const pr of prs) {
|
|
@@ -6656,7 +6964,7 @@ function formatStatus(result) {
|
|
|
6656
6964
|
}
|
|
6657
6965
|
|
|
6658
6966
|
// src/commands/status.ts
|
|
6659
|
-
async function statusCommand(config, json) {
|
|
6967
|
+
async function statusCommand(config, json, interactive) {
|
|
6660
6968
|
const user = config.user ?? await getAuthenticatedUser();
|
|
6661
6969
|
process.stderr.write(`Fetching PRs for ${user}...
|
|
6662
6970
|
`);
|
|
@@ -6677,6 +6985,8 @@ async function statusCommand(config, json) {
|
|
|
6677
6985
|
if (json) {
|
|
6678
6986
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
6679
6987
|
`);
|
|
6988
|
+
} else if (interactive && process.stdin.isTTY) {
|
|
6989
|
+
await interactiveMode(result, config);
|
|
6680
6990
|
} else {
|
|
6681
6991
|
process.stdout.write(formatStatus(result));
|
|
6682
6992
|
}
|
|
@@ -10699,12 +11009,12 @@ function getVersion() {
|
|
|
10699
11009
|
function createCLI() {
|
|
10700
11010
|
const program2 = new Command;
|
|
10701
11011
|
program2.name("prq").description("PR Queue — see what code reviews need your attention").version(getVersion());
|
|
10702
|
-
program2.command("status", { isDefault: true }).description("Show PRs needing your attention").option("-r, --repos <repos...>", "Filter to specific repos (owner/name)").option("-s, --stale-days <days>", "Days of inactivity to consider stale", "3").option("--json", "Output as JSON").action(async (opts) => {
|
|
11012
|
+
program2.command("status", { isDefault: true }).description("Show PRs needing your attention").option("-r, --repos <repos...>", "Filter to specific repos (owner/name)").option("-s, --stale-days <days>", "Days of inactivity to consider stale", "3").option("--json", "Output as JSON").option("-i, --interactive", "Interactive mode with keyboard shortcuts").action(async (opts) => {
|
|
10703
11013
|
const config = loadConfig({
|
|
10704
11014
|
repos: opts.repos,
|
|
10705
11015
|
staleDays: opts.staleDays ? parseInt(opts.staleDays, 10) : undefined
|
|
10706
11016
|
});
|
|
10707
|
-
await statusCommand(config, opts.json ?? false);
|
|
11017
|
+
await statusCommand(config, opts.json ?? false, opts.interactive ?? false);
|
|
10708
11018
|
});
|
|
10709
11019
|
program2.command("open <identifier>").description("Open a PR in the browser").action(async (identifier) => {
|
|
10710
11020
|
const config = loadConfig({});
|
|
@@ -10725,6 +11035,9 @@ function createCLI() {
|
|
|
10725
11035
|
const config = loadConfig({});
|
|
10726
11036
|
await runCommand(action, identifier, config);
|
|
10727
11037
|
});
|
|
11038
|
+
program2.command("skill").description("Install the /prq skill for Claude Code").option("-g, --global", "Install globally (~/.claude/skills/prq/)").action((opts) => {
|
|
11039
|
+
skillCommand(opts.global ?? false);
|
|
11040
|
+
});
|
|
10728
11041
|
program2.command("init").description("Create config file interactively").action(async () => {
|
|
10729
11042
|
await initCommand();
|
|
10730
11043
|
});
|