prq-cli 0.7.0 → 0.9.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 +20 -4
- package/dist/bin/prq.js +70 -95
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,19 +95,21 @@ prq skill --global # install globally
|
|
|
95
95
|
|
|
96
96
|
## Pluggable Actions
|
|
97
97
|
|
|
98
|
-
PRQ doesn't force a workflow. Every action is a configurable shell command template. Override the defaults or add your own in `.prqrc.json
|
|
98
|
+
PRQ doesn't force a workflow. Every action is a configurable shell command template — inline commands or scripts. Override the defaults or add your own in `.prqrc.json`.
|
|
99
|
+
|
|
100
|
+
Actions run with full terminal control. When you trigger an action, prq suspends its TUI, the command takes over the screen (interactive tools like Claude Code work as normal), and prq resumes when the command exits.
|
|
99
101
|
|
|
100
102
|
### Use Claude Code for reviews
|
|
101
103
|
|
|
102
104
|
```json
|
|
103
105
|
{
|
|
104
106
|
"actions": {
|
|
105
|
-
"review": "claude
|
|
107
|
+
"review": "claude '/review {url}'"
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
```
|
|
109
111
|
|
|
110
|
-
Now `prq review 482`
|
|
112
|
+
Now `prq review 482` opens an interactive Claude Code session.
|
|
111
113
|
|
|
112
114
|
### Use Codex for reviews
|
|
113
115
|
|
|
@@ -119,6 +121,18 @@ Now `prq review 482` dispatches to Claude Code.
|
|
|
119
121
|
}
|
|
120
122
|
```
|
|
121
123
|
|
|
124
|
+
### Use a script for complex workflows
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"actions": {
|
|
129
|
+
"review": "./scripts/review.sh {number} {url}"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The script handles its own logic — session management, resuming, branching, whatever you need.
|
|
135
|
+
|
|
122
136
|
### Use gh CLI to checkout
|
|
123
137
|
|
|
124
138
|
```json
|
|
@@ -143,9 +157,11 @@ With no config, `prq review` opens the files changed tab and `prq open` opens th
|
|
|
143
157
|
| `{number}` | `482` |
|
|
144
158
|
| `{owner}` | `org` |
|
|
145
159
|
| `{repo}` | `repo` |
|
|
160
|
+
| `{fullRepo}` | `org/repo` |
|
|
146
161
|
| `{title}` | `fix: handle edge case` |
|
|
147
162
|
| `{author}` | `alice` |
|
|
148
163
|
| `{days}` | `5` |
|
|
164
|
+
| `{category}` | `needs-re-review` |
|
|
149
165
|
|
|
150
166
|
## Agent & Automation
|
|
151
167
|
|
|
@@ -196,7 +212,7 @@ Full example:
|
|
|
196
212
|
"repos": ["org/repo1", "org/repo2"],
|
|
197
213
|
"staleDays": 5,
|
|
198
214
|
"actions": {
|
|
199
|
-
"review": "claude
|
|
215
|
+
"review": "claude '/review {url}'",
|
|
200
216
|
"checkout": "gh pr checkout {number} --repo {owner}/{repo}",
|
|
201
217
|
"approve": "gh pr review {number} --repo {owner}/{repo} --approve"
|
|
202
218
|
}
|
package/dist/bin/prq.js
CHANGED
|
@@ -6424,7 +6424,7 @@ function getAction(name, config) {
|
|
|
6424
6424
|
function listActions(config) {
|
|
6425
6425
|
return { ...DEFAULT_ACTIONS, ...config.actions };
|
|
6426
6426
|
}
|
|
6427
|
-
function buildContext(pr) {
|
|
6427
|
+
function buildContext(pr, category = "") {
|
|
6428
6428
|
const days = Math.floor((Date.now() - new Date(pr.updatedAt || Date.now()).getTime()) / 86400000);
|
|
6429
6429
|
return {
|
|
6430
6430
|
url: pr.url,
|
|
@@ -6434,7 +6434,8 @@ function buildContext(pr) {
|
|
|
6434
6434
|
fullRepo: `${pr.owner}/${pr.repo}`,
|
|
6435
6435
|
title: pr.title,
|
|
6436
6436
|
author: pr.author,
|
|
6437
|
-
days
|
|
6437
|
+
days,
|
|
6438
|
+
category
|
|
6438
6439
|
};
|
|
6439
6440
|
}
|
|
6440
6441
|
function interpolate(template, context) {
|
|
@@ -6481,98 +6482,62 @@ Available actions: ${available}`);
|
|
|
6481
6482
|
// src/commands/skill.ts
|
|
6482
6483
|
var SKILL_CONTENT = `---
|
|
6483
6484
|
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
|
+
description: PR review queue manager. Use when the user wants to check their PR review queue, review PRs, nudge stale PRs, open PRs, or manage code review workflow. Triggers on mentions of "review queue", "PRs waiting", "stale PRs", "what needs review", "open PR", "nudge", or "prq".
|
|
6485
6486
|
---
|
|
6486
6487
|
|
|
6487
|
-
# PRQ
|
|
6488
|
+
# PRQ CLI
|
|
6488
6489
|
|
|
6489
|
-
|
|
6490
|
+
\`prq\` is a CLI tool for managing code review queues. It's installed on this machine.
|
|
6490
6491
|
|
|
6491
|
-
##
|
|
6492
|
-
|
|
6493
|
-
Run this command to get the user's current review queue:
|
|
6492
|
+
## Commands
|
|
6494
6493
|
|
|
6495
6494
|
\`\`\`bash
|
|
6495
|
+
# Show the review queue (JSON for parsing)
|
|
6496
6496
|
prq status --json
|
|
6497
|
-
\`\`\`
|
|
6498
6497
|
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
- **stale** — PRs with no activity for N days
|
|
6503
|
-
- **waiting-on-others** — PRs the user authored that are waiting on someone else
|
|
6498
|
+
# Open a PR in the browser
|
|
6499
|
+
prq open <number>
|
|
6500
|
+
prq open <owner/repo#number>
|
|
6504
6501
|
|
|
6505
|
-
|
|
6502
|
+
# Open files changed tab for review
|
|
6503
|
+
prq review <number>
|
|
6506
6504
|
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
- The title
|
|
6511
|
-
- The detail (e.g., "new commits since your review 2d ago")
|
|
6505
|
+
# Post a nudge comment on a stale PR
|
|
6506
|
+
prq nudge <number> --yes
|
|
6507
|
+
prq nudge <number> --message "Custom message" --yes
|
|
6512
6508
|
|
|
6513
|
-
|
|
6509
|
+
# Run any configured action
|
|
6510
|
+
prq run <action> <number>
|
|
6511
|
+
\`\`\`
|
|
6514
6512
|
|
|
6515
|
-
##
|
|
6513
|
+
## JSON output
|
|
6516
6514
|
|
|
6517
|
-
|
|
6515
|
+
\`prq status --json\` returns:
|
|
6518
6516
|
|
|
6519
6517
|
\`\`\`json
|
|
6520
6518
|
{
|
|
6521
|
-
"
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6519
|
+
"user": "caio-pizzol",
|
|
6520
|
+
"prs": [
|
|
6521
|
+
{
|
|
6522
|
+
"category": "needs-re-review",
|
|
6523
|
+
"repo": "org/repo",
|
|
6524
|
+
"number": 2439,
|
|
6525
|
+
"title": "fix: something",
|
|
6526
|
+
"author": "alice",
|
|
6527
|
+
"url": "https://github.com/org/repo/pull/2439",
|
|
6528
|
+
"detail": "New commits since your review 1d ago"
|
|
6529
|
+
}
|
|
6530
|
+
]
|
|
6525
6531
|
}
|
|
6526
6532
|
\`\`\`
|
|
6527
6533
|
|
|
6528
|
-
|
|
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
|
|
6534
|
+
Categories: \`needs-re-review\`, \`requested\`, \`stale\`, \`waiting-on-others\`.
|
|
6570
6535
|
|
|
6571
|
-
|
|
6572
|
-
→ Run \`prq status --json\`, show only needs-re-review and requested categories
|
|
6536
|
+
## Usage
|
|
6573
6537
|
|
|
6574
|
-
|
|
6575
|
-
|
|
6538
|
+
- When the user asks about their review queue, run \`prq status --json\` and present the results grouped by category.
|
|
6539
|
+
- When the user asks to open, review, or nudge a PR, use the corresponding \`prq\` command.
|
|
6540
|
+
- For batch operations ("nudge all stale PRs"), filter the JSON output and confirm with the user before acting.
|
|
6576
6541
|
`;
|
|
6577
6542
|
function skillCommand(global) {
|
|
6578
6543
|
if (global) {
|
|
@@ -6797,14 +6762,34 @@ function render(state) {
|
|
|
6797
6762
|
process.stdout.write(lines.join(`
|
|
6798
6763
|
`));
|
|
6799
6764
|
}
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6765
|
+
function suspend() {
|
|
6766
|
+
process.stdin.setRawMode(false);
|
|
6767
|
+
process.stdin.pause();
|
|
6768
|
+
process.stdin.removeAllListeners("data");
|
|
6769
|
+
process.stdout.write("\x1B[?25h\x1B[2J\x1B[H");
|
|
6770
|
+
}
|
|
6771
|
+
function resume(state, onData) {
|
|
6772
|
+
process.stdin.setRawMode(true);
|
|
6773
|
+
process.stdin.resume();
|
|
6774
|
+
process.stdin.setEncoding("utf8");
|
|
6775
|
+
process.stdout.write("\x1B[?25l");
|
|
6776
|
+
process.stdin.on("data", onData);
|
|
6803
6777
|
render(state);
|
|
6778
|
+
}
|
|
6779
|
+
async function runAction(actionName, template, pr, state, onData) {
|
|
6780
|
+
const context = buildContext(toResolvedPR(pr), pr.category);
|
|
6781
|
+
const cmd = interpolate(template, context);
|
|
6782
|
+
suspend();
|
|
6783
|
+
process.stdout.write(source_default.dim(`
|
|
6784
|
+
running ${actionName} on ${pr.repo}#${pr.number}...
|
|
6785
|
+
|
|
6786
|
+
`));
|
|
6804
6787
|
try {
|
|
6805
6788
|
await executeCommand(cmd);
|
|
6789
|
+
resume(state, onData);
|
|
6806
6790
|
return source_default.green(`${actionName}: ${pr.repo}#${pr.number}`);
|
|
6807
6791
|
} catch {
|
|
6792
|
+
resume(state, onData);
|
|
6808
6793
|
return source_default.red(`${actionName} failed`);
|
|
6809
6794
|
}
|
|
6810
6795
|
}
|
|
@@ -6828,33 +6813,22 @@ async function interactiveMode(result, config) {
|
|
|
6828
6813
|
`);
|
|
6829
6814
|
return;
|
|
6830
6815
|
}
|
|
6831
|
-
process.stdin.setRawMode(true);
|
|
6832
|
-
process.stdin.resume();
|
|
6833
|
-
process.stdin.setEncoding("utf8");
|
|
6834
|
-
process.stdout.write("\x1B[?25l");
|
|
6835
|
-
render(state);
|
|
6836
6816
|
return new Promise((resolve) => {
|
|
6837
|
-
const
|
|
6838
|
-
process.stdin.setRawMode(false);
|
|
6839
|
-
process.stdin.pause();
|
|
6840
|
-
process.stdin.removeAllListeners("data");
|
|
6841
|
-
process.stdout.write("\x1B[?25h\x1B[2J\x1B[H");
|
|
6842
|
-
};
|
|
6843
|
-
process.stdin.on("data", async (key) => {
|
|
6817
|
+
const onData = async (key) => {
|
|
6844
6818
|
const pr = result.prs[state.selectedIndex];
|
|
6845
6819
|
if (state.actionMenu) {
|
|
6846
6820
|
if (key === "q" || key === "\x1B" || key === "a") {
|
|
6847
6821
|
state.actionMenu = null;
|
|
6848
6822
|
state.message = "";
|
|
6849
6823
|
} else if (key === "\x03") {
|
|
6850
|
-
|
|
6824
|
+
suspend();
|
|
6851
6825
|
resolve();
|
|
6852
6826
|
return;
|
|
6853
6827
|
} else {
|
|
6854
6828
|
const idx = parseInt(key, 10);
|
|
6855
6829
|
if (idx >= 1 && idx <= state.actionMenu.length) {
|
|
6856
6830
|
const action = state.actionMenu[idx - 1];
|
|
6857
|
-
state.message = await runAction(action.name, action.template, pr, state);
|
|
6831
|
+
state.message = await runAction(action.name, action.template, pr, state, onData);
|
|
6858
6832
|
state.actionMenu = null;
|
|
6859
6833
|
}
|
|
6860
6834
|
}
|
|
@@ -6864,7 +6838,7 @@ async function interactiveMode(result, config) {
|
|
|
6864
6838
|
switch (key) {
|
|
6865
6839
|
case "q":
|
|
6866
6840
|
case "\x03":
|
|
6867
|
-
|
|
6841
|
+
suspend();
|
|
6868
6842
|
resolve();
|
|
6869
6843
|
return;
|
|
6870
6844
|
case "\x1B[A":
|
|
@@ -6878,21 +6852,21 @@ async function interactiveMode(result, config) {
|
|
|
6878
6852
|
case "o": {
|
|
6879
6853
|
const template = allActions.open;
|
|
6880
6854
|
if (template) {
|
|
6881
|
-
state.message = await runAction("open", template, pr, state);
|
|
6855
|
+
state.message = await runAction("open", template, pr, state, onData);
|
|
6882
6856
|
}
|
|
6883
6857
|
break;
|
|
6884
6858
|
}
|
|
6885
6859
|
case "r": {
|
|
6886
6860
|
const template = allActions.review;
|
|
6887
6861
|
if (template) {
|
|
6888
|
-
state.message = await runAction("review", template, pr, state);
|
|
6862
|
+
state.message = await runAction("review", template, pr, state, onData);
|
|
6889
6863
|
}
|
|
6890
6864
|
break;
|
|
6891
6865
|
}
|
|
6892
6866
|
case "n": {
|
|
6893
6867
|
const template = allActions.nudge;
|
|
6894
6868
|
if (template) {
|
|
6895
|
-
state.message = await runAction("nudge", template, pr, state);
|
|
6869
|
+
state.message = await runAction("nudge", template, pr, state, onData);
|
|
6896
6870
|
}
|
|
6897
6871
|
break;
|
|
6898
6872
|
}
|
|
@@ -6920,7 +6894,8 @@ async function interactiveMode(result, config) {
|
|
|
6920
6894
|
break;
|
|
6921
6895
|
}
|
|
6922
6896
|
render(state);
|
|
6923
|
-
}
|
|
6897
|
+
};
|
|
6898
|
+
resume(state, onData);
|
|
6924
6899
|
});
|
|
6925
6900
|
}
|
|
6926
6901
|
|